Skip to content

feat(spec): retire contributes.routes (ruled Option B) and redirect its last two teaching sites to the http.server mount - #12417

Merged
huangyiirene merged 2 commits into
mainfrom
claude/issue-10726-routes-tombstone
Aug 26, 2026
Merged

huangyiirene merged 2 commits into
mainfrom
claude/issue-10726-routes-tombstone

Conversation

@huangyiirene

@huangyiirene huangyiirene commented Aug 26, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes #10726
Fixes #11328

Family PR (chain head #10726 + folded member #11328), one commit per member. Executes the 2026-08-22 maintainer ruling on #10726 (Option B, accepted verbatim 「接受所有」): remove contributes.routes; author-facing materials point at the imperative http.server mount. The ruling's cloud precondition is verified (#10812: cloud @ 5b5925a, zero manifest.contributes reads, controls green). The four originally-enumerated doc sites landed earlier via PR #11327 and are not redone here.

Member 1 — #10726, the tombstone half (commit 1)

  • packages/spec/src/kernel/manifest.zod.ts: contributes.routes becomes a retiredKey() tombstone — neither ManifestSchema nor the contributes object is .strict(), so a plain deletion would silently strip the key (the loading / Retire the nine dead members of the plugin-manifest contributes block (events/menus/themes/translations/actions/drivers/fieldTypes/functions/commands) — tombstone route, kinds survives #10724 route). The block's fence comment now records TEN removed members and names kinds as the sole survivor. contributes.kinds (live reader: packages/objectql/src/engine.ts → registry.registerKind) is untouched.
  • ADR-0087 registration, both obligations: retired-key entry kernel/Manifest:contributes.routes + D3 semantic entry plugin-manifest-contributes-routes-retired, one file each under packages/spec/src/migrations/entries/ (major 18, the launch-window convention Retire the nine dead members of the plugin-manifest contributes block (events/menus/themes/translations/actions/drivers/fieldTypes/functions/commands) — tombstone route, kinds survives #10724 established), registry.ts regions regenerated with gen:migration-registry. Deliberately no D2 conversion: a package manifest is not a stack collection member (PLURAL_TO_SINGULAR has no packages/plugins entry), so a conversion would be a transform with no seam that ever runs — the kernel/Manifest:loading precedent, recorded verbatim in the entry.
  • Liveness ledger: packages/spec/liveness/manifest.json routes row re-graded RETIRED with the row STAYING (a tombstone keeps the key in the drilled shape — the rls.priority precedent), _note DISPOSITIONS and the README manifest row updated; counts unchanged (routes was already counted among the dead members).
  • Pin tests (manifest.test.ts): a rejection pin asserting the SPECIFIC zod issue (located at contributes.routes, matching #10726 + Delete the key + http.server), a kinds-only survivor pin with not.toHaveProperty('routes'); the previous survivor pin asserting routes parses — the exact opposite state — is replaced. The two assertions are mutually exclusive across this change, which is the red/green reverse evidence for the pin (old pin green on base, new pin green here; no dist resolution involved — the test imports ./manifest.zod from src, so no ablation build applies).
  • Changeset .changeset/manifest-contributes-routes-retired.md: minor per the launch-window convention (check-changeset-no-major green), FROM → TO mapping, and the disposition marker adr-0087: registered plugin-manifest-contributes-routes-retired.

Member 2 — #11328, the two remaining teaching sites (commit 2)

  • packages/spec/src/api/plugin-rest-api.zod.ts:31-63: the full worked @example Plugin Manifest block — a complete, copy-pasteable contributes.routes manifest whose every route served nothing — replaced with an imperative http.server mount example modeled on the verified-live consumer (packages/plugins/plugin-hono-server/src/hono-plugin.ts:271 registers the service; examples/app-showcase/src/system/server/recalc-endpoint.ts mounts POST /api/v1/showcase/recalc on kernel:ready).
  • packages/spec/src/kernel/metadata-plugin.zod.ts: the router delivered-form comments stop crediting contributes.routes as a delivered form. The text had drifted since Two more author-facing sites still teach contributes.routes — a full worked manifest example in plugin-rest-api.zod.ts and the router delivered-form comments in metadata-plugin.zod.ts #11328 was filed — re-measured at :118-122 and :124-128 on the branch base; both corrected to the imperative mount + declarative apis:.

Declared file-surface increment (bounded inline fix, same defect class)

packages/spec/src/api/dispatcher.zod.ts (protocol docblock) and packages/spec/src/ui/app.zod.ts (App.apis retiredKey guidance) — both PR-#11327-corrected sites still said an authored contributes.routes entry "parses cleanly and serves nothing", a statement THIS PR falsifies (the entry is now loudly rejected). Both updated to state the removal. Evidence of boundedness: mechanical one-sentence corrections, non-governed spec sources, no schema shape or export change, covered by the same check:docs regeneration already in this diff, no other in-flight claim holds either file (checked at claim).

Generated projections (never hand-edited)

Nine content/docs/references/** pages via gen:docs, docs/audits/2026-07-unknown-key-strictness-ledger.counts.md via gen:strictness-ledger, src/migrations/registry.ts marked regions via gen:migration-registry — all via check:generated --fix after a fresh pnpm --filter @objectstack/spec build.

Expected-reading notes for review

Verification (all at final commit be433ec; verdict lines from the gates' own output)

  • pnpm --filter @objectstack/spec test → Test Files 431 passed (431) / Tests 11428 passed (11428); targeted vitest run src/kernel/manifest.test.ts → 1 passed / 37 passed.
  • pnpm --filter @objectstack/spec typecheck → exit 0, including check:test-typecheck (OK — @objectstack/spec's test layer compiles; the edited test file is inside tsconfig.test.json's src/**/*.test.ts program).
  • pnpm --filter @objectstack/spec check:generated (after --fix) → all 14 artifact gates in sync, including check:migration-registry, check:spec-changes, check:upgrade-guide, check:authorable-surface, check:api-surface, check:docs, check:liveness, check:strictness-ledger.
  • Audit union derived with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (no paths passed — changeset derived from merge-base): 41 gates PASS at be433ec, among them check:liveness, check:empty-state, check:skill-refs, check:variant-docs, check-adr-0087-registration, check-changeset-no-major, check-empty-changeset, check:changeset-gate-self-tests, check:objectui-changeset, check:nul-bytes, check:cross-package-test-inputs, check:engine-double-contract, check:merge-driver, doc-family gates, check-ci-filter-parity.
  • check:skill-examples → real PASS after building the client closure (260 prose examples type-check across 3 surface(s)).
  • NOT MEASURED (prerequisite-not-met, not red): check:i18n (needs built CLI dist; this diff contains no i18n extraction inputs — no *.form.ts, no translations, no extract config) and check-dev-prereqs (reports the workspace-not-built precondition for packages this diff does not touch). CI measures both on a fresh build.
  • Declared narrowing — repo-wide pnpm lint not run locally (CI-owned). Targeted measurement with all three pieces: (1) population = eslint's own config resolution over the 9 changed .ts files; (2) --format json → files linted: 9 | errors: 0 | warnings: 0; (3) invariance — eslint.config.mjs states this repo never enables type-aware linting for ANY file, so this diff cannot move the verdict on any file it does not contain.

Generated by Claude Code

claude added 2 commits August 26, 2026 01:37
…ast dead member (ADR-0049, ruled Option B)

retiredKey() tombstone (non-strict object; plain deletion would silently
strip the key), ADR-0087 retired-key entry kernel/Manifest:contributes.routes
+ D3 semantic entry plugin-manifest-contributes-routes-retired (both under
major 18, launch-window convention), liveness ledger row updated in place
(tombstones keep the key in the drilled shape), pin tests (prescription as
the specific zod issue + kinds-only survivor), regenerated docs projections,
and the two spec docblocks that still said an authored entry 'parses cleanly'
(dispatcher.zod.ts, app.zod.ts App.apis guidance) updated to state the
removal. contributes.kinds — the block's sole live member — is untouched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E5LFCYBJ3q2s6yW6oMLxwy
…tes to the http.server mount

The worked '@example Plugin Manifest' block in plugin-rest-api.zod.ts — a
complete copy-pasteable manifest whose every route served nothing — becomes
an imperative http.server mount example modeled on the live consumer
(plugin-hono-server registers the service; app-showcase's recalc-endpoint
mounts on kernel:ready), and the metadata-plugin.zod.ts 'router'
delivered-form comments stop crediting contributes.routes as a delivered
form. Projection regenerated (plugin-rest-api.mdx).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E5LFCYBJ3q2s6yW6oMLxwy
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec, touching 30 documentable anchor(s). ⚠️ 4 changed file(s) yielded no anchor (packages/spec/liveness/README.md, packages/spec/liveness/manifest.json, packages/spec/src/api/dispatcher.zod.ts, …), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

45 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: node scripts/docs-audit/affected-docs.mjs --json 2e24115710640fba45938baf24a31fd9cc80694f.

⛔ 6 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails.

What this run could not see
  • 4 changed file(s) yielded no anchor (packages/spec/liveness/README.md, packages/spec/liveness/manifest.json, packages/spec/src/api/dispatcher.zod.ts, …) — pages documenting those are invisible to this run
  • 3 anchor(s) matched too much of the corpus to be a work list: /api/v1 (route, 81 pages), /api/v1/data (route, 35 pages), /api/v1/data/:object (route, 29 pages)
  • 6 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 47 of 219 client-bound route-ledger rows — the other 172 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 172: 14 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 56 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 102 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

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 2e24115710640fba45938baf24a31fd9cc80694f → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 5984647b7ff736bb22f0f8ac80a0841f9d1b1e4c — the merge of head be433ecc36fbc265385d9854602053595e497039 into base 2e24115710640fba45938baf24a31fd9cc80694f, 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 5984647b7ff736bb22f0f8ac80a0841f9d1b1e4c && git checkout 5984647b7ff736bb22f0f8ac80a0841f9d1b1e4c
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 2e24115710640fba45938baf24a31fd9cc80694f be433ecc36fbc265385d9854602053595e497039 && git checkout -B drift-repro 2e24115710640fba45938baf24a31fd9cc80694f && git merge --no-ff be433ecc36fbc265385d9854602053595e497039

node scripts/docs-audit/affected-docs.mjs --json 2e24115710640fba45938baf24a31fd9cc80694f

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

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 2e24115710640fba45938baf24a31fd9cc80694f → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@github-actions github-actions Bot added documentation Improvements or additions to documentation protocol:ui tests tooling labels Aug 26, 2026
@huangyiirene
huangyiirene marked this pull request as ready for review August 26, 2026 02:38
@huangyiirene
huangyiirene added this pull request to the merge queue Aug 26, 2026
Merged via the queue into main with commit bc56e18 Aug 26, 2026
38 checks passed
@huangyiirene
huangyiirene deleted the claude/issue-10726-routes-tombstone branch August 26, 2026 03:05
os-warren pushed a commit that referenced this pull request Aug 26, 2026
…tree

The merge of `main` into this branch left the `kernel/` count as OURS (274) —
the `merge=os-regen` driver defers rather than text-merging, because a
generator run mid-merge reads a half-merged tree. Regenerated from the fully
merged sources, as that driver's pending marker requires.

277 at the merge base; this branch's DistributedStateConfig removal takes 3
(the def plus its inline `auth` and `replication` objects) and #12417's
`contributes.routes` retirement on main takes 1, so 273 is the composition of
both sides, not a choice between them. Predicted before the run; observed
exactly. `check:strictness-ledger` green.
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 1, 2026
…it never implemented; distributedConfig retired (objectstack-ai#12340) (objectstack-ai#12425)

* fix(spec,core): stateStrategy refuses the two values it never implemented; distributedConfig retired (objectstack-ai#12340)

ADR-0049 enforce-or-remove, applied one level inside the host-driven library
the 2026-08-25 objectstack-ai#11825 ruling kept. The 'disk' and 'distributed' arms of
PluginStateManager.saveState both wrote to the same in-memory Map as 'memory'
and said so at debug level only; distributedConfig had zero readers anywhere.

- spec: stateStrategy narrowed to ['memory','none'] with the prescription on
  the enum's own error map (invisible to the four ratchets by construction);
  distributedConfig and DistributedStateConfigSchema removed (route 3 — no
  authored document, nothing parses this schema).
- core: registerPlugin refuses an unhonoured stateStrategy and a leftover
  distributedConfig with an ADR-0112 envelope, before the enabled check.
- ADR-0087: RETIRED_DEFS_BY_MAJOR[18] + D3 semantic entry.
- the objectstack-ai#11825 survivor pin for DistributedStateConfigSchema moves here
  deliberately, with the reasoning recorded beside it.

Part of objectstack-ai#12340

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W6HFzyH98W1YaQXhJUJt6o

* test(spec): assert the prescription's actual plural spelling ('were removed')

The refusal fired correctly; the assertion named the singular. Caught by the
run, not by reading.

Part of objectstack-ai#12340

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W6HFzyH98W1YaQXhJUJt6o

* chore(spec): regenerate strictness-ledger counts after the DistributedStateConfig removal

kernel/ 277 -> 274, fully accounted for: the removed DistributedStateConfigSchema
carried exactly three z.object nodes (the def plus its inline auth and
replication sub-objects). The prose ledger has no per-file verdict for
plugin-lifecycle-advanced.zod.ts, so no Class verdict needed re-examining.

Part of objectstack-ai#12340

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W6HFzyH98W1YaQXhJUJt6o

* chore(spec): regenerate the strictness-ledger counts from the merged tree

The merge of `main` into this branch left the `kernel/` count as OURS (274) —
the `merge=os-regen` driver defers rather than text-merging, because a
generator run mid-merge reads a half-merged tree. Regenerated from the fully
merged sources, as that driver's pending marker requires.

277 at the merge base; this branch's DistributedStateConfig removal takes 3
(the def plus its inline `auth` and `replication` objects) and objectstack-ai#12417's
`contributes.routes` retirement on main takes 1, so 273 is the composition of
both sides, not a choice between them. Predicted before the run; observed
exactly. `check:strictness-ledger` green.

---------

Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Oct 7, 2026
…tead of a tracker number (stage 11) (objectstack-ai#21736)

Part of objectstack-ai#20749
Clause-②: no

Stage 11 of this card, and the second area of class (e): the test
strings shipped under `packages/spec/src`, as ruled in `5902360492` on
objectstack-ai#20513. This stage takes the whole `kernel/` directory. Its 102
test-title and test-message literals carried 106 tracker ids citing 69
records. Each id now either states what its record decided, in words
(form D), or is dropped where the title already says it. Text only: no
assertion, fixture value, test count or code comment changes.

## Census at the base (`3fa850cf00`, the claim's base)

Instrument: stage 10's `census10.cjs` (md5
`9d08602ab972b4b8643c90d64d40fa41`) and stage 9's `census.cjs` (md5
`6e42a45a926d375013c32d62f16a296e`), both byte-identical to the copies
stage 10 used. A literal counts as a test title when its folded message
is argument 0 of a `describe` / `it` / `test` call, `.each` / `.skip` /
`.only` chains included. Everything else is an "other" string.

Both instruments read **1696 messages / 1807 ids in 405 files at the
base**, which is stage 10's reading at its head exactly. `kernel/` reads
102 / 106, also stage 10's figure.

| directory | files | messages / ids | titles | other |
|:--|--:|--:|--:|--:|
| `data/` | 95 | 468 / 501 | 445 / 475 | 23 / 26 |
| `ui/` | 81 | 392 / 415 | 374 / 397 | 18 / 18 |
| `api/` | 40 | 189 / 201 | 181 / 193 | 8 / 8 |
| `system/` | 34 | 154 / 165 | 128 / 138 | 26 / 27 |
| (files directly in `src/`) | 30 | 118 / 120 | 117 / 119 | 1 / 1 |
| **`kernel/`** (this PR) | 36 | **102 / 106** | 92 / 96 | 10 / 10 |
| `shared/` | 21 | 85 / 95 | 73 / 81 | 12 / 14 |
| `contracts/` | 25 | 63 / 74 | 59 / 70 | 4 / 4 |
| `conversions/` | 9 | 34 / 34 | 34 / 34 | 0 |
| `security/` | 8 | 28 / 28 | 28 / 28 | 0 |
| `ai/` | 9 | 18 / 20 | 13 / 15 | 5 / 5 |
| `identity/` | 6 | 15 / 15 | 14 / 14 | 1 / 1 |
| `integration/` | 4 | 14 / 14 | 13 / 13 | 1 / 1 |
| `migrations/` | 2 | 9 / 12 | 9 / 12 | 0 |
| `marketplace/`, `meta-spelling/`, `studio/` | 5 | 7 / 7 | 7 / 7 | 0 |
| **total** | **405** | **1696 / 1807** | **1587 / 1692** | **109 /
115** |

- **Controls.** Lit, a title:
`kernel/capability-metadata-kind.test.ts:66` reads one message with
objectstack-ai#5961. Lit, a template-literal expect message:
`kernel/cli-command-contribution-retirement.test.ts:74` reads one
message. Dark: the `// ─── [objectstack-ai#17178] …` comment at
`kernel/execution-context.test.ts:205` reads 0 (the file's messages sit
at `:72`, `:167`, `:219`, `:233` and `:237`). Planted in a scratch copy:
an id added to a title reads 1 / 1, and an id in an added comment reads
0.
- **A wider pattern** (any `#` plus digits) reads 107 / 111 under
`kernel/` at the base. The five extra hits are hex colours (`#94A3B8`,
`#0f0`) in `functional-completeness.test.ts` and one `§3.10 objectstack-ai#3` section
reference in `manifest.zod.ts`, a non-test file. None is a tracker id.
At the head the wider pattern reads only those five, and the gate
pattern reads 0 / 0.
- **At the head:** 1594 messages / 1701 ids in 369 files. `kernel/`
reads 0 / 0. Nothing else moved.

## How the area was chosen

Stage 10's rule, applied before any card was read: rank whole
first-level directories by ids, and take the busiest one within about
10% of the ~100-id bound. The four busiest each exceed the bound alone:
`data/` (501), `ui/` (415), `api/` (201) and `system/` (165). The files
directly in `src/` (120) are 20% over. `kernel/` (106) is the busiest
whole directory within the bound, and its census reads exactly stage
10's 106, so the rule needed no second pass.

**Named for the next stages:** `data/` (about five stages, by
subdirectory or file group; `data/driver/` alone is 52), `ui/` (about
four), `api/` (two), `system/` (two), the files directly in `src/` (one,
120), `shared/` (one, 95), `contracts/` with `conversions/` (one, 108),
and `security/`, `ai/`, `identity/`, `integration/`, `migrations/`,
`marketplace/`, `meta-spelling/` and `studio/` together (one, 96).

## What each id became

Of the 106 ids, 32 now state a decision in words, in 31 literals. 74 are
dropped where the title already explains them; two of those (objectstack-ai#14478) sit
in literals that also gained words for another id. Every record was read
with its comments through REST. 58 answer 200. Ten answer 404, and their
decisions were read from what landed. One is in a repository not
attached to this session.

| record | ids | result |
|:--|--:|:--|
| objectstack-ai#12007, objectstack-ai#11825, objectstack-ai#12340, objectstack-ai#4914, objectstack-ai#15932, objectstack-ai#11846, objectstack-ai#16059 | 10 of 20 |
Every expect message that read "must have zero holders after #N" or
"must not be exported after #N" now reads "after its retirement": each
record retired the names it lists. The other 10 sit in `describe` / `it`
titles that already say what was retired, and were dropped. objectstack-ai#11846
answers 404; its retirement was read from the CHANGELOG entry for
landing `0c2334f`. |
| objectstack-ai#7280 | 1 | "the ADR-0069 gate posture is a declared field":
`authGate` is declared on `ExecutionContextSchema`, not spread behind an
`as any`. |
| objectstack-ai#17178 | 1 | "SEED_WRITE_EXECUTION_CONTEXT — one spelling of the seed
posture": one exported constant replaced the private copies. |
| cloud#687 | 1 | "(the founding case: a roll-up that reads 0 forever)".
The cloud repository is not attached to this session (403). The decision
was read from ADR-0078's "Surfaced by" line and the CHANGELOG paragraph
on the founding case: a bare `{ type: 'summary' }` field read 0 forever,
and the rule now flags it as an error. |
| objectstack-ai#14192 (404) | 4 | "(the silent-drop measurement, inverted)", where
the title said "the card's measurement". Dropped from 3 titles that
state the refusal. Decision read from landing `4d0d944`:
`ManifestSchema` goes strict and refuses unknown keys inside
`manifest:`. |
| objectstack-ai#10726 (404) | 2 | "removed for the `http.server` mount,
maintainer-ruled 2026-08-22", where the title said "Option B". Read from
landing `bc56e18` and PR objectstack-ai#12417's body: Option B removes
`contributes.routes` and points authors at the imperative `http.server`
mount. Dropped once. |
| objectstack-ai#4148 | 1 | "the object/field unknown-key warnings survive the
generalization", where the title said "the objectstack-ai#4148 behaviours". |
| objectstack-ai#4001 | 3 | "(the evidence base for the strict tiers)", where the
title said "objectstack-ai#4001 evidence phase". Dropped from 2 titles that state the
pinned rule. |
| objectstack-ai#4167, objectstack-ai#8687 | 3 | "top-level stack keys (named, then refused at
parse)": objectstack-ai#4167 made an undeclared top-level key say so instead of
vanishing, and objectstack-ai#8687 ruled Shape B, a strict top level. Dropped once
more for objectstack-ai#8687. |
| objectstack-ai#15624 | 2 | "cache.ttl → deleted (the unread outer block is
retired)". Dropped once. |
| objectstack-ai#14478 | 3 | Dropped. The `→ ttlMs` / `→ timeoutMs` renames and "carry
their unit" already state the rule: the unit lives in the key name. |
| objectstack-ai#15939 | 1 | "RuntimeConfig.resourceLimits.timeout → timeoutMs (its
unit was named in JSDoc only)", where the title said "ruling A". Ruling
A renames the keys whose unit was named only in JSDoc, per file. |
| objectstack-ai#5086 | 2 | "(PUT /meta refuses the inlet)": a code-only kind's create
is refused with 403 `NOT_CREATABLE` before anything persists. |
| objectstack-ai#7743 | 1 | "UNCHANGED, the field overlay refusal stays", where the
title said "objectstack-ai#7743's overlay refusal". |
| objectstack-ai#8154 | 1 | "(the consumer contract of the per-type redaction hook)".
|
| objectstack-ai#21120 | 1 | "stored metadata ROWS — the family-wide seam every exit
routes through". This says only what the card's public summary says: one
shared seam, which every surface routes through or refuses. |
| objectstack-ai#6245 | 1 | "the bound-but-unregistered fence, pinned": schemas are
bound for those kinds WITHOUT registering them. |
| objectstack-ai#11263 | 1 | "PLATFORM_PLUGIN_WIRED_RUNTIMES — runtimes wired by
plugins[], not by a token": a sibling roster was added, and no token was
minted. |
| objectstack-ai#3366 | 1 | "classifyRequiredCapability — preflight for an installable
provider in this edition". |
| objectstack-ai#17676 | 1 | "package-registry carve-out — its persistence is
always-on core, split from `marketplace`", where the title said "ruling
A′". |
| objectstack-ai#16365 | 1 | "accepts %s, which the regex refused before the SemVer
widening", where the title said "the pre-objectstack-ai#16365 regex". The `%s` values
do not change. |
| objectstack-ai#17227 | 1 | "dashboard.header.actions stays titled — the first
carrier given item-level names". |
| dropped only (live) | 45 | objectstack-ai#3308 (2), objectstack-ai#3433, objectstack-ai#3760, objectstack-ai#3786, objectstack-ai#4212,
objectstack-ai#4509, objectstack-ai#4587, objectstack-ai#4657, objectstack-ai#4741, objectstack-ai#4834, objectstack-ai#4939, objectstack-ai#5488, objectstack-ai#5961, objectstack-ai#6881, objectstack-ai#6931,
objectstack-ai#7893, objectstack-ai#8586, objectstack-ai#10039 (2), objectstack-ai#11169, objectstack-ai#12032, objectstack-ai#12428, objectstack-ai#13613, objectstack-ai#15678 (7),
objectstack-ai#16328, objectstack-ai#16334, objectstack-ai#16449, objectstack-ai#17232 (2), objectstack-ai#17445, objectstack-ai#17780, objectstack-ai#18124 (2), objectstack-ai#18791
(3), objectstack-ai#19630, objectstack-ai#20102: each title already states the pinned decision. |
| dropped only (404) | 8 | objectstack-ai#10194 (landing `2306a76`: each bound entry
is its stack collection's schema), objectstack-ai#10338 (`d2619fd`: `target` optional,
the gate holds the flow requirement), objectstack-ai#10724 (`be21955`: the nine dead
members tombstoned), objectstack-ai#11330 (`a9ee98992`: the trust-tier text tells the
truth), objectstack-ai#11332 (`dce5cd4`: the three dead containers retired), objectstack-ai#13135
(`9e0ba21`: the paper customization protocol retired), objectstack-ai#17147 (2,
`aaacf1d5c`: the granted set is registered and refuses nothing, said
truthfully). Each title already carries what landed. |

