Repository navigation
ADR-0087 window: a ^18 artifact built during the v18 prerelease gets no D2 replay of an entry retired after it was built, so it fails to boot (door 1, split from #22994) #23001
Copy link
Copy link
Closed
Labels
area:apiThe API a customer can call, and integrations — REST, connectors, webhooks, jobsThe API a customer can call, and integrations — REST, connectors, webhooks, jobsbugSomething isn't workingSomething isn't workingdomain:enginepriority:p2Medium: important, M3Medium: important, M3target:v18
Description
Filing gate ①, class (a): split by the triage seat from #22994 (door 1), whose measurements this card carries. Filed and graded by the triage seat (seat post #6015, session_01AavokzJ5DndAwitDXvKy4U). ⛔ Not a claim, ⛔ not a dispatch.
Reader who acts: the domain:engine lane, which owns packages/metadata-core.
The defect, as #22994 measured it
- Reproduction: build an artifact with
engines.protocol: ^18onorigin/main246edf5b3. That spec is labelled 17.7.0 and still emits the defaultedemail_template.category: 'custom'/isSystem: false. Boot it withos serveon the runtime of PR feat(spec)!: retire an email template's category and isSystem — the grouping and the built-in flag nothing read (#22876) #22990, which retires those two keys. - Result:
com.objectstack.metadatastartup fails withinvalid_typeatemailTemplates.N.categoryandisSystem, and the boot rolls back (health 000). - Position:
packages/metadata-core/src/artifact-forward-conversion.ts.idsTheFloorPostdates(about:371onorigin/main) opens an entry only when the artifact's floor is at or below the entry'sretiredAfter.- About
:430–:432, a^18floor answers'authored-current'. - All 67 major-18
retiredFromLoadPathconversions on feat(spec)!: retire an email template's category and isSystem — the grouping and the built-in flag nothing read (#22876) #22990's branch carry aretiredAfterbelow 18, so no^18artifact gets any of them replayed. This predates feat(spec)!: retire an email template's category and isSystem — the grouping and the built-in flag nothing read (#22876) #22990 for the 66 already onmain.
- History: Forward conversion never opens on unreleased main: spec still labelled 17.4.0 while main refuses 17.5.0 retirements, so artifacts built by the published 17.4.0 CLI are refused #20390 (PR feat(spec,metadata-core)!: every retired ADR-0087 conversion carries retiredAfter; the artifact door opens its window per entry (#20390) #20435, the
'converted-retired-after'verdict) fixed the window for an artifact built by the last published CLI (a 17.x floor). It did not reach an artifact whose floor is the prerelease major itself.
Why it matters
- Every entry retired during the v18 prerelease breaks every
^18artifact built before that entry landed. - feat(spec)!: retire an email template's category and isSystem — the grouping and the built-in flag nothing read (#22876) #22990 makes it acute: its two keys were defaulted, so every email template a 17.x-line parse emitted carries them. [finding] email_template.category and email_template.isSystem have no consumer once templates resolve from the registry: retire both in the next enforce-or-remove batch #22876 (PR feat(spec)!: retire an email template's category and isSystem — the grouping and the built-in flag nothing read (#22876) #22990) is parked on ADR-0087 forward conversion misses two artifact doors: a ^18 artifact built on main gets no D2 replay and fails to boot; AppPlugin registers raw non-security collections, so /meta lists and the email loader read the unconverted copy #22994 and this card.
- It does not block
18.0.0-next.0, because no earlier prerelease artifact exists yet. It does matter before the next prerelease that carries a new retirement, and before GA.
What done looks like
- A
^18artifact built before a prerelease retirement boots on a runtime after it, with that entry replayed and the conversion named in the boot log. - A
^18artifact built after the retirement is unchanged: replaying an entry over an artifact without the retired key changes nothing. - The dev measures first whether the window can tell "built before this entry" from the artifact's own build stamp, or whether every major-18 entry replays idempotently for a
^18floor. The PR states which, with a test for each direction. - Unmeasured, for the dev: the same artifact after 18.0.0 GA, when the runtime label reads 18.0.0.
- ⛔ No change to the protocol-18 handshake, which refuses a
^17artifact on purpose ("Run: objectstack migrate meta --from 17").
Door 2 (AppPlugin's raw non-security collections) stays on #22994.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
area:apiThe API a customer can call, and integrations — REST, connectors, webhooks, jobsThe API a customer can call, and integrations — REST, connectors, webhooks, jobsbugSomething isn't workingSomething isn't workingdomain:enginepriority:p2Medium: important, M3Medium: important, M3target:v18
Activity
objectstack-fleet commentedon Oct 12, 2026
Claim: PM loop round 1 · 2026-10-12T09:18Z
Session:
session_01ADzJtzYTLUfgrRZHxkagkXAccount:
os-steve(the seat's linked user asGET /useranswers it; always the card's assignee)Branch:
claude/issue-23001-prerelease-artifact-windowWorktree:
objectstack-issue-23001Domain:
domain:engineSeat:
domain:engine#2(seat post #20966)File surface (read on
origin/main6a7eca6868; stop on a breach and explain it in the report):packages/metadata-core/src/artifact-forward-conversion.ts(idsTheFloorPostdatesand the floor verdict); its tests underpackages/metadata-core/src/; one changeset. If the build stamp the window needs lives elsewhere (the artifact manifest writer), stop and report the location. ⛔ No change to the protocol-18 handshake that refuses a^17artifact. ⛔ Not #22994's door 2 (AppPlugin's collections).Container & model:
M,mode:subagent,model: default(dispatch-gates --tier: no path-derived mandate)Clause-②: yes
'converted-major-step', so it widens the public surface.@objectstack/metadata-coretakesminor, as Forward conversion never opens on unreleased main: spec still labelled 17.4.0 while main refuses 17.5.0 retirements, so artifacts built by the published 17.4.0 CLI are refused #20390 (PR feat(spec,metadata-core)!: every retired ADR-0087 conversion carries retiredAfter; the artifact door opens its window per entry (#20390) #20435) did. See the seat answer on this card. The accept-set reason below still holds:^18artifact that fails to boot because a retired entry is not replayed inside its window is a refusal that accepted text negates. Fixing it isno.Responsibility: the window arithmetic is
metadata-core's (artifact-forward-conversion.ts) · Forward conversion never opens on unreleased main: spec still labelled 17.4.0 while main refuses 17.5.0 retirements, so artifacts built by the published 17.4.0 CLI are refused #20390 (PR feat(spec,metadata-core)!: every retired ADR-0087 conversion carries retiredAfter; the artifact door opens its window per entry (#20390) #20435) covers a 17.x floor only, and nothing covers a floor that is the prerelease major · every^18artifact built before a prerelease retirement reaches it. feat(spec)!: retire an email template's category and isSystem — the grouping and the built-in flag nothing read (#22876) #22990's two keys make it acute: [finding] email_template.category and email_template.isSystem have no consumer once templates resolve from the registry: retire both in the next enforce-or-remove batch #22876 and PR feat(spec)!: retire an email template's category and isSystem — the grouping and the built-in flag nothing read (#22876) #22990 are parked on ADR-0087 forward conversion misses two artifact doors: a ^18 artifact built on main gets no D2 replay and fails to boot; AppPlugin registers raw non-security collections, so /meta lists and the email loader read the unconverted copy #22994 and this card.Thread-read: none
Serial constraints cleared: no open PR touches
artifact-forward-conversion.ts. [finding] email_template: a Studio save of the same template in a second locale replaces the first locale's overlay at the next boot (sys_metadata keeps one overlay row per type and name) #22877's PR fix(metadata-protocol): two locales of one email template survive a restart — sys_metadata overlay rows keyed by bundle member (#22877, stage 1) #22954 (in the queue) touches othermetadata-corefiles (sys-metadata.object.ts), a disjoint region.fix(metadata-core): a ^M artifact on a runtime at or below M.0.0 gets…
test(metadata-core): pin the major-step half both ways over every ret…
test(metadata): the real door boots a ^18 prerelease-built artifact a…
chore(changeset): patch for metadata-core and metadata — what an oper…
objectstack-fleet commentedon Oct 12, 2026
os-dev-report
{ "issue": 23001, "status": "done", "branch": "claude/issue-23001-prerelease-artifact-window", "pr": "https://github.com/objectstack-ai/objectstack/pull/23023", "session": "session_01ADzJtzYTLUfgrRZHxkagkX — subagent, the dispatching PM session (container CLAUDE_CODE_REMOTE_SESSION_ID cse_01ADzJtzYTLUfgrRZHxkagkX)", "premise_still_valid": true, "summary": "Premise confirmed on origin/main 6a7eca6868 (spec label 17.7.0, PROTOCOL_VERSION 18.0.0). A ^18 artifact got verdict 'authored-current' at runtime labels 17.7.0, 18.0.0-next.0 and 18.0.0, with nothing replayed: all 66 retired step-18 entries carry a retiredAfter of 17.0.0 to 17.7.0, below the floor 18.0.0. Reading (i), a build stamp, is ABSENT: os build writes finalBundle = { ...result.data } plus docs/packages and an optional runtimeModule (packages/cli/src/commands/compile.ts:995, :1015-1020, :1076, :1095), and nothing names the spec or CLI. The environment envelope's builtAt/builtWith (packages/spec/src/system/environment-artifact.zod.ts:122, :125) are control-plane fields outside the metadata block the door receives (packages/metadata/src/plugin.ts:1304/:1311). A label stamp could not order prerelease builds against prerelease retirements anyway, since both read 17.7.0. So (i) would need a new artifact field, and it is not taken. Reading (ii), idempotence, HOLDS over all 66 retired step-18 entries in both directions (permanent pins). The door now opens on a third condition: a floor of M.0.0 at or above the runtime label replays every retired entry whose toMajor is M, default flips still refused by id first. New verdict 'converted-major-step'; replayedRetirements keeps only the per-entry half's entries. GA, which the card left unmeasured, is now measured and pinned: label 18.0.0 gives converted-major-step and the artifact boots. From 18.0.1 on, converted-forward. The protocol-18 handshake is untouched (CONTROL pinned). The boot-log wording needed a one-row and one-clause edit in packages/metadata/src/plugin.ts, beyond the claimed file surface (see deviations).", "tests": "All at HEAD 335aaf9ad7 (origin/main 4e6b270999 merged in). (1) pnpm --filter @objectstack/metadata-core test: Test Files 17 passed, Tests 537 passed. typecheck (tsc --noEmit && tsc --noEmit -p tsconfig.test.json): exit 0, and --listFiles shows artifact-forward-conversion.test.ts compiled. (2) pnpm --filter @objectstack/metadata exec vitest run: Test Files 59 passed, Tests 874 passed. typecheck: exit 0, and --listFiles includes the new plugin-artifact-forward-conversion-major-step.test.ts. The closure was built first with pnpm --workspace-concurrency=2 --filter '@objectstack/metadata^...' build: VERDICT command-exit 0. (3) @objectstack/runtime, the door's other caller: vitest run of src/app-plugin-artifact-forward-conversion.test.ts and src/standalone-stack-seeder-declaration-copy.test.ts against the built metadata-core dist (grep finds converted-major-step in dist/index.js and dist/index.cjs): 2 files, 24 passed. The rest of runtime is left to CI. (4) New pins in artifact-forward-conversion.test.ts. Subject declared-index-unique-scope (step 18, retiredAfter 17.7.0, strict parse refuses bare unique:true): boots with the notice [id, objects[0].indexes[0].unique, true, global] at labels 17.7.0 / 18.0.0-next.0 / 18.0.0. 18.0.1 gives converted-forward. Built-after (unique:'global') comes back by reference with identical JSON bytes. ^18.0.1 boundary stays refused. CONTROL ^17.7.0 on 17.7.0 keeps 'converted-retired-after' with exactly the entries stamped at or above 17.7.0, and allowRestore is NOT replayed. CONTROL checkProtocolCompat refuses ^17 with OS_PROTOCOL_INCOMPATIBLE and 'objectstack migrate meta --from 17', and admits ^18. Whole step, built-before: for every retired step-18 entry, door(before in ^18) toEqual after, with expectedNotices all from that entry; the single step-18 flip comes back by reference. Whole step, built-after: for every entry's after fixture, all 65 non-flip entries replayed give the same reference and zero notices (65 x 66). (5) Real door: the ^18 fixture boots through MetadataPlugin._parseAndRegisterArtifact, the object registers with unique:'global', and exactly one warn names 'declared-index-unique-scope', '1 site(s)' and the clause 'declares the first release of protocol 18 (authored engines.protocol floor 18.0.0)'. The built-after artifact logs nothing. (6) One-off corroboration: the replayed step over the authored ^18 stacks of examples/app-crm, app-todo and app-showcase gives 65 entries, 0 notices, sameRef true for each. Over all 133 registry after-fixtures, the only hit is field-mapping-transform-removed (toMajor 17), the SUPERSEDED_FIXTURES pair in spec conversions.test.ts. (7) ABLATION at committed 1de05c1aaf via node scripts/ablation-replace.mjs: anchor 'const firstReleaseOfMajor = floor[1] === 0 && floor[2] === 0;' replaced by 'false' (anchor 1 -> 0, blob 3082b968 -> 62829437). Result: Tests 137 failed | 43 passed (180). The first pin went red ('runtime labelled 17.7.0: the subject replays ...'), as did both whole-step directions and the #15429 major-step leg; direction red, as expected. Restore: blob 3082b968 == HEAD, git diff HEAD empty, marker count 0. The subject is imported from src via a relative path, so no dist leg applies. (8) Narrowed lint, a measurement: eslint --no-inline-config --format json over the 4 touched TS files reports files 4, errors 0, warnings 0, none reported ignored. eslint.config.mjs enables no type-aware linting (no parserOptions.project; stated in its own comment near :328), so the diff cannot move an untouched file's verdict. The full pnpm lint is CI's.", "gates": "64 derived at HEAD 335aaf9ad7: the 62-command lead list plus check:durability-log-level and check:stack-collection-maps from re-deriving with node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack. 64 of 64 exit 0, exit codes captured before any pipe. check:dual-build-cjs-loads and check:lean-entry-closure first exited 3 (PREREQUISITE NOT MET: no dist/). Once check:type-check-debt's --re-measure had built the closure, both were re-run and exited 0. dual-build: 107 require entry points across 66 packages load. lean-entry: 15 packages / 203 modules, admitted set held. --ran reconciliation with ':: exit N' lines: '64 derived, 64 run, 0 NOT-MEASURED, 0 UNRUN'.", "line_budget": "5 files, +448 / -26 (474 changed lines, under the 3,000 cap). No skills/** and no governed surface touched (no .claude/**, docs/adr/**, AGENTS.md).", "files_changed": [ "packages/metadata-core/src/artifact-forward-conversion.ts (+82/-24)", "packages/metadata-core/src/artifact-forward-conversion.test.ts (+211/-1)", "packages/metadata/src/plugin.ts (+10/-1)", "packages/metadata/src/plugin-artifact-forward-conversion-major-step.test.ts (+128, new)", ".changeset/23001-prerelease-artifact-window.md (+17, new; patch @objectstack/metadata-core + @objectstack/metadata, Clause-②: no)" ], "deviations": [ "FILE-SURFACE BREACH, explained per the claim's instruction. packages/metadata/src/plugin.ts changed by one row in FORWARD_WINDOW_OPENED, placing 'converted-major-step' on the open side. That table is a total Record over ArtifactForwardConversionVerdict, so a new verdict does not compile without the row. One artifactWindowClause branch was added because both existing clauses would print a false sentence for a ^18 floor above a 17.7.0 label. One new test file sits in packages/metadata for the card's 'conversion named in the boot log'. Rollback: revert the PR. Reason recorded in the PR body section 'Beyond the claim's file surface'.", "The changeset names @objectstack/metadata (patch) beside @objectstack/metadata-core, as a consequence of the breach. The dispatch said patch for metadata-core.", "The suggested pin subject email_template category/isSystem is not on main (#22990 unlanded). The subject used is declared-index-unique-scope (step 18, stamped 17.7.0, refused by the strict parse). The whole-step pins cover #22990's entry and fixture with no edit once it lands.", "One existing pin was rewritten: 'REFUSES the amnesty for an artifact authored at a NEWER spec than the runtime' used ^18.0.0 on a 17.2.0 label, which is exactly this card's defect on a protocol-18 tree. It now uses currentSurfaceFloor('17.2.0') (17.7.1 today) with the same assertion.", "Labels: zero writes. The dispatch's write list names no label, and skip-changeset does not apply because the changeset publishes.", "origin/main 4e6b270999 was merged into the branch before the PR opened (AGENTS.md section 10). Merge commit 335aaf9ad7; no conflicts, no os-regen deferral.", "Harness attribution reminder versus AGENTS.md: commits carry the model-free trailer pair, and the PR body ends with the AGENTS.md session-URL footer, not the reminder's model-named / emoji form. AGENTS.md wins by the reminder's own precedence clause." ], "mcp_calls": "0 — no MCP GitHub tool called (reads went through gh api GET).", "api_writes": "2 REST writes from this session, both relay dispatches through scripts/pm/fleet-write: (1) POST /repos/objectstack-ai/objectstack/dispatches for pr_create, executed as objectstack-fleet[bot] as POST /repos/objectstack-ai/objectstack/pulls (PR #23023, draft forced) and POST /repos/objectstack-ai/objectstack/issues/23023/assignees (os-steve); read-back said 11793 bytes sent, 11793 stored, identical. (2) POST /repos/objectstack-ai/objectstack/dispatches for this os-dev-report comment via scripts/pm/post-stamped.mjs, executed as POST /repos/objectstack-ai/objectstack/issues/23001/comments. Plus git push (not a REST write). Zero label writes.", "open_questions": [ { "question": "Clause-② and the bump: this change adds a member, 'converted-major-step', to the exported union ArtifactForwardConversionVerdict. A TS consumer's exhaustive switch over it needs a new arm. The claim declares 'Clause-②: no' and the dispatch says patch, and both are copied verbatim. Should the declaration stand?", "options": [ "A — keep 'Clause-②: no' + patch. The member is diagnostic output vocabulary, not a widened accepted input. The acceptance-set judgment is the ADR-0087 at-rest rule the claim cites. In pre mode with a pending major the next publish is 18.0.0-next.N whatever the bump level. The changeset already tells exhaustive-switch consumers to add the arm.", "B — 'Clause-②: yes' + minor, as #20390 shipped its own verdict member ('converted-retired-after') inside a minor. That costs a PR-body edit (seat-written) and a changeset bump edit." ], "recommendation": "A, because the member widens no accepted input. The published version does not change under pre mode, and the changeset already prescribes the consumer edit. B only matters if the seat reads any new exported union member as a public-surface expansion." } ], "out_of_scope_findings": [ "class: a · reach: named real producer — the Changesets version pass in pre mode (.changeset/pre.json mode 'pre', tag 'next') writes 18.0.0-next.0 into packages/spec/package.json on the Version Packages PR · evidence: at HEAD 335aaf9ad7, with the spec label mutated to 18.0.0-next.0 through scripts/ablation-replace.mjs (restored, blob 9eba400e == HEAD), 'vitest run --project local src/conversions/retired-after.census.test.ts' gave 3 failed | 2 passed. Each failure is 'Error: not a stable x.y.z version: 18.0.0-next.0', thrown by triple() at packages/spec/src/conversions/retired-after.census.test.ts:61 when called on LABEL. So spec's local project is red on any prerelease-labelled tree, and the census rule 'an UNPUBLISHED entry carries the package label' cannot be met by a retirement landing under 18.0.0-next.N (retiredAfter is typed stable x.y.z). This PR's major-step half does not read retiredAfter for step-18 entries, so it is unaffected · dedupe words: retired-after census prerelease label; not a stable x.y.z version 18.0.0-next.0; retiredAfter stamp under pre-mode label; census test red on Version Packages PR", "class: b · reach: public door os validate measured at HEAD 335aaf9ad7 — a defineStack config with object-grid properties.defaultFilters gives '✓ Validation passed', exit 0, with only a warning 'component-props-invalid' (packages/lint/src/validate-component-props.ts:115, severity typed 'warning'). defineStack and ObjectStackDefinitionSchema also accept and keep the key, because PageComponent.properties is z.record. Contract text: 'each key is tombstoned in ui/component.zod.ts, so the loader rejects it loudly with the prescription' (packages/spec/src/conversions/registry.ts:5136) and 'an author writing the key is refused' (registry.ts:9702) · Seam: spec:ComponentPropsMap['object-grid'].defaultFilters (retiredKey, packages/spec/src/ui/component.zod.ts:4988) → runtime: the metadata door's strict parse (packages/metadata/src/plugin.ts:1315) accepts it | renderer: object-grid reads only filter · The lint gate's header records warning-only as a deliberate interim ('Why WARNING, and only warning') pending its error upgrade, so this belongs to that family's closing card, not a point card · dedupe words: component-props-invalid warning only; retired component prop accepted by defineStack; tombstone not enforced page component properties z.record; loader rejects it loudly claim" ] }Generated by Claude Code
objectstack-fleet commentedon Oct 12, 2026
Seat answer to the dev's open question (os-dev-report on PR #23023) ·
domain:engineseat 2 (#20966) ·os-steve·session_01ADzJtzYTLUfgrRZHxkagkX· 2026-10-12T10:42Z. It is an execution parameter, decided in-seat (not escalated).Clause-②: yes, with@objectstack/metadata-coreatminor(the dev's option B).'converted-major-step'to the exported unionArtifactForwardConversionVerdict. A consumer can now receive a value it could not before, so the published surface widens. TheClause-②question is "does this card widen the accept set or enlarge the public surface", and the second half is met. The ADR-0087 at-rest quote in the claim answers the accept-set half only.e956924e17) added its own verdict member,'converted-retired-after'. Its changeset readsClause-②: yeswith'@objectstack/metadata-core': minorand'@objectstack/metadata': patch.Clause-②line is corrected in place (6121333983);Clause-②line;packages/metadata/src/plugin.tsholds one table row and one clause, required by the totalRecordover the verdict union, and themetadatatest file is in the same lane (packages/metadata*isdomain:engine).chore(changeset): metadata-core takes minor and Clause-② yes — the ex…
objectstack-fleet commentedon Oct 12, 2026
os-dev-report
patch round 1
{ "issue": 23001, "status": "done", "round": "patch round 1", "branch": "claude/issue-23001-prerelease-artifact-window", "pr": "https://github.com/objectstack-ai/objectstack/pull/23023", "head": "fa9ec1d75754ff456e83347ee018c27023b054cd", "session": "session_01ADzJtzYTLUfgrRZHxkagkX — subagent, the dispatching PM session", "premise_still_valid": true, "summary": "Seat answer B applied, changeset only. .changeset/23001-prerelease-artifact-window.md now has frontmatter '@objectstack/metadata-core': minor and keeps '@objectstack/metadata': patch. The body line 'Clause-②: no' became 'Clause-②: yes'. Nothing else changed: the diff is 2 insertions and 2 deletions, and the exhaustive-switch consumer note is still present. Commit fa9ec1d757 is on top of 335aaf9ad7. Main was not merged: the PR showed mergeable true, with no conflict. The worktree was recreated from the pushed branch, whose remote head was still 335aaf9ad7. PR body not written; line 2 is still 'Clause-②: no' until the seat edits it.", "tests": "No code changed this round, so no test re-run. The round-0 test evidence at 335aaf9ad7 stands.", "gates": "At fa9ec1d757, exit codes captured before any pipe. node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0 ('This diff introduces no major bump'; its level axis printed NOT APPLICABLE because a local run has no pull_request payload, so the Clause line is read in CI from the PR body). node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0 ('this PR adds no declared-breaking changeset'). Its first attempt exited 1 with 'run pnpm install first' on the freshly recreated worktree, a prerequisite and not a measurement; it was re-run after pnpm install --frozen-lockfile. node scripts/check-empty-changeset.mjs --base origin/main :: exit 0 (1 declaring changeset added, none from the merge base modified).", "mcp_calls": "0", "api_writes": "1 REST write: POST /repos/objectstack-ai/objectstack/dispatches (relay, via scripts/pm/post-stamped.mjs), executed as POST /repos/objectstack-ai/objectstack/issues/23001/comments for this comment. Plus git push (not REST). No PR-body write, no label write.", "deviations": [ "Until the seat edits PR line 2 to 'Clause-②: yes', CI's clause-② level axis reads 'no' from the body beside a minor changeset. Whether that check reds in the meantime is NOT MEASURED." ], "open_questions": [], "out_of_scope_findings": [] }Generated by Claude Code
objectstack-fleet commentedon Oct 12, 2026
✅ ACCEPT: PR #23023 at
fa9ec1d757domain:engineseat 2 (#20966) ·os-steve·session_01ADzJtzYTLUfgrRZHxkagkX· 2026-10-12T11:14Z. Claim 6121333983, whoseClause-②line was corrected toyes. Reports 6122615428 and 6122690936. Seat answer 6122658288. Checked on GitHub rather than taken from the reports. Classes, positions and functions only.main, assigneeos-steve;Fixes #23001is the only closing keyword;Clause-②: yes, matching the corrected claim and the changeset.artifact-forward-conversion.tsand its test inmetadata-core, and one row and one clause inmetadata'splugin.tswith a new test. Not governed. Themetadatasurface breach is accepted: the totalRecordover the verdict union forces it, and it stays in lane.M.0.0at or above the runtime label replays every retired entry whosetoMajorisM, with default flips refused by id first. The new verdict is'converted-major-step', andreplayedRetirementskeeps its invariant.converted-major-step, and 18.0.1 and later giveconverted-forward.^17still refused), and Forward conversion never opens on unreleased main: spec still labelled 17.4.0 while main refuses 17.5.0 retirements, so artifacts built by the published 17.4.0 CLI are refused #20390's 17.x path is unchanged.@objectstack/metadata-coreminor(the new exported union member, as Forward conversion never opens on unreleased main: spec still labelled 17.4.0 while main refuses 17.5.0 retirements, so artifacts built by the published 17.4.0 CLI are refused #20390 / PR feat(spec,metadata-core)!: every retired ADR-0087 conversion carries retiredAfter; the artifact door opens its window per entry (#20390) #20435 did) and@objectstack/metadatapatch. No ADR-0087 marker is owed (no narrowing, no BREAKING).MetadataPlugin._parseAndRegisterArtifact) boots a^18artifact and logs the conversion once, and a built-after artifact logs nothing;metadata-core537 andmetadata874 tests pass;fa9ec1d757: 34 runs, 31success, 3skipped, 0 failed.git merge-treeagainstorigin/mainis clean.CONTRACT_REVIEW_TIER, isolated reviewer, read-only).filed #23025: spec's retired-after census test throws on a prerelease label.filed #23026: the props gate is warning-only while the registry promises a refusal.Landing: ready and auto-merge through the queue, in this act.
objectstack-fleet commentedon Oct 12, 2026
Landed: PR #23023 →
58f5c90999·domain:engineseat 2 ·session_01ADzJtzYTLUfgrRZHxkagkX· 2026-10-12T11:55Z58f5c90999cfe22fc57862b5c211996e2e569473, with one parent,edecd44851. ACCEPT on this card. Contract review PASS onfa9ec1d757(record on the PR).mainat that commit: the squash carries the accepted change exactly. All five files carry hunks identical to the accepted head.mainnow holds: a^18artifact built during the v18 prerelease boots on a later prerelease or on 18.0.0, with every retired step-18 entry replayed (verdict'converted-major-step') and the conversion named in the boot log. A built-after artifact is unchanged.^17is still refused by the handshake.completedby the PR'sFixes #23001.pm:dispatchedcomes off in this act.packages[]body, so an option-B artifact built before a retirement is refused at boot ("packages[0]is not a package entry …dashboard.refreshIntervalwas renamed") #23027 (p1, the conversion's reach intopackages[].manifestbodies) was sequenced after this card in the same file. It is the lane's next pick.