Last Updated: 2026-05-08 (M9 Expression Unification + D9/D10/D11 added — CEL/AST-first migration plan) Authoritative Spec: content/docs/concepts/north-star.mdx - §7 Alignment Check is the single source of truth for Built / Drift / Missing. This file is the actionable checklist derived from that ledger. When north-star §7 changes, update this file too.
| Symbol | Meaning |
|---|---|
| ✅ | Shipped - code exists and is integrated |
| 🟡 | Partial / Drift - exists but wrong shape, needs evolution |
| 🔴 | Not started |
| ⛔ | Explicit non-goal - do not implement in Phase 1 |
Phase 1 is code-first ObjectStack:
- The local TypeScript workspace is the only user-metadata authoring surface.
objectstack compileproduces JSON.objectstack publishuploads that JSON to the control plane.- Studio is a control-plane dashboard, metadata viewer, artifact inspector, and observability surface.
- ObjectOS pulls artifacts through HTTP and never reads control-plane DB tables directly.
The implementation path is therefore:
Artifact format -> control-plane metadata -> Artifact API -> ObjectOS loader -> publish endpoint -> Studio viewer
Code that exists and matches the intended architecture. Do not regress these.
| What | Code anchor |
|---|---|
| Organization CRUD + member/invitation system | apps/studio/src/hooks/useSession.ts |
| Project CRUD + per-project Turso/memory DB provisioning | packages/services/service-tenant/ |
| Per-project ObjectKernel with LRU cache | packages/runtime/src/project-kernel-factory.ts |
Hostname-based routing: sys_project.hostname -> kernel resolution |
packages/runtime/src/environment-registry.ts |
ControlPlaneProxyDriver - org-scoped data isolation |
packages/runtime/src/control-plane-proxy-driver.ts |
AppCatalogService - per-project app events -> org-scoped sys_app catalog |
packages/services/service-tenant/src/services/app-catalog.service.ts |
TS -> JSON compile pipeline (objectstack compile) |
packages/cli/src/commands/compile.ts |
Zod -> JSON Schema publishing (z.toJSONSchema) - TS/JSON bridge |
packages/spec/scripts/build-schemas.ts |
Scaffolded TS file tree (create-objectstack -> defineStack() + split src/objects/*.ts) |
packages/create-objectstack/src/index.ts |
JSON-payload metadata column (sys_metadata.metadata textarea) |
packages/metadata/src/objects/sys-metadata.object.ts |
CLI publish - local JSON -> remote server wire (endpoint shape still wrong, see D2) |
packages/cli/src/commands/publish.ts |
M1 Project Artifact envelope schema (schemaVersion / projectId / commitId / checksum / metadata / functions / manifest) |
packages/spec/src/system/project-artifact.zod.ts |
M3 / M4 Cloud Artifact API + runtime loader (/cloud/resolve-hostname, /cloud/projects/:id/artifact, /cloud/projects/:id/metadata + ArtifactKernelFactory) |
packages/services/service-cloud/src/cloud-artifact-api-plugin.ts |
Single-project boot mode (OS_MODE=standalone) — createSingleProjectPlugin seeds local org/project + serves studio/runtime-config |
packages/services/service-cloud/src/single-project-plugin.ts |
Static Setup App (no runtime SetupPlugin) — fixed App artifact registered by plugin-auth |
packages/platform-objects/src/apps/setup.app.ts |
| packages/objectql/src/formula.ts | |
| Studio Flow Viewer + Flow Test Runner + Flow Runs panel | apps/studio/src/components/FlowViewer.tsx |
| Automation: flow auto-discovery from ObjectQL registry | packages/services/service-automation/src/plugin.ts |
D1 ObjectOS metadata DB bridge removed - MetadataPlugin no longer registers sys_metadata / sys_metadata_history or auto-bridges ObjectQL to DatabaseLoader |
packages/metadata/src/plugin.ts |
Existing code that contradicts the intended Phase 1 architecture. Fix these before building new surface area that depends on them.
packages/cli/src/commands/publish.ts POSTs a "package" payload that is not the Phase 1 project metadata endpoint.
Required evolution:
- Endpoint:
POST /api/v1/cloud/projects/:projectId/metadata - Payload: compiled
dist/objectstack.json(output ofobjectstack compile) - Server behavior: validate with Zod, write current project metadata state, create
commitId, compute checksum - Response:
{ projectId, commitId, checksum }
Decision (2026-04-25): delete, don't repurpose. Phase 1 metadata is scoped by organization_id + project_id. Deployment target differences are runtime/deployment configuration, not metadata row partitioning. Branch-like variants are explicitly deferred.
Fix path:
- Add/control-plane metadata ownership columns:
organization_id+project_id. - Backfill existing rows to the owning organization/project.
- Delete
env_idcolumn and all references. - Update unique indexes from environment-scoped keys to project-scoped keys.
Known anchors to scrub:
- packages/metadata/src/objects/sys-metadata.object.ts
- packages/metadata/src/objects/sys-metadata-history.object.ts
- packages/metadata/src/loaders/database-loader.ts
- packages/metadata/src/projection/metadata-projector.ts
- packages/metadata/src/utils/history-cleanup.ts
- packages/objectql/src/plugin.ts
- packages/objectql/src/protocol.ts
- packages/client/src/index.ts
Object identity is now single-sourced on name. The deprecated namespace
field has been removed from ObjectSchemaBase (packages/spec/src/data/object.zod.ts)
and the schema strips the key from any legacy input. Package-level namespace
(used by the registry for FQN computation, marketplace publishing, and
DatasourceRoutingRule) is intentionally retained — it is a separate mechanic.
ManifestSchema.scope is now a clean three-value enum ('cloud' | 'system' | 'project').
The deprecated 'platform' and 'environment' aliases have been removed
(packages/spec/src/kernel/manifest.zod.ts).
ScopedServiceManager and SharedProjectPlugin were added but their integration into the request path is incomplete. Either finish them or remove them.
Each plugin's manifest header + objects list now lives in a single canonical
src/manifest.ts per plugin. Both objectstack.config.ts (compile-time) and
the plugin's runtime manifest.register() import from that file, eliminating
the empty-./src/objects/ divergence that previously caused plugin-auth and
plugin-security to ship empty object lists from compile while their runtimes
registered the real schemas.
Anchors:
- packages/plugins/plugin-auth/src/manifest.ts
- packages/plugins/plugin-security/src/manifest.ts
- packages/services/service-tenant/src/manifest.ts
packages/objectql/src/formula.ts is a hand-written 433 LoC recursive-descent parser exposing 22 functions (UPPER / NOW / TODAY / IF / AND …). It is the only "real" expression engine in the repo, but:
- No public training corpus. AI agents have to learn our private DSL from scratch every prompt.
- Silent failure mode.
evaluateFormulawraps the whole evaluator intry { … } catch { return undefined }. Business rules fail open with no signal. - No execution bounds. No recursion depth limit, no step counter, no timeout. Untrusted input is a DoS vector.
- Salesforce-incomplete. 22 functions vs 100+ in the language we'd be cloning.
- Single-engine bet. All formula / predicate / condition fields share the same evaluator regardless of security posture.
Decision (2026-05-08): delete and replace with CEL. Salesforce compatibility is not a goal — see content/docs/concepts/north-star.mdx §8. See M9 Expression Unification below.
~25 fields named formula / condition / expression / criteria / visible / visibleOn are declared as bare z.string() with .describe('Formula expression'). These are not all the same language — they include Salesforce-style formulas, predicate conditions, JS expressions (mapping), cron expressions (job), SQL fragments (analytics joins, partial indexes), and OpenAPI runtime expressions (rest-server callbacks). None are typed, none declare a dialect.
Anchors (non-exhaustive):
- packages/spec/src/data/field.zod.ts —
formula.expression,conditionalRequired - packages/spec/src/data/validation.zod.ts —
condition,scope - packages/spec/src/data/hook.zod.ts —
condition - packages/spec/src/ui/{app,page,view,action}.zod.ts —
visible / visibility / visibleOn / disabled - packages/spec/src/security/sharing.zod.ts —
condition - packages/spec/src/automation/{workflow,approval}.zod.ts —
criteria,entryCriteria - packages/spec/src/ai/{orchestration,predictive}.zod.ts —
condition,entryCriteria,dataFilter - packages/spec/src/kernel/feature.zod.ts —
expression
Resolved by: M9 — replace with ExpressionSchema { dialect, ast } (string shorthand accepted as input, AST emitted in artifact).
examples/app-crm/src/data/index.ts uses new Date(Date.now() + 86400000 * 30) and similar patterns inside seed records. These are evaluated at TS compile time, baking the developer's wall-clock into dist/objectstack.json:
- Two consecutive
objectstack buildruns produce non-byte-identical artifacts (timestamps drift seconds-to-minutes apart). This violates the implicit "deterministic build" contract for cacheable artifacts. - Customers installing the package later receive seed dates anchored to the developer's "today + 30 days", forever. The dynamic semantics intended by the developer are lost.
Resolved by: M9 — Dataset.records accepts SeedValue = primitive | Expression; SeedLoader evaluates expressions at install time using the customer's clock and identity context.
apps/objectos/objectstack.config.ts currently registers control-plane and ObjectOS concerns on the same ObjectKernel. North-star §5 names these as two separate vertices; implementation should follow.
Decision: split into apps/cloud (Control Plane Server) and apps/objectos (ObjectOS Runtime). Both are ObjectStack-framework apps booted from their own objectstack.config.ts. They share the same ObjectKernel, spec, and adapter stack. They differ only in their plugin manifest.
Plugin partition:
| Plugin | apps/cloud (Control Plane) |
apps/objectos (ObjectOS) |
|---|---|---|
createControlPlanePlugins(...) (ObjectQL on control DB + driver + system-project + sys_* metadata) |
Yes | - |
MultiProjectPlugin (env-registry, kernel-manager, template-seeder) |
Yes | - |
AuthPlugin |
Yes | Yes |
createTenantPlugin(...) |
Yes | Yes |
SecurityPlugin |
Yes | Yes |
AuditPlugin |
Yes | Yes |
SetupPlugin (Studio bootstrap) |
Yes (optional) | - |
ObjectQLPlugin (project-scoped) |
- | Yes |
MetadataPlugin (artifact-loader mode - see M4) |
- | Yes |
User-app AppPlugin (compiled app) |
- | Yes |
Fix path:
- Create
apps/cloud/with its ownobjectstack.config.tscarrying the Control Plane manifest above. - Strip control-plane plugins out of apps/objectos/objectstack.config.ts; reduce it to the ObjectOS manifest.
- Decide deployment topology (separate Vercel projects vs. one repo / two entrypoints).
Depends on: M3, M4. Until ObjectOS can boot from Artifact API, apps/objectos cannot run standalone.
Ordered by dependency. Items higher in the list unblock those below them.
- Add a Zod schema for the artifact envelope.
- Minimum envelope:
schemaVersion,projectId,commitId,checksum,metadata,functions,manifest. - Specify function-code packaging (
ProjectArtifactFunctionSchema: name + language + inlinedcode+ optional source/hash) and plugin/driver requirement declaration (ProjectArtifactManifestSchema: plugins, drivers, engine). - Required: schemaVersion / projectId / commitId / checksum / metadata / manifest. Optional: builtAt / builtWith / payloadRef.
- Reserved
payloadReffor future S3 indirection ({ url, expiresAt, checksum }).
Code anchor: packages/spec/src/system/project-artifact.zod.ts. Tests: packages/spec/src/system/project-artifact.test.ts.
Prerequisite for: M3, M4.
明确 ObjectOS 启动输入 = Artifact(不可变、可缓存的元数据信封)+ Deployment Config(业务 DB 坐标、凭据、项目身份、密钥;不进 artifact)。详见 north-star.mdx §6.3。
- north-star.mdx §6.3 增补 Runtime Inputs 节(含本地单 project env 表 + 反模式说明)
- 实现本地 standalone / cloud env 路径:
OS_MODE(旧OS_MULTI_PROJECT) /OS_PROJECT_ID/OS_DATABASE_URL/OS_DATABASE_DRIVER/OS_ARTIFACT_PATH(默认./dist/objectstack.json)/AUTH_SECRET - 修复 Drift:
ProjectKernelFactory不再直连控制面 DB 读sys_project/sys_project_credential,改走 Artifact API + Deployment Config 注入(localProject分支) - apps/objectos/objectstack.config.ts 的 env 命名收敛到
OS_*前缀,isLocalMode分流本地/云端路径
Resolves: Open Question §9.2(已解决)+ 新增 Drift(ProjectKernelFactory 绕过 Artifact API)。
- Move user metadata out of project DBs into the control-plane DB.
- Scope metadata rows by
organization_id+project_id. - Add or update unique keys for
project_id+ metadatatype+ metadataname. - Data migration script for existing installations.
- Keep project DBs for business rows only.
Prerequisite for: M3, D3.
-
GET /api/v1/cloud/projects/:projectId/artifact- assembles the current project's metadata + inlined function code into a single consumable blob. - Validate the outgoing artifact with the M1 Zod schema.
- Content hash / ETag for cache validation. (Synthetic
sha256-prefixedcommitId+checksumminted when the source bundle does not provide them.) - Response includes
commitIdandchecksum. - Reserve response shape for future
{ url, expiresAt, checksum }indirection, but do not build S3 yet.
Code anchor: packages/services/service-cloud/src/cloud-artifact-api-plugin.ts. Docs: content/docs/concepts/cloud-artifact-api.mdx.
Prerequisite for: M4.
-
MetadataPluginproduction source: HTTP fetch against Artifact API. (ArtifactApiClient+ArtifactKernelFactory.) - Validate artifact with Zod before hydrating kernel.
- Local artifact cache with durability across control-plane outages. (TTL cache in
ArtifactApiClient.) - Cache key by
projectId+commitId/checksum.
Code anchor: packages/services/service-cloud/src/artifact-kernel-factory.ts.
Completes: production ObjectOS artifact source.
-
POST /api/v1/cloud/projects/:projectId/metadata- receives compiled JSON. (server side shipped 2026-04-30 alongside M3; seecloud-artifact-api-plugin.ts.) - Validates payload with
ObjectStackDefinitionSchemaor the canonical compiled stack schema. - Writes current project metadata state to control-plane storage.
- Creates
commitId, computes checksum, and returns{ projectId, commitId, checksum }. - Evolves packages/cli/src/commands/publish.ts to call this endpoint.
Resolves: D2.
- Project metadata browser for Objects / Fields / Functions / Views / Flows / Agents.
- Artifact inspector: schema version, commit id, checksum, publish time, payload preview.
- Publish history list.
- Runtime health/logs panels.
- Explicitly read-only for user metadata.
-
from-local-filekernel boot mode: ObjectOS readsdist/objectstack.json(or in-memory TS definition) and runs without a control-plane connection. - Wire as a distinct boot mode; does not pollute the production
from-artifact-apipath. -
objectstack devCLI command triggers this mode.
Open question: should dev consume TS directly (hot reload friendly) or compile-first (production-path parity)?
- Artifact schemas -> Amis/React components without hand-wiring.
Single canonical expression language across all metadata domains. Replace the
custom formula engine (D9), the scattered z.string() expression fields (D10),
and the compile-time-frozen seed timestamps (D11) with one tagged
ExpressionSchema { dialect, ast } whose persisted form is always an AST.
Strategic rationale. Future authors of metadata and formulas are AI agents, not human admins. The wire format must therefore have (a) abundant public training corpus, (b) formal grammar, (c) AST-first persistence (no parsing ambiguity, structured-output friendly), (d) sandboxed bounded execution. CEL (Google Common Expression Language, Apache-2.0) satisfies all four; the existing custom DSL satisfies none. Salesforce flavor is explicitly not a goal — see north-star §8.
Dialect map:
| dialect | engine | use cases |
|---|---|---|
cel |
cel-js + ObjectStack stdlib |
formula fields, predicates (condition / criteria / visible), seed dynamic values |
js |
isolated-vm / quickjs (existing) | L2 hook bodies (packages/spec/src/data/hook-body.zod.ts ScriptBody) |
cron |
cron-parser |
system/job.zod.ts schedule |
SQL fragments (analytics joins, partial indexes) stay driver-native and are not unified into the expression registry — they have a different security and portability posture.
- New
packages/formula/withcel-jsintegration. - ObjectStack CEL stdlib:
now(),today(),daysFromNow(n),daysAgo(n),isBlank(v),coalesce(v, fallback), plusrecord.*/previous.*/input.*/os.user.*/os.org.*/os.envvariable scope. -
packages/spec/src/shared/expression.zod.tsexportsExpressionDialect,ExpressionSchema,ExpressionInputSchema,PredicateSchema. -
ExpressionEngineregistry withevaluate(expr, ctx)single entrypoint.
-
cel\...`,F`...`(formula),P`...`(predicate) tagged-template helpers exported from@objectstack/spec`. -
objectstack compilenormalizes anysourcestring in input metadata intoast. Deferred to M9.7 — current artifact carries{ dialect, source }; AST emission lands with the AI structured-output milestone.
- Migrated all ~25 fields listed in D10 to
ExpressionInputSchema. Input acceptsstring | Expressionfor back-compat; output is the canonicalExpressionenvelope. - Surfaces migrated:
Field.formula(formula type),Field.conditionalRequired,Field.visibleOn,ConditionalValidation.when,ObjectFieldGroup.visibleOn,View.visibleOn,View.criteria,Action.disabled,Hook.condition,SharingRule.condition,Flow.decision.expression,Mapping.transform(js dialect),Job.schedule.expression(cron dialect). - Spec test suite updated (6840 passing).
Resolves: D10.
-
Dataset.recordsacceptsSeedValue = primitive | Expression | nested. -
SeedLoader.load()walks records, callsExpressionEngine.evaluatewith a per-load pinnednowbefore write.seedCtxexposesos.user / os.org / os.envfrom the install environment. - CRM example: 48 dynamic dates migrated from
new Date()(compile-time) tocel\daysFromNow(N)`/cel`daysAgo(N)`` (install-time).
Resolves: D11.
-
packages/objectql/src/engine.ts(computed fields,planFormulaProjection/applyFormulaPlan) andpackages/objectql/src/hook-wrappers.ts(hook conditions) callExpressionEngine.evaluate. - Deleted the legacy
packages/objectql/src/formula.tsrecursive-descent parser andpackages/spec/docs/formula-functions.md.
Resolves: D9.
- All 4 CRM formula fields (
lead.full_name,contact.full_name,campaign.response_rate,campaign.roi) re-written in CEL usingcoalesce(...)/ ternary patterns. - CI determinism gate: two consecutive
objectstack buildruns produce byte-identicaldist/objectstack.json(SHA-191efccc…). -
planFormulaProjectionfix: formulas are now evaluated even when REST returns the default projection (no explicit?fields=). - Browser-verified end-to-end via
pnpm dev:crm:Lead.full_namerenders "Lisa Thompson",Campaign.roirenders1907.04. - Follow-up: ~22 inert validation/sharing
condition:strings on other CRM objects still use Salesforce flavor (not yet evaluated by runtime — no validation engine wired). Migrate when validation engine lands.
- Publish
CelExprSchemaas JSON Schema for AI constrained decoding. - New
skills/objectstack-formula/SKILL.md— mandates CEL-only emission, lists stdlib, gives mandatory patterns and the legacy → CEL translation table. - Wire structured-output prompts into Studio AI assistant + CLI scaffolding.
- Node-graph editor backed directly by
CelExprSchema. No string parser needed in Studio. Deferred — depends on M6.
A full audit across all 15 protocol domains identified surfaces that still escape the canonical envelope. M9.3 fixed the 25 obvious formula/predicate fields; this milestone closes the gaps in adjacent semantic categories.
Status: All 5 sub-milestones (a–e) shipped 2026-05-08. Engines: real
cron-engine + template-engine registered alongside cel. CRM example
fully migrated (codemod) and remains byte-identical across builds
(e2af9e57…). Flow runtime now routes dialect:'cel' conditions through
@objectstack/formula with a vars.* scope; legacy {var} template syntax
preserved for back-compat.
-
automation/workflow.zod.tsTask.dueDate(documented "ISO string or formula") →z.union([z.string(), ExpressionInputSchema])so authors can writecel\daysFromNow(3)``. -
api/graphql.zod.tsComputedField.expression("Computation expression") →ExpressionInputSchema. -
ai/runtime-ops.zod.tscustomCondition→ExpressionInputSchema(predicate semantics identical toHook.condition). -
kernel/metadata-loader.zod.tsfilter: 'Filter predicate as string'→ExpressionInputSchema. -
ui/component.zod.tsForm.onSubmit: 'Action expression on form submit'→ disambiguate: action reference vs CEL predicate; pick one shape.
-
data/field.zod.tsField.defaultValue: z.unknown()→z.union([z.unknown(), ExpressionSchema])so authors can writedefaultValue: cel\today()`orcel`os.user.id`` instead of writing a hook. - Same change for
data/external-lookup.zod.ts,ui/page.zod.ts,ui/dashboard.zod.ts,api/export.zod.ts. - DataEngine on insert: when
defaultValue.dialectis set, evaluate viaExpressionEnginewith the request-time identity context.
10 sites still ship bare z.string().describe('Cron expression…') instead of using the canonical envelope (Job.schedule.expression already does it):
-
integration/connector.zod.tsschedule -
automation/etl.zod.tsschedule -
automation/execution.zod.tscronExpression -
api/export.zod.tscronExpression(×2) -
system/disaster-recovery.zod.tsschedule(×2) -
system/cache.zod.tsschedule -
ai/predictive.zod.tsretrainSchedule -
ai/orchestration.zod.tscron -
ai/devops-agent.zod.tsiterationFrequency
Persist as ExpressionInputSchema so AI authors emit one envelope shape regardless of domain. Engine wraps cron-parser.
Today every domain reinvents {{var}} interpolation:
-
system/notification.zod.ts— Emailsubject/body, SMSmessage, Pushbody -
data/object.zod.ts—titleFormat: '{name} - {code}' -
integration/connector/github.zod.ts—messageTemplate,titleTemplate,bodyTemplate,releaseNameTemplate -
ai/model-registry.zod.ts/ai/orchestration.zod.ts/ai/mcp.zod.ts— prompt templates -
api/graphql.zod.ts— cache key / query templates -
Add
'template'toExpressionDialectenum. -
@objectstack/formulaengine fordialect: 'template'— strict Mustache subset ({{path.to.value}}only, no logic), same variable scope as CEL (record,os.user, …). -
Migrate the surfaces above to
ExpressionInputSchema. AI authors then know: anything templated or computed goes throughExpression.
These are typed structured objects today, but power users sometimes need a raw CEL fallback. Make each z.union([structured, ExpressionInputSchema]):
-
system/audit.zod.tscondition -
system/metrics.zod.tssuccessCriteria,condition -
system/tracing.zod.tscondition -
cloud/marketplace-admin.zod.tscriteria
These remain non-CEL by design and should NOT be unified:
- SQL fragments —
data/analytics.zod.ts(sql),data/object.zod.ts(partialSQL WHERE). Driver-native; portability story differs. - NoSQL filters —
data/driver-nosql.zod.tspartialFilterExpression(MongoDB JSON). - OData / OpenAPI runtime expressions —
api/odata.zod.ts,api/rest-server.zod.tsexpression({$request.body#/callbackUrl}). Externally-specified DSLs. - Wire query params —
api/protocol.zod.tsfilter/filters/sort. JSON-encoded ObjectQL on the wire. - Structured
FilterCondition—data/data-engine.zod.tswhere. Typed object DSL preferred for query planner; CEL escape hatch already exists viaView.criteria/Workflow.criteria.
| Item | Reason |
|---|---|
Branch / sys_branch / branch_id |
Deferred. Phase 1 has one current metadata state per project. |
| Branch hostnames, branch diff, branch merge | Deferred with the branch model. |
| Studio metadata editing | Deferred. Studio is read-only for user metadata in Phase 1. |
| Bidirectional CLI ↔ Studio write model | Deferred. Local TS workspace is the only metadata authoring surface in Phase 1. |
objectstack pull JSON -> TS emitter |
Deferred until there is a control-plane writer that can change metadata outside local TS. |
| Merge/conflict UX | Deferred. commitId identifies revisions and artifacts, not collaborative merge state. |
| Versioning / Release / Tag entity | Deferred. Freezing current metadata into immutable releases comes later. |
| Salesforce-flavor formula compatibility | Deferred / not pursued. The legacy 22-function custom DSL (D9) is replaced by CEL (M9), not extended. Authors targeting Salesforce semantics rewrite in CEL — see north-star §8 anti-pattern "No private DSL". |
| S3 artifact backend | Deferred. Artifact API response shape should allow it later, but backend is control-plane DB now. |
These are intentionally outside Phase 1 but should remain compatible with the Phase 1 model.
- Add visual metadata editors in Studio.
- Route every Studio save through a control-plane metadata write API.
- Introduce optimistic concurrency for CLI ↔ Studio writes.
- Decide whether
objectstack pullgenerates canonical TS or attempts source-preserving round trips.
- Add
sys_branchandbranch_id. - Migrate the Phase 1 current project metadata state to default branch
main. - Evolve partition key from
(organization_id, project_id)to(organization_id, project_id, branch_id). - Add branch hostnames, branch diff, branch merge, and conflict UX.
- Add Release / Tag entity.
- Freeze project or branch states into immutable artifacts.
- Add rollback UI.
- Swap Artifact API backend to S3/signed URL where useful.
M1 Artifact format v0
├── M1.x Runtime Inputs 边界化 (Artifact + Deployment Config 分离)
└── M2 Metadata migration to control plane
├── M3 Project Artifact API
│ └── M4 ObjectOS artifact loader
└── M5 Project publish endpoint -> resolves D2
└── M6 Studio metadata/artifact viewer
M7 objectstack dev offline boot (parallel after M1)
M8 UI auto-generation (long tail after artifact schema stabilizes)
M9 Expression unification (parallel; spec-only changes, no Phase-1 prereq)
├── M9.1 packages/formula + ExpressionSchema
├── M9.2 DX shorthand (cel`…`, F`…`, P`…`) + compile normalization
├── M9.3 Replace ~25 z.string() expression fields → resolves D10
├── M9.4 Dataset SeedValue + SeedLoader expression eval → resolves D11
├── M9.5 Delete packages/objectql/src/formula.ts → resolves D9
├── M9.6 Migrate examples/app-crm + byte-identical-build CI gate
├── M9.7 AI structured-output (publish JSON Schema + skill doc)
└── M9.8 Studio visual editor (after M6)
D3 remove env_id (after M2 ownership columns exist)
D8 split apps/cloud + apps/objectos(after M3/M4 make ObjectOS standalone)
D9 / D10 / D11 (resolved through M9 sub-tasks above)
| Document | Role |
|---|---|
| content/docs/concepts/north-star.mdx | Authoritative spec - §1 tenets, §3 surfaces, §5 architecture, §7 ledger, §9 open questions |
| CLAUDE.md | Dev conventions - Zod-first, naming, kernel standards |
| .github/copilot-instructions.md | Mirror of CLAUDE.md for Copilot |
| packages/cli/src/commands/compile.ts | TS -> JSON compile (Built anchor) |
| packages/cli/src/commands/publish.ts | Publish command (Drift D2 target) |
| packages/metadata/src/plugin.ts | MetadataPlugin (artifact/local-file metadata loader; D1 resolved anchor) |