Skip to content

feat(spec): PLATFORM_PLUGIN_WIRED_RUNTIMES — provenance roster for plugins[]-wired out-of-repo runtimes - #11613

Merged
os-steve merged 1 commit into
mainfrom
claude/issue-11263-organizations-capability-roster
Aug 24, 2026
Merged

feat(spec): PLATFORM_PLUGIN_WIRED_RUNTIMES — provenance roster for plugins[]-wired out-of-repo runtimes#11613
os-steve merged 1 commit into
mainfrom
claude/issue-11263-organizations-capability-roster

Conversation

@os-steve

Copy link
Copy Markdown
Collaborator

Fixes#11263

Measurement map — why the triage gloss ("add the row") is not directly executable

Re-measured on 6d3550961f (this branch's base), confirming the card's own structural objection:

  • PLATFORM_CAPABILITY_PROVIDERS (packages/spec/src/kernel/platform-capabilities.ts) is keyed by requires capability token, and its own docblock defines the vocabulary as "the union of every token the framework CLI and cloud's objectos-runtime resolve".
  • @objectstack/organizations is resolved by neither through requires. serve.ts loads it off the resolved tenancy posture (multiTenant = tenancyPosture !== 'single', ADR-0105 D1, serve.ts around line 2777), via importFromHost from the host app (cloud#1013). It backs no token; defineStack today rejects requires: ['organizations'] as an unknown token — correctly.
  • @objectstack/security-enterprise is also plugins[]-wired and has a row only because it additionally backs the hierarchy-security token.
  • Consequence: minting an organizations token to make a row keyable would enter the requires vocabulary (the drift test enforces 1:1), flipping requires: ['organizations'] from authoring-time rejection to accepted-but-resolved-by-nothing — new capability-token semantics, which the dispatch marks as maintainer territory. Widening the map's key space past the vocabulary breaks the same 1:1 invariant and would make classifyRequiredCapability classify a non-token as a provider hit. Both rejected by measurement.

Chosen shape — the minimal by-construction alternative the card itself anticipated: a sibling, package-name-keyed provenance export in the same file, covering the plugins[]-wired out-of-repo population, which is exactly two.

Changes

  • packages/spec/src/kernel/platform-capabilities.ts — new exported PLATFORM_PLUGIN_WIRED_RUNTIMES (Readonly Record, frozen; key = npm package name) with row type PlatformPluginWiredRuntime (edition: Exclude of CapabilityEdition minus 'open' — the mis-filing cannot compile; note required). Rows: @objectstack/organizations (enterprise, posture-load provenance in prose) and @objectstack/security-enterprise (enterprise, names its hierarchy-security dual listing). The token-keyed map's docblock now points at the sibling for the population it structurally cannot hold. Provenance only: no new token, no widened key space, no posture-to-token resolution semantics — each runtime's load condition stays in the runtime that loads it.
  • packages/spec/src/index.ts — root-entry named exports for both symbols (kernel subpath re-exports wholesale).
  • packages/cli/test/serve-capability-vocabulary.test.ts — the drift test the card names, extended with the cross-roster pins: (1) the organizations row exists and is enterprise; (2) every enterprise-edition provider package must have a roster row — derived from CapabilityEdition's own definition (enterprise = the app installs and wires via plugins[]), so security-enterprise's double listing is checked rather than divergent, and the next enterprise token is covered on arrival; (3) wherever both rosters name one package they agree on edition; (4) no roster package appears in serve's CAPABILITY_PROVIDERS package values.
  • packages/spec/src/kernel/platform-capabilities.test.ts — shape invariants: frozen/non-empty, keys are @objectstack/ package names, notes non-empty, runtime edition check, and keys disjoint from the capability vocabulary (the single-list rule's load-bearing half: this roster can never grow a second way to spell a requires token).
  • packages/spec/api-surface/ + export-origins/ — regenerated via check:generated --fix (only the 2 proved-stale artifacts); diff is exactly the two new symbols, additive.
  • .changeset/plugin-wired-runtime-roster.md@objectstack/spec: minor (new exported symbols; not breaking, no ADR-0087 marker owed).

On the single-list warning the file carries: the two rosters have disjoint key spaces (tokens vs package names) and disjoint membership criteria (requires-resolved vs plugins[]-wired); the one fact they can state twice (edition of a package in both) is drift-tested to agree. Residual not covered by construction: serve.ts's own organizationsPkg literal is not read from this roster (serve consumption was outside the claimed file surface); if the PM wants the runtime to consume the roster's key, that is a one-line follow-up.

Verification — all at final commit e239aa5fe4 (tree clean; no commits after any run)

  • spec build → ✓ packages/spec/dist/.build-input-hash written, exit 0.
  • pnpm --filter @objectstack/spec testTest Files 420 passed (420) · Tests 11218 passed (11218).
  • pnpm --filter @objectstack/spec typecheckcheck:test-typecheck: OK — @objectstack/spec's test layer compiles, exit 0.
  • pnpm --filter @objectstack/spec check:generated → first run named 2 stale (api-surface/, export-origins/); after --fix: ✓ All 14 generated artifacts are up to date.
  • pnpm --filter @objectstack/cli exec vitest run test/serve-capability-vocabulary.test.tsTests 12 passed (12); pnpm --filter @objectstack/cli typecheck → exit 0.
  • Ablation of the new drift pins, both legs rebuilt and disk-confirmed. Subject resolves through @objectstack/spec/kernel → spec dist/, so both legs rebuilt spec and ran scripts/ablation-dist-preflight.mjs. Mutant leg (organizations edition flipped to cloud + security-enterprise roster row deleted): anchored-write confirmation injected A count: 1 · injected B count: 1 · removed roster anchor count: 0; preflight ✓ marker present in 8 built files for both markers; predicted direction RED on exactly the two targeted pins — observed Tests 2 failed | 10 passed (12), the two failures being the organizations-edition pin and the enterprise-membership pin, nothing else. Restore leg (trap-guarded git checkout HEAD): markers 0/0 on disk, roster anchor back to 1, preflight --absent✓ marker absent from all 209 built files, rerun Tests 12 passed (12).
  • Gate families derived from the actual diff with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (no paths passed; stderr confirmed derivation from this checkout at e239aa5fe4): 27 path-derived + 3 convention-triggered. All run locally, each exit code captured before any pipe (per-gate log + immediate $?): check:nul-bytes · check:changeset-gate-self-tests · check:objectui-changeset · check-adr-0087-registration · check-changeset-no-major · check-empty-changeset · release-rehearsal-clone --self-test · check:merge-driver · check-dev-prereqs · check-ci-filter-parity · check:cross-package-test-inputs · check:slot-lookup · check:spec-parsed-alias · check:test-source-alias · check:type-source-resolution · check:published-files · check:query-options-erasure · check:engine-double-contract · check:where-matcher · check-plugin-teardown-shape · check-affected-docs · check:empty-state · check:liveness · check:strictness-ledger · check:variant-docs · check:doc-formula-expressions · check:i18n · check:i18n-coverage · check:type-check-coverage · check:type-check-debt --re-measure — all exit 0. Verdict lines quoted rather than bare exits: check-type-check-coverage: OK — 65/78 workspace packages type-checked; check-type-check-coverage --re-measure: OK — 32 ledger entr(ies) re-measured in 224.7s, 1897 raw tsc error(s) total, none above its recorded number (run against the built closure: turbo run build --filter=./packages/* --filter=./packages/*/*70 total, exit 0, so the verdict is a measurement, not a refusal). Three gates (dev-prereqs, i18n, i18n-coverage) first failed on the documented unbuilt-workspace precondition and passed after the closure build — build-state, not diff findings.
  • Repo-wide pnpm lint (eslint . --no-inline-config) run in full, not narrowed → exit 0 at e239aa5fe4.

Draft on purpose — needs:contract-review (clause ②: new public spec surface); the PM flips after contract review. Merge is not blocked on any other card; #10921 is already closed by its own PR and is referenced here as prior fact only.

Generated by Claude Code


Generated by Claude Code

…r plugins[]-wired out-of-repo runtimes
The token-keyed PLATFORM_CAPABILITY_PROVIDERS map structurally cannot hold
@objectstack/organizations: it backs no requires token (serve loads it off
the resolved tenancy posture, ADR-0105). Add a sibling, package-name-keyed
provenance roster covering the plugins[]-wired out-of-repo population of two
(organizations + security-enterprise), edition typed to exclude 'open' by
construction. Provenance only: no new capability token, no widened key space,
no posture-to-token resolution semantics. Drift pins in
serve-capability-vocabulary.test.ts hold the two rosters to one edition
wherever they name the same package, and derive enterprise-row membership so
the next enterprise token is covered on arrival.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T9cDbY2NBiVJWYx3BpWfH2
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

2 anchor(s) derived from 1 changed package(s); no hand-written page names any of them. ✅

What this run could not see
  • 4 changed file(s) yielded no anchor (packages/spec/api-surface/kernel.json, packages/spec/api-surface/root.json, packages/spec/export-origins/kernel.json, …) — pages documenting those are invisible to this run
  • 2 name(s) were too generic to anchor anything (single lowercase words)

Coarse fallback — 126 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 2cf5a96cc77cc4a7b64c2512246ac1e121f348d8packageMentionDocs.

Which tree this was computed on

This run read content/docs from 4a88f205f009f05b7a5638768dd54a9f46bcc4ea — the merge of head e239aa5fe4d807724da894ee17b639cf57fd8a3a into base 2cf5a96cc77cc4a7b64c2512246ac1e121f348d8, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 4a88f205f009f05b7a5638768dd54a9f46bcc4ea && git checkout 4a88f205f009f05b7a5638768dd54a9f46bcc4ea
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 2cf5a96cc77cc4a7b64c2512246ac1e121f348d8 e239aa5fe4d807724da894ee17b639cf57fd8a3a && git checkout -B drift-repro 2cf5a96cc77cc4a7b64c2512246ac1e121f348d8 && git merge --no-ff e239aa5fe4d807724da894ee17b639cf57fd8a3a
node scripts/docs-audit/affected-docs.mjs --json 2cf5a96cc77cc4a7b64c2512246ac1e121f348d8

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation tests tooling labels Aug 24, 2026
@os-steve
os-steve marked this pull request as ready for review August 24, 2026 08:52
@os-steve
os-steve added this pull request to the merge queueAug 24, 2026
Merged via the queue into main with commit 177ebdcAug 24, 2026
39 checks passed
@os-steve
os-steve deleted the claude/issue-11263-organizations-capability-roster branch August 24, 2026 09:26
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

2 participants

@os-steve@claude