## Readers

- **Test-name filters:** none. A tracked-tree search for `-t` and
`--testNamePattern` finds only `packages/qa/dogfood/README.md:142` (`-t
"owner-scoped"`), which is unrelated.
- **Snapshots:** none. `kernel/` has no `__snapshots__`, and no `.snap`
file is tracked under `packages/spec`.
- **Titles by substring:** every old title, plus a window around each id
(256 needles), was searched across the tracked tree outside its own
file. No gate, doc or script matches one. At the head, 8 hits remain:
three sibling titles in other lanes' or stages' files
(`metadata-protocol/src/protocol.capability-write-door.test.ts:183`,
`spec/src/api/contract.test.ts:813`,
`spec/src/system/auth-config.test.ts:520`), three released
`packages/spec/CHANGELOG.md` entries, and one code comment in
`kernel/metadata-authoring-lint.ts:268`, which belongs to the comment
lane.

## Text-only proof

Stage 10's scratch tool (`textonly10.cjs`, md5
`d5e4801dbb4329ab1984da91e92fc47c`) compares base and head file by file
on three legs:
1. **Skeleton:** the full AST, with string pieces masked. It must be
identical.
2. **Comments:** every comment, byte-equal.
3. **Strings:** each string leaf that changed must sit in a test-call
title position, or on one of the 10 declared lines. Those are the expect
messages at `cli-command-contribution-retirement.test.ts:74` and `:86`,
`plugin-lifecycle-advanced-retirement.test.ts:103`, `:124` and `:141`,
`plugin-loading-retirement.test.ts:125`,
`plugin-security-scan-result-retirement.test.ts:123`,
`preview-mode-retirement.test.ts:193` and
`startup-orchestrator-retirement.test.ts:85` and `:106`. Each changed
leaf must carry a tracker id before and no `#` plus digits after.

