-
3f218e4: feat(create-objectstack): the blank scaffold ships the three generic connector executors by default
npm create objectstacknow generates anobjectstack.config.tsthat wires therest,openapi, andmcpconnector executor plugins (ADR-0022/0023/0024 + ADR-0097) intoplugins:, alongsiderequires: ['automation']. This closes the last authoring gap in the ADR-0097 promise that integrations are expressible and executable as pure metadata: an author (human or AI) can now add a declarativeconnectors:entry namingprovider: 'rest' | 'openapi' | 'mcp'and have it materialize into a live, dispatchable connector at boot — with no host-code edit.plugins:—new ConnectorRestPlugin(),new ConnectorOpenApiPlugin(),new ConnectorMcpPlugin()(zero-arg = contribute the provider factory only).requires: ['automation']— the automation service performs the materialization and owns the registry the executors register into. It is also a hard dependency of the connector plugins, so a scaffold that lists them inplugins:without it fails boot; automation ships transitively via@objectstack/cli.- deps —
@objectstack/connector-rest,@objectstack/connector-openapi,@objectstack/connector-mcp. - Security (#3055): declarative
mcpstdio transports stay denied by default — opt in per host withnew ConnectorMcpPlugin({ declarativeStdio: ['node'] }).
Brand connectors (Slack, …) remain marketplace/opt-in.
-
83e8f7d: feat(mcp): decouple the stdio auto-start switch from the HTTP surface + surface the MCP endpoint on
os devboot (#3167)The MCP HTTP surface (
/api/v1/mcp) and the long-lived stdio transport used to share one env var:OS_MCP_SERVER_ENABLED=trueturned the HTTP surface on and silently auto-started the stdio transport — which bridges the raw metadata service- data engine with no per-request principal (unscoped). An operator setting it to "make sure MCP is on" got an unscoped transport as a side effect.
@objectstack/types— newresolveMcpStdioAutoStart(). Stdio auto-start is now its own switch,OS_MCP_STDIO_ENABLED(default off);OS_MCP_SERVER_ENABLEDgoverns only the HTTP surface. The legacyOS_MCP_SERVER_ENABLED=truetrigger still starts stdio for one release, flagged as deprecated.=falseis unchanged (it only ever gated HTTP).@objectstack/mcp—MCPServerPlugin.start()gates stdio on the new switch and logs a one-time deprecation warning when started via the legacy alias.@objectstack/cli—os devnow prints the MCP endpoint, the agent-skill URL, and a ready-to-pasteclaude mcp addcommand on boot (gated on the HTTP surface being on), so the "an agent operates the app it's building" loop is discoverable at dev time.create-objectstack— the blank scaffold README documents that the app is itself an MCP server (the serve side), distinct from the consume-side connector.
-
3b6ef8a: Scaffolded projects ship with a
.gitignoreagain —npx create-objectstackproduced none, leavingnode_modules/and.envun-ignored for every new user.npm pack/pnpm packstrip.gitignorefrom a tarball unconditionally, at every depth. The blank template committed one atsrc/templates/blank/.gitignoreand the build faithfully copied it todist/templates/blank/.gitignore, butfiles: ["dist"]publishing dropped it on the way to the registry — so the file was present in the repo, present in every local build, and absent from all 11 files of a real scaffold. Verified against the published 15.1.1 tarball, which shipsdist/templates/blank/.dockerignoreand no.gitignore.The template is now committed as
_gitignore(a name npm does not strip) and restored to.gitignorewhen the template is copied, via aTEMPLATE_FILE_ALIASESmap in the newtemplate-copy.ts. Only.gitignoreis aliased: the strip list is.gitignoreand.npmrc, not "every dotfile" —.dockerignorepacks fine and stays literal.The restored ignore rules also cover
.env/.env.*, which they never did. The template README has users writeOS_AUTH_SECRETandOS_SECRET_KEYinto a.env, anddocker-compose.ymlcalls that file "never committed" — but only the prose said so, and.dockerignorewas the only file that listed it.A packing ratchet in
template-consistency.test.tsguards both halves: it packs the real package, scaffolds from the extracted tarball with the real copy logic, and asserts every template file lands under its intended name. Source-level assertions cannot see this class of bug — the file only vanishes at publish. -
3a8ce9d: fix(create-objectstack): the blank scaffold declares pnpm build approvals, so a fresh
pnpm installno longer exits 1 on pnpm 11pnpm 11 turned an unapproved dependency build script from a warning into a hard error. The blank template declared no build approvals, so the very first command a new user runs failed on any current pnpm:
npx create-objectstack myapp && cd myapp && pnpm install # [ERR_PNPM_IGNORED_BUILDS] Ignored build scripts: better-sqlite3@12.11.1, esbuild@0.28.1 # exit 1The scaffold now ships a
pnpm-workspace.yamlapproving the two packages it actually depends on building —better-sqlite3(the native sqlite driver behind@objectstack/driver-sql) andesbuild(compilesobjectstack.config.ts).Both approval keys are present because pnpm reads them by version, and neither alone covers the supported range:
allowBuilds(a package → boolean map) — the only key pnpm 11 honors, and understood back to pnpm 10.31.onlyBuiltDependenciesalone still errors.onlyBuiltDependencies(a list) — pnpm 10.0–10.30, which ignoreallowBuilds.
npm and yarn ignore the file, so the npm install path is unaffected. Both packages ship prebuilt binaries, so this was an install-time hard stop rather than a runtime defect — the project ran fine once installed.
This is the #3091 failure class (in-repo settings masking what users resolve) and was caught by the publish smoke gate added in #3100, which installs the release candidate the way a user does — on whatever pnpm corepack hands a fresh machine.
-
809214f: Stop leaking repo-internal skills into scaffolded projects. The scaffolder (and the docs) advertised
npx skills add objectstack-ai/objectstack --all, and the skills CLI's--allimplies--skill '*'— which includes evenmetadata.internalskills — so repo-internal tooling like.claude/skills/dogfood-verificationlanded in every new project's.agents/skills/. All install commands are now scoped to the published catalog via the/skillssubpath (npx skills add objectstack-ai/objectstack/skills --all), the internal skill is additionally markedmetadata.internal: trueto hide it from interactive discovery, and a template-consistency ratchet plus a scaffold-e2e assertion keep the boundary from regressing.
-
3f218e4: feat(create-objectstack): the blank scaffold ships the three generic connector executors by default
npm create objectstacknow generates anobjectstack.config.tsthat wires therest,openapi, andmcpconnector executor plugins (ADR-0022/0023/0024 + ADR-0097) intoplugins:, alongsiderequires: ['automation']. This closes the last authoring gap in the ADR-0097 promise that integrations are expressible and executable as pure metadata: an author (human or AI) can now add a declarativeconnectors:entry namingprovider: 'rest' | 'openapi' | 'mcp'and have it materialize into a live, dispatchable connector at boot — with no host-code edit.plugins:—new ConnectorRestPlugin(),new ConnectorOpenApiPlugin(),new ConnectorMcpPlugin()(zero-arg = contribute the provider factory only).requires: ['automation']— the automation service performs the materialization and owns the registry the executors register into. It is also a hard dependency of the connector plugins, so a scaffold that lists them inplugins:without it fails boot; automation ships transitively via@objectstack/cli.- deps —
@objectstack/connector-rest,@objectstack/connector-openapi,@objectstack/connector-mcp. - Security (#3055): declarative
mcpstdio transports stay denied by default — opt in per host withnew ConnectorMcpPlugin({ declarativeStdio: ['node'] }).
Brand connectors (Slack, …) remain marketplace/opt-in.
-
83e8f7d: feat(mcp): decouple the stdio auto-start switch from the HTTP surface + surface the MCP endpoint on
os devboot (#3167)The MCP HTTP surface (
/api/v1/mcp) and the long-lived stdio transport used to share one env var:OS_MCP_SERVER_ENABLED=trueturned the HTTP surface on and silently auto-started the stdio transport — which bridges the raw metadata service- data engine with no per-request principal (unscoped). An operator setting it to "make sure MCP is on" got an unscoped transport as a side effect.
@objectstack/types— newresolveMcpStdioAutoStart(). Stdio auto-start is now its own switch,OS_MCP_STDIO_ENABLED(default off);OS_MCP_SERVER_ENABLEDgoverns only the HTTP surface. The legacyOS_MCP_SERVER_ENABLED=truetrigger still starts stdio for one release, flagged as deprecated.=falseis unchanged (it only ever gated HTTP).@objectstack/mcp—MCPServerPlugin.start()gates stdio on the new switch and logs a one-time deprecation warning when started via the legacy alias.@objectstack/cli—os devnow prints the MCP endpoint, the agent-skill URL, and a ready-to-pasteclaude mcp addcommand on boot (gated on the HTTP surface being on), so the "an agent operates the app it's building" loop is discoverable at dev time.create-objectstack— the blank scaffold README documents that the app is itself an MCP server (the serve side), distinct from the consume-side connector.
-
3b6ef8a: Scaffolded projects ship with a
.gitignoreagain —npx create-objectstackproduced none, leavingnode_modules/and.envun-ignored for every new user.npm pack/pnpm packstrip.gitignorefrom a tarball unconditionally, at every depth. The blank template committed one atsrc/templates/blank/.gitignoreand the build faithfully copied it todist/templates/blank/.gitignore, butfiles: ["dist"]publishing dropped it on the way to the registry — so the file was present in the repo, present in every local build, and absent from all 11 files of a real scaffold. Verified against the published 15.1.1 tarball, which shipsdist/templates/blank/.dockerignoreand no.gitignore.The template is now committed as
_gitignore(a name npm does not strip) and restored to.gitignorewhen the template is copied, via aTEMPLATE_FILE_ALIASESmap in the newtemplate-copy.ts. Only.gitignoreis aliased: the strip list is.gitignoreand.npmrc, not "every dotfile" —.dockerignorepacks fine and stays literal.The restored ignore rules also cover
.env/.env.*, which they never did. The template README has users writeOS_AUTH_SECRETandOS_SECRET_KEYinto a.env, anddocker-compose.ymlcalls that file "never committed" — but only the prose said so, and.dockerignorewas the only file that listed it.A packing ratchet in
template-consistency.test.tsguards both halves: it packs the real package, scaffolds from the extracted tarball with the real copy logic, and asserts every template file lands under its intended name. Source-level assertions cannot see this class of bug — the file only vanishes at publish. -
3a8ce9d: fix(create-objectstack): the blank scaffold declares pnpm build approvals, so a fresh
pnpm installno longer exits 1 on pnpm 11pnpm 11 turned an unapproved dependency build script from a warning into a hard error. The blank template declared no build approvals, so the very first command a new user runs failed on any current pnpm:
npx create-objectstack myapp && cd myapp && pnpm install # [ERR_PNPM_IGNORED_BUILDS] Ignored build scripts: better-sqlite3@12.11.1, esbuild@0.28.1 # exit 1The scaffold now ships a
pnpm-workspace.yamlapproving the two packages it actually depends on building —better-sqlite3(the native sqlite driver behind@objectstack/driver-sql) andesbuild(compilesobjectstack.config.ts).Both approval keys are present because pnpm reads them by version, and neither alone covers the supported range:
allowBuilds(a package → boolean map) — the only key pnpm 11 honors, and understood back to pnpm 10.31.onlyBuiltDependenciesalone still errors.onlyBuiltDependencies(a list) — pnpm 10.0–10.30, which ignoreallowBuilds.
npm and yarn ignore the file, so the npm install path is unaffected. Both packages ship prebuilt binaries, so this was an install-time hard stop rather than a runtime defect — the project ran fine once installed.
This is the #3091 failure class (in-repo settings masking what users resolve) and was caught by the publish smoke gate added in #3100, which installs the release candidate the way a user does — on whatever pnpm corepack hands a fresh machine.
-
809214f: Stop leaking repo-internal skills into scaffolded projects. The scaffolder (and the docs) advertised
npx skills add objectstack-ai/objectstack --all, and the skills CLI's--allimplies--skill '*'— which includes evenmetadata.internalskills — so repo-internal tooling like.claude/skills/dogfood-verificationlanded in every new project's.agents/skills/. All install commands are now scoped to the published catalog via the/skillssubpath (npx skills add objectstack-ai/objectstack/skills --all), the internal skill is additionally markedmetadata.internal: trueto hide it from interactive discovery, and a template-consistency ratchet plus a scaffold-e2e assertion keep the boundary from regressing.
-
f531a26: feat(protocol): complete ADR-0087 — load-seam handshake, chain backfill 12–15, release artifacts (#2643)
Closes the remaining ADR-0087 gaps (see the ADR's as-built Addendum):
- P0 load seams (D1). The protocol handshake now runs on the boot-time
durable-package rehydration path (
@objectstack/service-packagerefuses an incompatiblesys_packagesrow with the structuredOS_PROTOCOL_INCOMPATIBLEdiagnostic and keeps booting) and onAppPluginfor code-defined stacks (fail-fast before the manifest is decomposed).objectstack lintgainsprotocol/missing-engines-range(warning + fix-it) and thecreate-objectstackblank template stampsengines: { protocol: '^<major>' }(re-stamped at version time byscripts/sync-template-versions.mjs) — the two ends of the grandfathering ratchet. - Chain backfill (D2/D3).
MetadataConversion.retiredFromLoadPathimplements the load-window's second half (retired entries replay only viamigrate meta/ fixture CI). Steps 12–15 land: theapi.requireAuthflip (semantic), the ADR-0090 wave (3 retired conversions + 5 semantic TODOs), theBookAudiencerename (retired conversion), and the ADR-0089 visibility unification (visibleOn/visibility→visibleWhenas LIVE load-window conversions) + the.strict()flip (semantic). The protocol-11compactLayout→highlightFieldsrename is backfilled as a retired step-11 conversion.migrate meta --from 10now reaches protocol 15. - Release artifacts (D4).
spec-changes.jsonis generated from the registries (gen:spec-changes, CI drift-checked), ships in the npm artifact together withapi-surface.json, and is attached to each@objectstack/specGitHub Release withadded[]/removed[]filled from the api-surface diff against the previously published release. The upgrade guide (docs/protocol-upgrade-guide.md) is generated from the same registries and CI drift-checked — a projection that cannot drift.
- P0 load seams (D1). The protocol handshake now runs on the boot-time
durable-package rehydration path (
-
f531a26: Scaffolded projects are now container-ready out of the box: the
blanktemplate ships aDockerfile(two-stage build onto the officialghcr.io/objectstack-ai/objectstackruntime image), adocker-compose.yml(app + Postgres single-host stack), and a.dockerignore, plus a Deploy section in the project README.docker build -t my-app .works immediately afternpm create objectstack.
- eaff014: Scaffolded projects now install the current framework release instead of a stale major. The bundled
blanktemplate had^6.0.0ranges frozen in while the registry was publishing 14.x, sonpm create objectstackproduced a project eight majors behind the docs — and the template's code no longer compiled against 14.x anyway (Field.longTextremoved,api.restno longer adefineStackkey,sharingModelnow required by the ADR-0090 security gate). The template is updated to the current API, and the scaffolder now rewrites every@objectstack/*range in the generatedpackage.jsonto^<its own version>(all packages version in lockstep), so generated projects track the release even if the committed template drifts again. A consistency test ratchets the template's major and the README's template table against the registry. The template README also documents the seeded dev-admin sign-in that data-API curls need.
-
7cf283a: Make
os validatethe author-time verification gate and steer scaffolds toward it.os validatenow runs the same CEL/predicate gate asos build/os compile(ADR-0032): everyvisible/disabled/requiredWhen/validation/flow/sharing predicate is checked for CEL syntax andrecord.<field>existence on the target object. It already ran the protocol schema and widget-binding checks; the expression gate closes the gap so a bare field ref (doneinstead ofrecord.done) — which silently hides an action on every record at runtime (#2183/#2185) — fails validation instead of shipping.os validateis now a read-only superset of the build's checks (no artifact emitted).create-objectstacknow emits anAGENTS.md(and.github/copilot-instructions.md) into every generated project instructing coding agents to runnpm run validateafter editing metadata, aligns the blank template'sdev/startscripts with the example apps (objectstack dev/objectstack start), and sharpens the post-create "Next steps" output.
-
15fc484: Upgrade
@object-ui/*packages to v6.0.@objectstack/cli:@object-ui/consoleand@object-ui/studiofrom^5.4.2→^6.0.0— bundled Studio + Console assets now ship the v6 UI shell (new design language, refreshed sidebar, redesigned record header).@objectstack/account:@object-ui/i18nfrom^5.4.2→^6.0.0— i18n runtime now matches the v6 console/studio API.- Root devDependency
@object-ui/consolefrom^5.4.2→^6.0.0so workspace scripts and the docs build pick up v6. create-objectstack:tarfrom^7.4.3→^7.5.15(security + perf fixes when unpacking remote templates).
Heads-up for consumers:
@object-ui/*v6 is a major release of the bundled UI; pages rendered through the CLI'sstudio/consolemounts may look different from v5. The protocol surface is unchanged.
- 15e0df6: chore: unify all package versions to a single patch release