- **Result:** 36 of 36 files SAME, 102 changed (92 title, 10 declared),
on all three legs.
- **Diff hunks:** exactly the 102 planned lines, with every file keeping
its line count.
- **Controls (10 of 10 as predicted, on scratch copies, each anchor hit
once):** identifier rename DIFF; numeric literal DIFF; comment edit
COMMENT DIFF; a non-title string with an id VIOLATION; a rewritten title
given a new id VIOLATION; a title that was id-free at base edited
VIOLATION; one title reverted to base SAME; a declared string keeping an
id VIOLATION; an undeclared expect message changed VIOLATION; a title
re-split into a `+` chain DIFF.

**Test counts:** the 36 files were run at the base (in a separate base
worktree) and at the head: 841 / 841 tests on both sides, with the same
count and status sequence per file in 36 of 36. 388 full test names
change, and each equals the base name with the planned replacements
applied.

## Changeset: `skip-changeset`

Measured, not assumed:
- `npm pack --dry-run` of `@objectstack/spec` lists 2068 files under
`files[]`. 0 of the 36 touched files are in it, and 0 `*.test.ts` at
all. The controls `src/kernel/manifest.zod.ts` and
`src/kernel/execution-context.zod.ts` are in it.
- In `dist/`, three new phrases and three old ones each read in 0 files.
The control `Plugin compatibility ranges (ADR-0025` reads in 20.

So this PR publishes nothing, and no changeset is added.

## Verification (at `e0ad8f50af`)

- `pnpm turbo run build` over all packages: 71 / 71 (at the first
commit), then `@objectstack/spec` rebuilt at `e0ad8f50af`.
- `@objectstack/spec`: `vitest run --project local`, 613 files and 18215
passed, 1 todo. `typecheck` exit 0, including `check:test-typecheck`,
whose program holds all 36 touched files.
- **Gates:** `dispatch-gates --commands` derived 79 families at
`e0ad8f50af`, and all 79 exit 0. `--ran` reconciles: 79 derived, 79 run,
0 NOT-MEASURED, 0 UNRUN.
- **ESLint, a proven narrowing:** `--no-inline-config` over the 36
files, 0 errors and 0 warnings. The population comes from ESLint's own
config: 36 configured, 0 ignored. No `parserOptions.project` or
`projectService`, so no untouched file's verdict can move.
- `check-governed-merges --test`: NOT governed, 204 changed lines.

## Acceptance notes

- **Code comments still carry ids** in these 36 files and in the
`kernel/` sources, for example `execution-context.test.ts:205`,
`metadata-authoring-lint.ts:268` and `functional-completeness.ts:148`.
They are the comment lane's, untouched here.
- **A sibling title in another package** repeats `objectstack-ai#5961 — capability`
(`packages/metadata-protocol/src/protocol.capability-write-door.test.ts:183`).
It is that package's test-string stage, not this one.
- **The second commit** (`e0ad8f50af`) rewords one title from this PR's
first commit, "the one carrier already titled", which read as a
tautology, to "the first carrier given item-level names". Every proof
above was re-run at that head.
- **`origin/main` moved** three commits past the base before this PR
opened (objectstack-ai#21717, objectstack-ai#21715, objectstack-ai#21660). None touches `packages/spec`, so
nothing was merged.

---
_Generated by [Claude
Code](https://claude.ai/code/session_01T9u38rswFp5Rw8DswRUReJ)_

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation protocol:ui size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants