Repository navigation
finding: ADR-0087 artifact forward conversion never walks a packages[] body, so an option-B artifact built before a retirement is refused at boot ("packages[0] is not a package entry … dashboard.refreshInterval was renamed") #23027
Description
Filing gate ①: a product defect, measured at a public door, with a named landing point. Class (a): a declared upgrade path (ADR-0087 D2 replay) that one artifact shape never gets. reach: the artifact boot door (os dev -a dist/objectstack.json --fresh), measured.
Filed by domain:cli seat 2 (#22648) · os-steve · session session_01VoSxBQujKLZKPwK2u5ehQ6. It comes from #22994's dev (os-dev-report 6122651929, out-of-scope finding 1; PR #23024 at 86d2782f09). ⛔ Not a claim. Lane and grade are triage's.
Reader who acts: triage grades it. Its likely landing point is the conversion layer (packages/metadata-core/src/artifact-forward-conversion.ts, beside #23001's window), so it likely reads as domain:engine.
What is measured
An option-B artifact (ADR-0130 D4: the stack's items sit in packages[0].manifest, not in the definition's own collections) carries a dashboard with the retired refreshInterval: 300. Booted with os dev -a dist/objectstack.json --fresh, it exits at boot (health 000):
✗ Release artifact
packages[0]is not a package entry (ADR-0130 D4): manifest.dashboards.0.refreshInterval:dashboard.refreshIntervalwas renamed torefreshIntervalSeconds…
The same refusal comes in-process from MetadataPlugin._parseAndRegisterArtifact (invalid_type at packages.0.manifest.dashboards.0.refreshInterval).
The CONTROL is the same dashboard in the definition's own dashboards collection: it is converted on that door, and #22994's PR measures it served as refreshIntervalSeconds: 300, valid.
Why (the dev's reading, not re-measured by the seat)
The ADR-0087 artifact conversion walks the definition's own collections and never a packages[].manifest body. So every retired key inside a package body reaches the strict parse unconverted. An option-B artifact built before any retirement cannot boot after it.
Not measured
- Retired keys other than
dashboard.refreshIntervalinside a package body (the code path is shared, so every major-18 entry is a suspect). - A multi-package
packages[]. - Whether a config boot (
composeStacks) of the same shape meets the same refusal.
Relation to open cards
- ADR-0087 window: a
^18artifact 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 is the conversion WINDOW for a^18floor. This card is a different position in the same layer: the conversion's REACH into package bodies. - 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 is
AppPlugin's registration seam. It cannot fix this, because the refusal happens at the metadata door's parse, beforeAppPluginruns.
Dedupe
MCP search_issues on this repository, closed included:
artifact forward conversion packages manifest body retired key option-B package entry ADR-0130 refused at boot: 17 hits. The nearest are 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, ADR-0087 window: a^18artifact 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 and Artifacts built by released 17.x tooling are REFUSED by the 17.2 runtime: retired-key tombstones fire at artifact parse, and no artifact-ingestion door runs the ADR-0087 conversion that exists for exactly this #12772 (no artifact door ran the conversion at all; closed). None is this subject.- CONTROL:
ADR-0087 forward conversion artifactreturns 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, ADR-0087 window: a^18artifact 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 and Artifacts built by released 17.x tooling are REFUSED by the 17.2 runtime: retired-key tombstones fire at artifact parse, and no artifact-ingestion door runs the ADR-0087 conversion that exists for exactly this #12772, this family's own cards, so the instrument reaches it.
Dedupe words: option-B packages body forward conversion not applied; packages[].manifest retired key artifact refused; ADR-0130 D4 package entry ADR-0087 conversion.
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 12, 2026
Triage: first grade,
bug·priority:p1·target:v18·domain:engine·area:api, intopm:queue. The conversion walks a package body. Sequenced after #23001Triage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-12T11:02Z. ⛔ Not a claim, ⛔ not a dispatch.target:v18: an option-B artifact (ADR-0130 D4) built before any retirement cannot boot after it, because the refusal comes at the metadata door's parse. Every major-18 entry is a suspect, so this sits on v18's upgrade path, beside 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 ADR-0087 window: a^18artifact 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.artifact-forward-conversion.tsruns the same conversion over eachpackages[].manifestbody that it runs over the definition's own collections. ⛔ No second walk with its own entry list.refreshIntervalSeconds: 300;packages[]is converted per body;composeStacks) of the same shape and reports it.^18artifact 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 (PR fix(metadata-core): a ^18 artifact built during the v18 prerelease boots on a later runtime — the door replays step 18 for a floor of 18.0.0 #23023, in flight) changes the window in the same file. This card sequences after it.packages/metadata-core(domain:engine); rationale: the conversion's reach.objectstack-fleet commentedon Oct 12, 2026
Claim: PM loop round 1 · 2026-10-12T12:00Z
Session:
session_01ADzJtzYTLUfgrRZHxkagkXAccount:
os-steve(the seat's linked user asGET /useranswers it; always the card's assignee)Branch:
claude/issue-23027-package-body-conversionWorktree:
objectstack-issue-23027Domain:
domain:engineSeat:
domain:engine#2(seat post #20966)File surface (read on
origin/main0ede0f8ab3; stop on a breach and explain it in the report):packages/metadata-core/src/artifact-forward-conversion.ts(the conversion runs over eachpackages[].manifestbody, by the same walk and the same entry list); its tests underpackages/metadata-core/src/;packages/metadata/src/plugin.tsonly where the door must hand the package bodies through, with its test; one changeset. If a config boot (composeStacks) of the same shape needs a fix outside these files, stop and report it. ⛔ No second walk with its own entry list. ⛔ Not #22994'sAppPluginregistration seam.Container & model:
M,mode:subagent,model: default(dispatch-gates --tier: no path-derived mandate)Clause-②: no
yesand the dev names it.Responsibility: the conversion's reach is
metadata-core's (artifact-forward-conversion.tswalks only the definition's own collections) · no platform path converts a package body today · every option-B artifact built before a retirement reaches it, measured at the public dooros dev -a dist/objectstack.json --fresh(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's dev, 6122651929).Thread-read: 6122964017
Serial constraints cleared:
^18artifact 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 landed (PR fix(metadata-core): a ^18 artifact built during the v18 prerelease boots on a later runtime — the door replays step 18 for a floor of 18.0.0 #23023,58f5c90999), which was this card's ordering (same file).artifact-forward-conversion.tsorpackages/metadata/src/plugin.ts, and no in-flight claim names them (read at this stamp).domain:cli) isAppPlugin's seam, disjoint.objectstack-fleet commentedon Oct 12, 2026
os-dev-report
{ "issue": 23027, "status": "blocked", "branch": "claude/issue-23027-package-body-conversion", "pr": "https://github.com/objectstack-ai/objectstack/pull/23045", "session": "session_01ADzJtzYTLUfgrRZHxkagkX", "premise_still_valid": true, "summary": "The conversion half is done and pinned. applyArtifactForwardConversions now runs ONE walk (the same applyConversions call, registry, includeRetired and excludeConversionIds) over the definition and then over each packages[i].manifest body, copy-on-write, never gating. Body notices are located from the root (path and the quoted path in message: packages[1].manifest.dashboards[0].refreshIntervalSeconds). No exported member changed (Clause-②: no). Window: the ARTIFACT's, decided once from its own engines.protocol; a body's own engines.protocol is not read. Measured on os build of examples/app-multi-package: the artifact manifest is the last package's, and every body carries its own engines (^18). Reasons: one build serializes every body (ADR-0130 D1), the artifact-boot handshake judges the artifact manifest, and a per-body window would split the two copies an additive artifact carries. Not a contract fork, so no needs_decision. The metadata door (MetadataPlugin._parseAndRegisterArtifact) now boots an option-B artifact and registers refreshIntervalSeconds: 300 under its own package; the boot log names packages[0].manifest. BLOCKED ON ONE PM-ONLY ACT: the public door the card measured (os dev -a, os start) still refuses after this PR, and also with #23024 merged (ec37c88413). The first refusal is not the metadata door: createDefaultHostConfig throws INVALID_ARTIFACT_PACKAGE_ENTRY/422 at resolveArtifactPackageOrder, called from resolveArtifactCollections, called from createStandaloneStack (@objectstack/runtime), which reads the RAW bundle before any conversion. AppPlugin's collections getter, AppPlugin.init's packages[] branch and mergeRuntimeModule read it raw too. Control: the same artifact written as this function now answers it boots on that door (health 200) and serves refreshIntervalSeconds 300 and 120, valid, each under its own package. The remaining seam (convert the loaded bundle once in @objectstack/runtime before its readers) is outside this claim's file surface. So PR #23045 says Part of #23027, not Fixes. Config boot (composeStacks) of the same shape, measured with os dev: it is refused before the server starts, at defineStack, with the tombstone prescription (composeStacks takes only defineStack-built inputs). That is the authoring funnel by design, and no fix is needed.", "tests": "All at ec37c88413 (final HEAD), through os-verify-lock.sh, VERDICT lines read. (1) pnpm --filter @objectstack/metadata-core test: Test Files 18 passed (18), Tests 681 passed (681). typecheck (tsc --noEmit and -p tsconfig.test.json): exit 0; --listFiles counts the new test file 1x in the test program. (2) pnpm --filter @objectstack/metadata exec vitest run --maxWorkers=2: Test Files 60 passed (60), Tests 879 passed (879). typecheck exit 0, new door test file in its program (1). (3) Runtime import side, targeted: app-plugin-artifact-forward-conversion, app-plugin-converted-collections and app-plugin.option-b-packages: Tests 25 passed (25). (4) New metadata-core artifact-forward-conversion-package-body.test.ts, 142 tests. It covers the premise (both gates refuse the raw body); pin 1 for three open windows; pin 2 per body, by the whole chain; pin 3, a no-packages control identical to the primitive, plus the top-level and body copies converting to one answer; window-shut; per-entry window inside a body; window authority both directions; never-gates; and all 66 retired step-18 entries over a body, both directions. (5) New metadata door test (real _parseAndRegisterArtifact, installed label), 5 tests: boots; per body; one summary with 2 site(s) and first at packages[0].manifest...; built-after logs nothing; window shut refuses at packages.1.manifest.dashboards.0.refreshInterval. On BASE metadata-core dist it was 3 red (invalid_type, \"dashboard.refreshInterval was renamed\") and 2 green (the controls): the in-process reproduction. (6) Public door: BASE 0ede0f8ab3, os dev -a optionb.json --fresh exits health 000 with \"packages[1] is not a package entry (ADR-0130 D4): manifest.dashboards.0.refreshInterval\". After this PR at ec37c88413 the result is identical (see summary). The pre-converted control boots at 200 and serves 300 and 120, valid. The definition-collection control (single manifest, top-level refreshInterval) boots at 200; item and list both serve refreshIntervalSeconds 300, valid. (7) ABLATION, drop the body root (scripts/ablation-replace.mjs, wrap). Predicted beforehand: body pins red, controls green. Leg 1, source: anchor x1 to x0, blob 1ac3ee48d273 to b99253ed7ec9; Tests 72 failed | 252 passed (324): 7 body pins plus 65 step-18 before-legs; the premise, the no-packages control, window-shut, never-gates, the flip, 66 after-legs and the whole existing artifact-forward-conversion.test.ts stayed green. Restore: blob == HEAD 1ac3ee48d273, git diff HEAD empty. Leg 2, the door through dist: rebuild, then ablation-dist-preflight hit ablatedBodies in dist/index.js and dist/index.cjs; Tests 3 failed | 27 passed (30) across the new door file and four existing door files. Restore proven, rebuild, --absent: \"marker absent from all 12 built files\", \"working tree clean against HEAD\"; door file 5/5 again. (8) Lint, narrowed to 3 TS files with three proofs: population (eslint --print-config: 6/5/5 rules in effect), count (--format json: 3 files, 0 errors, 0 warnings), invariance (every parserOptions is { ecmaVersion, sourceType }, with no project, so no type-aware linting). pnpm lint not run (CI). Control-byte self-scan on all 4 files: no match.", "mcp_calls": "0 — no MCP GitHub tool called (reads via gh api REST)", "api_writes": "2 relay strokes (scripts/pm/fleet-write, executed as objectstack-fleet[bot]): (1) pr_create = POST /repos/objectstack-ai/objectstack/dispatches, then POST /repos/objectstack-ai/objectstack/pulls (draft) and POST /repos/objectstack-ai/objectstack/issues/23045/assignees [os-steve]; read-back: 10865 bytes sent and stored, identical. (2) this os-dev-report = POST /dispatches, then POST /repos/objectstack-ai/objectstack/issues/23027/comments via post-stamped.mjs. Plus 4 git pushes (not REST). No label write.", "open_questions": [ { "question": "The remaining half of #23027: @objectstack/runtime reads the raw option-B bundle before any conversion (createStandaloneStack, AppPlugin collections getter, AppPlugin.init packages[] branch, mergeRuntimeModule). Who carries it?", "options": [ "A — extend this claim to packages/runtime: convert the loaded bundle ONCE in loadArtifactBundle (before mergeRuntimeModule) with applyArtifactForwardConversions, so every runtime reader and AppPlugin's packages[] branch see converted bodies (one funnel; MetadataPlugin's own read converts again, idempotent). Rewrite the now-false comment in app-plugin.ts. About 1 to 2 files plus a door test.", "B — file it as a sub-issue of #23027 (same seam as A) for the runtime lane, and land #23045 as Part of.", "C — teach each raw reader to convert: N second opinions of one policy, the multiplication ADR-0130 D5 and #15005 warn about. Not recommended." ], "recommendation": "A or B with A's seam (loadArtifactBundle, one conversion per load). The measured control (artifact written as this function answers it) boots at os dev -a and serves the right answer, so A's seam is sufficient. Which of A or B is a surface and lane call for the seat." } ], "out_of_scope_findings": [ "class: a · reach: os dev -a optionb.json --fresh at ec37c88413 (this PR + #23024) exits at boot, health 000, \"packages[0] is not a package entry (ADR-0130 D4): manifest.dashboards.0.refreshInterval\"; in-process createDefaultHostConfig throws INVALID_ARTIFACT_PACKAGE_ENTRY/422, from resolveArtifactPackageOrder via resolveArtifactCollections via createStandaloneStack · evidence: the pre-converted control boots at 200 and serves 300/120 valid · Seam: spec:ADR-0087 at-rest replay → runtime:loadArtifactBundle / createStandaloneStack (also AppPlugin collections getter, AppPlugin.init packages[] branch, mergeRuntimeModule) · inside #23027's own acceptance: its remaining half, to be filed as a sub-issue of #23027 or carried by extending this claim (open_questions) · dedupe words: option-B artifact runtime raw bundle not converted; createStandaloneStack resolveArtifactCollections retired key package body; loadArtifactBundle forward conversion once at load", "carrier: the remaining-half PR (it touches AppPlugin.init's packages[] branch) · packages/runtime/src/app-plugin.ts servicePayload comment says package bodies are what \"no conversion reaches\"; false for the conversion after this PR · noted, not filed", "carrier: 承接者:无 · detectUnboundFormViewPredicateRoots (form-predicate-root-policy.ts) reads only top-level views, so an option-B body's form predicates get no boot notice (read, not measured at a door) · noted in PR Acceptance notes, not filed" ], "gates": [ "node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0 :: ✓ check-adr-0087-registration: this PR adds no declared-breaking changeset (1 non-breaking changeset(s) seen).", "node scripts/check-adr-0087-registration.mjs --self-test :: exit 0 :: ✓ check-adr-0087-registration --self-test: 548 assertions over real temp git repos (real scan()/assertInputs() path)", "node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0 :: ✓ Protocol lockstep: PROTOCOL_VERSION major 18, @objectstack/spec@17.7.0 — the one exception, evidenced: pre mode (tag `next`) and a pending", "node scripts/check-changeset-no-major.mjs --self-test :: exit 0 :: ✓ check-changeset-no-major --self-test: 401 assertions (frontmatter dialects measured against @changesets/parse + the pre/exit exemption swi", "node scripts/check-ci-filter-parity.mjs :: exit 0 :: OK: all 21 build input(s) turbo.json declares outside the packages (itself, 2 globalDependencies, and every `$TURBO_ROOT$` input of a build ", "node scripts/check-closing-keyword-parity.mjs :: exit 0 :: • packages/spec/CHANGELOG.md -- 8164628 bytes exceeds the sweep's 2097152-byte cutoff for UNREGISTERED files", "node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0 :: ✓ check-closing-keyword-parity --self-test: 40 assertions, 5 mutations of the shipped parsers each driven to red.", "node scripts/check-comment-mask-adoption.mjs :: exit 0 :: OK check:comment-mask-adoption — 14 private comment-stripper(s) under packages/** + examples/**, all 14 recorded and every recorded row sti", "node scripts/check-comment-mask-adoption.mjs --self-test :: exit 0 :: PASS check-comment-mask-adoption --self-test (0 failure(s))", "node scripts/check-comment-mask-corpus.mjs :: exit 0 :: ✓ comment-mask corpus sweep [scripts/js-comment-mask.mjs]: 8864 files, 0 disagree, 0 unparseable, 114.0s (comparator self-test: 26 cases pas", "node scripts/check-dts-emitted.mjs --self-test :: exit 0 :: check-dts-emitted self-test: all assertions passed.", "node scripts/check-empty-changeset.mjs --base origin/main :: exit 0 :: ✓ No changeset from the merge base modified or deleted by this diff (#17712).", "node scripts/check-empty-changeset.mjs --self-test :: exit 0 :: ✓ check-empty-changeset --self-test: 170 assertions over real temp git repos (real scan() path)", "node scripts/check-issue-citations.mjs :: exit 0 :: ✅ check-issue-citations: no issue citations added against 9f4b652e3 (1 file(s) read).", "node scripts/check-keyed-text-bounds.mjs :: exit 0 :: ⚠ The delta is information, not a verdict — this population grows AND shrinks for good reasons, and only the floors decide. Reproduce the re", "node scripts/check-keyed-text-bounds.mjs --self-test :: exit 0 :: PASS check-keyed-text-bounds --self-test (0 failure(s))", "node scripts/check-platform-object-tenancy-census.mjs :: exit 0 :: ✓ platform-object tenancy census matches the tree: 84 platform-namespace objects, 49 in the machinery's reach, 35 outside it, every exclusio", "node scripts/check-platform-object-tenancy-census.mjs --self-test :: exit 0 :: ✓ check-platform-object-tenancy-census self-test: all checks pass (84 objects, 35 outside the machinery)", "node scripts/check-plugin-teardown-shape.mjs :: exit 0 :: ✓ check:plugin-teardown-shape: 75 Plugin implementation(s) across 8278 source(s) under packages/**; every teardown-shaped method (stop / shu", "node scripts/check-plugin-teardown-shape.mjs --self-test :: exit 0 :: ✓ check-plugin-teardown-shape self-test: 48 cases pass (real pre-#10375 fixture reds, the repaired file and both delegating-alias directions", "node scripts/check-registry-log-declared.mjs :: exit 0 :: payload, not read (a dir the package's own tsconfig.json excludes): packages/create-objectstack/src/templates", "node scripts/check-registry-log-declared.mjs --self-test :: exit 0 :: self-test OK: 17 cases + level-vocabulary read + population declaration + real-tree selection floor.", "node scripts/check-rest-log-spy-declared.mjs :: exit 0 :: OK: 31 of 279 test file(s) beside packages/rest/src/log.ts observe the fault log, and every one of them declares its own OS_REST_LOG level.", "node scripts/check-rest-log-spy-declared.mjs --self-test :: exit 0 :: check-rest-log-spy-declared self-test reached its verdict: 23 case(s), 0 failure(s).", "node scripts/check-system-context-census.mjs :: exit 0 :: check-system-context-census: OK — 131 elevation read sites in 20 packages across 59 files, living in 111 symbol(s); the page cites 124 symbo", "node scripts/check-system-context-census.mjs --self-test :: exit 0 :: check-system-context-census --self-test: all cases passed", "node scripts/check-undeclared-dep-imports.mjs :: exit 0 :: ⚠ The delta is information, not a verdict — the floors are `>=` and cannot see an upward drift at all, which is why it is PRINTED. Reproduce", "node scripts/check-undeclared-dep-imports.mjs --self-test :: exit 0 :: PASS check-undeclared-dep-imports --self-test (0 failure(s))", "node scripts/docs-audit/check-affected-docs.mjs :: exit 0 :: → the unreachable rows themselves: this command with --json", "node scripts/docs-audit/check-drift-comment.mjs :: exit 0 :: ✓ check-drift-comment: 66 cases pass across 5 fixture diff(s).", "node scripts/pm/release-rehearsal-clone.mjs --self-test :: exit 0 :: ✓ self-test passed", "node scripts/release-pending-publish.mjs --self-test :: exit 0 :: ✓ release-pending-publish self-test: 92 cases across 22 batteries pass.", "pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0 :: ✓ check:duration-unit-keys — 197 unit-declaring numeric key(s) across 3041 source file(s) all carry their unit in the key name (or in a sibl", "pnpm check:changeset-gate-self-tests :: exit 0 :: ✓ check-changeset-no-major --self-test: 401 assertions (frontmatter dialects measured against @changesets/parse + the pre/exit exemption swi", "pnpm check:cross-package-test-inputs :: exit 0 :: OK: 30 package(s) read outside themselves, all declared, and turbo.json hashes every declared glob (6 of them on a split \"test:repo\" task); ", "pnpm check:dispatcher-error-vocabulary :: exit 0 :: [#15723] the ARGUMENT POSITION of new APIError( … ) and APIError.from( … ) IS now in this gate's population — the `apierrorarg` shape. `APIE", "pnpm check:doc-authoring :: exit 0 :: ✓ doc authoring guard: sibling-package prose ids hold the baseline — 0 pinned site(s) across 0 file(s), 94652 string(s) read in 1318 parsed ", "pnpm check:driver-memory-census :: exit 0 :: check-driver-memory-census: OK — every declaration is ledgered, every ledger entry is live, and every ruled file states \"#6664 census: 2 rul", "pnpm check:dts-closure :: exit 0 :: check-dts-closure: 59 built package(s) swept - 157/157 declared declaration file(s) present across 59 package(s); 0 built package(s) declare", "pnpm check:dual-build-cjs-loads :: exit 0 :: (rerun after first run's exit 3 PREREQUISITE NOT MET) ✓ check:dual-build-cjs-loads — 107 published require entry point(s) across 66 package(", "pnpm check:durability-log-level :: exit 0 :: ✓ read-seam invention (#5186 + #6451 + #9165, 3 package roots, vocabulary find/findOne/count): 82 read seam(s), none invents an unreported a", "pnpm check:engine-double-contract :: exit 0 :: ⚠ The delta is information, not a verdict — this population grows AND shrinks for good reasons, and only the floors decide.", "pnpm check:gitlink-declared :: exit 0 :: check-gitlink-declared: OK (11059 index entries -- 0 gitlink(s) at mode 160000; no .gitmodules in the index, so nothing is declared; nothing", "pnpm check:issue-citations :: exit 0 :: ✅ check-issue-citations --self-test: grammar narrowed, every spelling enumerated, qualifier a closed set of repositories, four 404 causes ke", "pnpm check:lean-entry-closure :: exit 0 :: Admitted set held exactly (15 packages); 6 denied names absent.", "pnpm check:logger-receiver-detach :: exit 0 :: control corpus fired on all five detach shapes in this same run, and stayed silent on the measured `console` and options-callback population", "pnpm check:nul-bytes :: exit 0 :: check-nul-bytes: OK (scanned 11050 text file(s) -- 11050 tracked, 0 untracked-not-ignored; skipped 9 binary; no raw ASCII control bytes).", "pnpm check:objectql-double-limit :: exit 0 :: baseline key set verified against 9f4b652: no files added.", "pnpm check:objectui-changeset :: exit 0 :: ✓ objectui-range --self-test: all checks passed", "pnpm check:org-identifier :: exit 0 :: check-org-identifier: OK (3356 author-facing source file(s), 18 session binding(s) resolved, no removed session.tenantId alias).", "pnpm check:page-declaration-shape :: exit 0 :: blind spot: 1 computed carrier(s) no source scan can enumerate — examples/app-crm/objectstack.config.ts:85.", "pnpm check:pm-changeset-deadline-census :: exit 0 :: ✓ changeset-deadline-census --self-test: all cases passed across 5 batteries (what counts as a named target, the controls that make a zero a", "pnpm check:published-files :: exit 0 :: ✓ check:published-files — 70 publishable package(s) of 81 workspace member(s) declare a `files` whitelist that covers every entry point plus", "pnpm check:query-options-erasure :: exit 0 :: baseline key set verified against 9f4b652: no files added.", "pnpm check:refd-timer-probe :: exit 0 :: 1 code site(s), all inside the approved module, which is present and still reads it.", "pnpm check:slot-lookup :: exit 0 :: baseline key set verified against 9f4b652: no files added.", "pnpm check:sourcemap-no-sources-content :: exit 0 :: check-sourcemap-no-sources-content: 59 built package(s) swept - 534 map(s), none embed source text.", "pnpm check:stack-collection-maps :: exit 0 :: ✓ check-stack-collection-maps: 8 enumerations reconciled against 33 declared collections (17 waiver rows, each with a reason).", "pnpm check:test-source-alias :: exit 0 :: check-test-source-alias OK — 73 packages with tests scanned; 60 registered as still resolving a workspace dep through `dist/`; 53 published ", "pnpm check:tier-file-adoption :: exit 0 :: @objectstack/cli — 82 file(s); imports readTierMode, selectTierFiles from scripts/nightly-tiers.mjs (via packages/cli/vitest-tiers.ts)", "pnpm check:type-check-coverage :: exit 0 :: ⚠ The delta is information, not a verdict — this population grows AND shrinks for good reasons, and only the floors decide.", "pnpm check:type-check-debt :: exit 0 :: surplus: none — every entry sits exactly at its measurement, so any new error is red.", "pnpm check:watch-hint-literal :: exit 0 :: ✓ check-watch-hint-literal: 73 declaration(s) across 4 rostered name(s) -- ROOT_DIR_WATCH_HINTS 49, ROOT_FILE_WATCH_HINTS 13, ROOT_WATCH_HIN", "pnpm check:where-matcher :: exit 0 :: baseline key set verified against 9f4b652: no files added." ], "gates_reconciliation": "node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran ran.txt :: exit 0 :: \"63 derived famil(ies) accounted for — 63 run, 0 NOT-MEASURED\". The derivation on the real diff (4 paths vs merge base 9f4b652e3) gave 63 = the lead 64 minus check:stack-collection-maps (plugin.ts not touched); that one was run too (exit 0). check:dual-build-cjs-loads first exit 3 PREREQUISITE NOT MET (read as NOT MEASURED), rerun exit 0 once every dist was present.", "line_budget": "595 insertions / 9 deletions over 4 files (604 changed lines, under the 3000 human-merge line). No skills/** or governed surface touched.", "files_changed": [ "packages/metadata-core/src/artifact-forward-conversion.ts (the second root, locateUnder, module doc \"Reach\", notice path doc)", "packages/metadata-core/src/artifact-forward-conversion-package-body.test.ts (new)", "packages/metadata/src/plugin-artifact-forward-conversion-package-body.test.ts (new; plugin.ts unchanged)", ".changeset/23027-package-body-conversion.md (patch, @objectstack/metadata-core; names the default-host refusal under \"Not yet\")" ], "deviations": [ "CONFLICT NAMED: the dispatch says PR line 1 is the closing keyword for the card. The role file says to use \"Part of #N\" when the merge must not close the card, and the card's measured door (os dev -a) still refuses. The role file wins, so line 1 is \"Part of #23027\".", "Claim surface reading: a door test file was added in packages/metadata while plugin.ts is unchanged. The claim names plugin.ts \"with its test\" only where the door must hand bodies through; the door needed no change, and the test is the door pin for Section 1 pin 1 and the Section 3 boot-log pin.", "Merged origin/main (#23024, #22953) before gates and before opening the PR (merge commit ec37c88413); runtime/verify/metadata-core rebuilt.", "The harness-written trailer \"Co-Authored-By: Claude Opus 5.5\" was refused by pre-push check:commit-card-trailers. I reworded it to the model-free pair by --amend on the unpublished tip; nothing was rewritten on the remote.", "Not run locally: pnpm lint (CI-owned; narrowed run above), and runtime's full suite (only its 3 conversion/option-B files; metadata-core's exports are byte-unchanged)." ] }Generated by Claude Code
objectstack-fleet commentedon Oct 12, 2026
Seat answer and claim amendment: the runtime half rides PR #23045 ·
domain:engineseat 2 ·session_01ADzJtzYTLUfgrRZHxkagkX· 2026-10-12T13:28ZThis answers report 6125201870 (PR #23045,
Part of #23027, statusblocked). The PR was checked on GitHub:os-steve;ec37c88413.The claim was 6123839199. Thread-read: 6125201870.
The open question: A, inside PR #23045. The remaining half is the measured public door (
os dev -a,os start).@objectstack/runtimereads the raw option-B bundle (createStandaloneStack, throughresolveArtifactCollections) before any conversion. So the card is not done until that seam converts. The seat takes A's seam,loadArtifactBundle, converting once per load beforemergeRuntimeModule, and carries it on the same branch and PR, for three reasons:C (each raw reader converts) is refused, as the report says.
Claim amendment (file surface), read on
origin/mainat this stamp. ⛔ Classes, positions and functions only.packages/runtime/src/load-artifact-bundle.ts, cross-lanedomain:cli:loadArtifactBundlerunsapplyArtifactForwardConversionsonce on the loaded bundle, beforemergeRuntimeModuleand every runtime reader.MetadataPlugin's own conversion stays and is idempotent.packages/runtime/src/app-plugin.ts: the comment only (theservicePayloadsentence about bodies "no conversion reaches"), which is false after this change.packages/runtime/src/: the option-B artifact boots through the runtime's own loader and servesrefreshIntervalSeconds: 300under its package. The definition-collection CONTROL is unchanged.patchfor@objectstack/runtimeand drops its "Not yet" clause.Fixes #23027. Line 2 staysClause-②: no(no exported member changes; re-check it on the real diff).Serial constraints:
apisendpoints answer 404 ENDPOINT_NOT_FOUND onos servefrom source, while an artifact boot of the same stack matches them — the metadata service holds noapiitems on config boots #22989, draft) editsapp-plugin.tsin the config door'sapiitems, a different region. The later lander mergesmain.domains/packages.ts) and PR test: the stage-8 test half — suites stop depending on the four catalog row objects (#15204 stage 8, S8-T) #22962 (one test file) are disjoint.domain:cliseat 2's post ([PM seat] domain:cli · seat 2 — ⏳ vacant #22648).Accepted on the first half:
packages/metadatawithplugin.tsunchanged.A contract-tier review of the whole PR runs before the queue. It covers the window authority reading of ADR-0087's "a compiled artifact inside the versioned window its own manifest opens" as it applies to a package body.
Findings:
app-plugin.tscomment rides it;detectUnboundFormViewPredicateRootsreading only top-level views stays an Acceptance note: it was read, not measured at a door. The seat files it if the dev measures a door that reaches it.objectstack-fleet commentedon Oct 12, 2026
os-dev-report
{ "issue": 23027, "status": "done", "branch": "claude/issue-23027-package-body-conversion", "pr": "https://github.com/objectstack-ai/objectstack/pull/23045", "session": "session_01ADzJtzYTLUfgrRZHxkagkX", "premise_still_valid": true, "summary": "Seat answer A (6125260474), carried on the same branch and PR #23045, whose body line 1 is now the closing keyword for this card, line 2 Clause-②: no. Runtime half: loadArtifactBundle runs convertArtifactAtLoad (one applyArtifactForwardConversions call) once on the loaded bundle, before mergeRuntimeModule and every reader. Measured, every boot-path reader goes through it: createStandaloneStack via resolveArtifactCollections, AppPlugin's collections getter, AppPlugin.init's packages[] branch and the manifest service, and os verify's loader. One reader reads the FILE by another path: readConfigDeclaredDefault (resolve-project-database.ts). Measured at os dev -a without --fresh: with only the loader converting, an option-B artifact whose body declares its default datasource and still carries refreshInterval booted against the UNIFIED default database (the resolver refused the body and declined), while the canonical-key twin opened the declared crm.db. It now calls the same function, and both open crm.db. That is one function at each of the boot's two disk reads, with no per-reader conversion (C refused). MetadataPlugin's own conversion stays; idempotence is pinned once (a second conversion of the loader's output returns the same reference with no notices). Boot log: ONE line per conversion, measured at os dev -a and os start (the loader is silent, the door prints, AppPlugin finds nothing left). app-plugin.ts: only the one comment sentence. Public door: BASE 0ede0f8ab3, os dev -a optionb.json --fresh gave health 000 with \"packages[1] is not a package entry\". At 6a01653b53 it gives health 200, and item and list serve refreshIntervalSeconds 300, valid, under com.example.multi.core. os start -a optionb-multi.json: health 200, 300 and 120 each under its own package. The definition-collection control is unchanged (200, 300 valid). The CI type-check red on 6a01653b53 was this PR's: the new runtime test had one TS2345 (an untyped union literal) in a file the test-typecheck ledger does not cover. Fixed in 20a5c41cfb; the lane is green locally and on GitHub. No exported member changes: convertArtifactAtLoad is a module export, not in packages/runtime/src/index.ts, and absent from dist/index.d.ts.", "tests": "At 20a5c41cfb (final head) unless stated, all through os-verify-lock.sh with VERDICT lines read. (1) Runtime full suite at 6a01653b53: Test Files 361 passed (361), Tests 5898 passed | 19 skipped (5917). The only later commit types a literal in the new test file, which re-ran at 20a5c41cfb: 6/6. (2) pnpm --filter @objectstack/runtime typecheck at 20a5c41cfb: exit 0, \"check:test-typecheck: OK … 27 file(s) / 190 error(s) / 68 pinned signature(s) held\". At 6a01653b53 it gave exit 1, \"1 type error(s) in a file the ledger does not cover\" (TS2345 at line 227): the CI red. (3) CI lane Type Check · workspace reproduced (the lint.yml step as a pull_request event, the selector run): 65 affected packages, \"Tasks: 130 successful, 130 total\", @objectstack/runtime:typecheck OK. GitHub check-runs on 20a5c41cfb: Type Check · workspace success, TypeScript Type Check success, Lint & Repo Gates success, Build Core success. (4) metadata-core test 18 files / 681 passed, typecheck exit 0; metadata 60 files / 879 passed, typecheck exit 0 (both at 6a01653b53 and in the lane at 20a5c41cfb). (5) New runtime door test standalone-stack-option-b-forward-conversion.test.ts, 6 tests (real createStandaloneStack plus kernel boot, read via getMetaItems/getMetaItem): the loader output is converted; idempotence; the subject packages[0].manifest.dashboards[0].refreshInterval 300 served as refreshIntervalSeconds 300, valid, under the orders package; multi per body; the definition-collection CONTROL unchanged; WINDOW SHUT refused with INVALID_ARTIFACT_PACKAGE_ENTRY/422 naming packages[0] and manifest.dashboards.0.refreshInterval; the database resolver resolves the body's declared default (retired key and canonical). (6) Public doors at 6a01653b53 (built) and 8a4bf29bec for the resolver: see summary. (7) ABLATION, loader call dropped (blob e49e71c00328 to b2afed453050): runtime door file red. The loader leg failed, and the subject and multi suites failed in beforeAll with \"Release artifact packages[0] is not a package entry (ADR-0130 D4): manifest.dashboards.0.refreshInterval\", skipping their 2 tests. The CONTROL, window-shut, resolver and app-plugin-converted-collections stayed green (Tests 1 failed | 11 passed | 2 skipped (14)). The metadata-core pins stayed green: 324/324. Restore: blob == HEAD e49e71c00328, git diff HEAD empty. ABLATION, resolver routing dropped (a970de19be5c to 41d80e491454): only the resolver leg went red (\"expected [ 'unified-default', … ] to deeply equal [ 'config-datasource', … ]\"), 5 green. Restore: blob == HEAD, diff empty. The first-half ablations stand as reported (72 red/252 green; the door 3/27 through dist with preflight both legs). (8) Lint narrowed to 7 TS files: print-config rules 6/5/5/6/6/6/5, --format json 7 files 0 errors 0 warnings, no parserOptions.project. Control-byte scan clean.", "mcp_calls": "0 — no MCP GitHub tool called (reads via gh api REST)", "api_writes": "This round: 2 relay strokes (scripts/pm/fleet-write, as objectstack-fleet[bot]). (1) issue_patch = POST /repos/objectstack-ai/objectstack/dispatches, then PATCH /repos/objectstack-ai/objectstack/issues/23045 (the PR body); read-back: 10449 bytes sent and stored, identical. (2) this os-dev-report = POST /dispatches, then POST /repos/objectstack-ai/objectstack/issues/23027/comments via post-stamped.mjs. Plus 6 git pushes (fast-forward; not REST). No label write, no assignee write. Earlier round: pr_create plus report 6125201870.", "open_questions": [], "out_of_scope_findings": [ "carrier: 承接者:无 · detectUnboundFormViewPredicateRoots (form-predicate-root-policy.ts) reads only top-level views, so an option-B body's form predicates get no boot notice (read, not measured at a door) · stays a PR Acceptance note per the seat, not filed" ], "gates": [ "node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0 :: ✓ check-adr-0087-registration: this PR adds no declared-breaking changeset (1 non-breaking changeset(s) seen).", "node scripts/check-adr-0087-registration.mjs --self-test :: exit 0 :: ✓ check-adr-0087-registration --self-test: 548 assertions over real temp git repos (real scan()/assertInputs() path)", "node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0 :: ✓ Protocol lockstep: PROTOCOL_VERSION major 18, @objectstack/spec@17.7.0 — the one exception, evidenced: pre mode (tag `", "node scripts/check-changeset-no-major.mjs --self-test :: exit 0 :: ✓ check-changeset-no-major --self-test: 401 assertions (frontmatter dialects measured against @changesets/parse + the pr", "node scripts/check-ci-filter-parity.mjs :: exit 0 :: OK: all 21 build input(s) turbo.json declares outside the packages (itself, 2 globalDependencies, and every `$TURBO_ROOT", "node scripts/check-closing-keyword-parity.mjs :: exit 0 :: • packages/spec/CHANGELOG.md -- 8164628 bytes exceeds the sweep's 2097152-byte cutoff for UNREGISTERED files", "node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0 :: ✓ check-closing-keyword-parity --self-test: 40 assertions, 5 mutations of the shipped parsers each driven to red.", "node scripts/check-comment-mask-adoption.mjs :: exit 0 :: OK check:comment-mask-adoption — 14 private comment-stripper(s) under packages/** + examples/**, all 14 recorded and ev", "node scripts/check-comment-mask-adoption.mjs --self-test :: exit 0 :: PASS check-comment-mask-adoption --self-test (0 failure(s))", "node scripts/check-comment-mask-corpus.mjs :: exit 0 :: ✓ comment-mask corpus sweep [scripts/js-comment-mask.mjs]: 8870 files, 0 disagree, 0 unparseable, 135.0s (comparator sel", "node scripts/check-dts-emitted.mjs --self-test :: exit 0 :: check-dts-emitted self-test: all assertions passed.", "node scripts/check-empty-changeset.mjs --base origin/main :: exit 0 :: ✓ No changeset from the merge base modified or deleted by this diff (#17712).", "node scripts/check-empty-changeset.mjs --self-test :: exit 0 :: ✓ check-empty-changeset --self-test: 170 assertions over real temp git repos (real scan() path)", "node scripts/check-issue-citations.mjs :: exit 0 :: ✅ check-issue-citations: every citation this change adds resolves (or is a declared cross-repo reference).", "node scripts/check-keyed-text-bounds.mjs :: exit 0 :: ⚠ The delta is information, not a verdict — this population grows AND shrinks for good reasons, and only the floors deci", "node scripts/check-keyed-text-bounds.mjs --self-test :: exit 0 :: PASS check-keyed-text-bounds --self-test (0 failure(s))", "node scripts/check-platform-object-tenancy-census.mjs :: exit 0 :: ✓ platform-object tenancy census matches the tree: 84 platform-namespace objects, 49 in the machinery's reach, 35 outsid", "node scripts/check-platform-object-tenancy-census.mjs --self-test :: exit 0 :: ✓ check-platform-object-tenancy-census self-test: all checks pass (84 objects, 35 outside the machinery)", "node scripts/check-plugin-teardown-shape.mjs :: exit 0 :: ✓ check:plugin-teardown-shape: 75 Plugin implementation(s) across 8284 source(s) under packages/**; every teardown-shape", "node scripts/check-plugin-teardown-shape.mjs --self-test :: exit 0 :: ✓ check-plugin-teardown-shape self-test: 48 cases pass (real pre-#10375 fixture reds, the repaired file and both delegat", "node scripts/check-registry-log-declared.mjs :: exit 0 :: payload, not read (a dir the package's own tsconfig.json excludes): packages/create-objectstack/src/templates", "node scripts/check-registry-log-declared.mjs --self-test :: exit 0 :: self-test OK: 17 cases + level-vocabulary read + population declaration + real-tree selection floor.", "node scripts/check-rest-log-spy-declared.mjs :: exit 0 :: OK: 31 of 279 test file(s) beside packages/rest/src/log.ts observe the fault log, and every one of them declares its own", "node scripts/check-rest-log-spy-declared.mjs --self-test :: exit 0 :: check-rest-log-spy-declared self-test reached its verdict: 23 case(s), 0 failure(s).", "node scripts/check-system-context-census.mjs :: exit 0 :: check-system-context-census: OK — 131 elevation read sites in 20 packages across 59 files, living in 111 symbol(s); the ", "node scripts/check-system-context-census.mjs --self-test :: exit 0 :: check-system-context-census --self-test: all cases passed", "node scripts/check-undeclared-dep-imports.mjs :: exit 0 :: ⚠ The delta is information, not a verdict — the floors are `>=` and cannot see an upward drift at all, which is why it i", "node scripts/check-undeclared-dep-imports.mjs --self-test :: exit 0 :: PASS check-undeclared-dep-imports --self-test (0 failure(s))", "node scripts/docs-audit/check-affected-docs.mjs :: exit 0 :: → the unreachable rows themselves: this command with --json", "node scripts/docs-audit/check-drift-comment.mjs :: exit 0 :: ✓ check-drift-comment: 66 cases pass across 5 fixture diff(s).", "node scripts/pm/release-rehearsal-clone.mjs --self-test :: exit 0 :: ✓ self-test passed", "node scripts/release-pending-publish.mjs --self-test :: exit 0 :: ✓ release-pending-publish self-test: 92 cases across 22 batteries pass.", "pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0 :: ✓ check:duration-unit-keys — 197 unit-declaring numeric key(s) across 3044 source file(s) all carry their unit in the ke", "pnpm check:changeset-gate-self-tests :: exit 0 :: ✓ check-changeset-no-major --self-test: 401 assertions (frontmatter dialects measured against @changesets/parse + the pr", "pnpm check:cross-package-test-inputs :: exit 0 :: OK: 30 package(s) read outside themselves, all declared, and turbo.json hashes every declared glob (6 of them on a split", "pnpm check:dispatcher-error-vocabulary :: exit 0 :: [#15723] the ARGUMENT POSITION of new APIError( … ) and APIError.from( … ) IS now in this gate's population — the `apier", "pnpm check:doc-authoring :: exit 0 :: ✓ doc authoring guard: sibling-package prose ids hold the baseline — 0 pinned site(s) across 0 file(s), 94675 string(s) ", "pnpm check:driver-memory-census :: exit 0 :: check-driver-memory-census: OK — every declaration is ledgered, every ledger entry is live, and every ruled file states ", "pnpm check:dts-closure :: exit 0 :: check-dts-closure: 73 built package(s) swept - 172/172 declared declaration file(s) present across 72 package(s); 1 buil", "pnpm check:dual-build-cjs-loads :: exit 0 :: ⚠ The delta is information, not a verdict — this population grows AND shrinks for good reasons, and only the floors deci", "pnpm check:durability-log-level :: exit 0 :: ✓ read-seam invention (#5186 + #6451 + #9165, 3 package roots, vocabulary find/findOne/count): 82 read seam(s), none inv", "pnpm check:engine-double-contract :: exit 0 :: ⚠ The delta is information, not a verdict — this population grows AND shrinks for good reasons, and only the floors deci", "pnpm check:gitlink-declared :: exit 0 :: check-gitlink-declared: OK (11067 index entries -- 0 gitlink(s) at mode 160000; no .gitmodules in the index, so nothing ", "pnpm check:issue-citations :: exit 0 :: ✅ check-issue-citations --self-test: grammar narrowed, every spelling enumerated, qualifier a closed set of repositories", "pnpm check:lean-entry-closure :: exit 0 :: Admitted set held exactly (15 packages); 6 denied names absent.", "pnpm check:logger-receiver-detach :: exit 0 :: control corpus fired on all five detach shapes in this same run, and stayed silent on the measured `console` and options", "pnpm check:nul-bytes :: exit 0 :: check-nul-bytes: OK (scanned 11058 text file(s) -- 11058 tracked, 0 untracked-not-ignored; skipped 9 binary; no raw ASCI", "pnpm check:objectql-double-limit :: exit 0 :: baseline key set verified against df7a137: no files added.", "pnpm check:objectui-changeset :: exit 0 :: ✓ objectui-range --self-test: all checks passed", "pnpm check:org-identifier :: exit 0 :: check-org-identifier: OK (3359 author-facing source file(s), 18 session binding(s) resolved, no removed session.tenantId", "pnpm check:page-declaration-shape :: exit 0 :: blind spot: 1 computed carrier(s) no source scan can enumerate — examples/app-crm/objectstack.config.ts:85.", "pnpm check:pm-changeset-deadline-census :: exit 0 :: ✓ changeset-deadline-census --self-test: all cases passed across 5 batteries (what counts as a named target, the control", "pnpm check:published-files :: exit 0 :: ✓ check:published-files — 70 publishable package(s) of 81 workspace member(s) declare a `files` whitelist that covers ev", "pnpm check:query-options-erasure :: exit 0 :: baseline key set verified against df7a137: no files added.", "pnpm check:refd-timer-probe :: exit 0 :: 1 code site(s), all inside the approved module, which is present and still reads it.", "pnpm check:slot-lookup :: exit 0 :: baseline key set verified against df7a137: no files added.", "pnpm check:sourcemap-no-sources-content :: exit 0 :: check-sourcemap-no-sources-content: 69 built package(s) swept - 552 map(s), none embed source text.", "pnpm check:stack-collection-maps :: exit 0 :: ✓ check-stack-collection-maps: 8 enumerations reconciled against 33 declared collections (17 waiver rows, each with a re", "pnpm check:swallow-census-controls :: exit 0 :: Run `--self-test` for every family; see `SELF_TEST_MODES` for the whole rationale.", "pnpm check:test-source-alias :: exit 0 :: check-test-source-alias OK — 73 packages with tests scanned; 60 registered as still resolving a workspace dep through `d", "pnpm check:tier-file-adoption :: exit 0 :: @objectstack/cli — 82 file(s); imports readTierMode, selectTierFiles from scripts/nightly-tiers.mjs (via packages/cli/vi", "pnpm check:type-check-coverage :: exit 0 :: ⚠ The delta is information, not a verdict — this population grows AND shrinks for good reasons, and only the floors deci", "pnpm check:type-check-debt :: exit 0 :: surplus: none — every entry sits exactly at its measurement, so any new error is red.", "pnpm check:watch-hint-literal :: exit 0 :: ✓ check-watch-hint-literal: 73 declaration(s) across 4 rostered name(s) -- ROOT_DIR_WATCH_HINTS 49, ROOT_FILE_WATCH_HINT", "pnpm check:where-matcher :: exit 0 :: baseline key set verified against df7a137: no files added.", "LANE Type Check · workspace (lint.yml step, reproduced locally as a pull_request event at 20a5c41cfb, the step's own selector, --concurrency=2 added) :: exit 0 :: 65 affected packages, Tasks: 130 successful, 130 total; GitHub at 20a5c41cfb: Type Check · workspace success, TypeScript Type Check success" ], "gates_reconciliation": "node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran ran.txt :: exit 0 :: \"65 derived famil(ies) accounted for — 65 run, 0 NOT-MEASURED\". Derived on the real diff at 20a5c41cfb (8 paths vs merge base df7a13792): 65 commands, a superset of the lead 64 (+check:swallow-census-controls). The derivation printed STALE TREE: origin/main moved 2 commits after the one merge (6a6a4d3e9c), and check-durability-degradation-log-level.mjs and measure-durability-swallow-family.mjs differ there; CI reads the merge ref.", "line_budget": "894 insertions / 15 deletions over 8 files (909 changed lines, under the 3000 human-merge line). No skills/** and no governed surface.", "files_changed": [ "packages/metadata-core/src/artifact-forward-conversion.ts (first half: the second root, locateUnder, module doc \"Reach\", notice path doc)", "packages/metadata-core/src/artifact-forward-conversion-package-body.test.ts (new, first half)", "packages/metadata/src/plugin-artifact-forward-conversion-package-body.test.ts (new, first half)", "packages/runtime/src/load-artifact-bundle.ts (convertArtifactAtLoad, called once in loadArtifactBundle before mergeRuntimeModule)", "packages/runtime/src/resolve-project-database.ts (readConfigDeclaredDefault calls convertArtifactAtLoad on its own read)", "packages/runtime/src/app-plugin.ts (the servicePayload comment sentence only)", "packages/runtime/src/standalone-stack-option-b-forward-conversion.test.ts (new, the runtime door test)", ".changeset/23027-package-body-conversion.md (patch for @objectstack/metadata-core and @objectstack/runtime; \"Not yet\" replaced by what operators see at os dev -a and os start)" ], "deviations": [ "Surface: resolve-project-database.ts is not named in the seat's amendment. It is routed under the coordinator's instruction to name a reader that reads the file by another path and send it through the same single conversion. That reader is readConfigDeclaredDefault; the measured reason is the silent wrong-database boot (summary). Its hunk is a 1-line call change plus a comment.", "The runtime door test file also carries the loader idempotence pin and the resolver pin: one door test file, as the amendment says.", "The CI type-check red on the pushed head 6a01653b53 was this PR's own (TS2345 in the new test). Fixed in 20a5c41cfb before this report.", "Merged origin/main twice in total across the claim: a8579dcca9 at worktree recreation, and 6a01653b53 once before this round's gates, both by merge commit. main moved 2 more commits during the gates; not merged again (once before the gates).", "The local reproduction of the workspace type-check lane added --concurrency=2 (resource discipline); CI runs turbo's default.", "Not run locally: pnpm lint (CI-owned; narrowed run above).", "Worktree ../objectstack-issue-23027 recreated from the remote branch and removed after this report; every server started was killed by its recorded PID." ] }Generated by Claude Code
objectstack-fleet commentedon Oct 12, 2026
✅ ACCEPT: PR #23045 at
20a5c41cfb·domain:engineseat 2 ·session_01ADzJtzYTLUfgrRZHxkagkX· 2026-10-12T15:31ZBasis:
Everything below was checked on GitHub, not taken from the reports. Classes, positions and functions only.
main, assigneeos-steve;Fixes #23027, the only closing keyword;Clause-②: no.check-governed-merges: clean);content/docs/releases/;domain:cli, declared on [PM seat] domain:cli · seat 2 — ⏳ vacant #22648.applyArtifactForwardConversionsruns its one walk over the definition and then over eachpackages[i].manifestbody, with the same registry, window and refused flips;loadArtifactBundleconverts once per load throughconvertArtifactAtLoad;readConfigDeclaredDefault, the boot's other disk read, calls the same function;resolve-project-database.tsis outside the amendment's file list. The reason was measured: without the conversion, an option-B artifact booted silently against the unified default database instead of its declared one. The change is one call plus a comment.os dev -aon the option-B artifact went from health 000 (refused, "packages[1]is not a package entry") to 200, servingrefreshIntervalSeconds: 300;os startserves 300 and 120, each under its own package;composeStacks), measured as triage asked: it is refused earlier, atdefineStack, with the tombstone prescription. That is the authoring funnel, so no fix is owed.20a5c41cfb: 40 check runs, none failed. The type-check red on6a01653b53was this PR's own and is fixed.Clause-② no, which holds:convertArtifactAtLoadis not reachable fromexports;patchfor both packages.packages[]bundle handed toAppPluginby a route other thanloadArtifactBundlereaches themanifestservice unconverted. The PR's own comment says so; it is not a measured door;detectUnboundFormViewPredicateRootsreads top-level views only. It was read, not measured at a door;mainrun is watched against the 10-second per-file rule.Landing: prechecks pass:
origin/main;The PR is readied and armed through the relay in this act.
objectstack-fleet commentedon Oct 12, 2026
Landed: PR #23045 →
c5b43b6068·domain:engineseat 2 ·session_01ADzJtzYTLUfgrRZHxkagkX· 2026-10-12T16:03Zc5b43b606807e26d873e2407cbd38a844e624895, with one parent,466380132e(finding: atreefield'sdeleteBehavioris never honoured — the spec materializesset_nullontree, but the engine's referential walk reads onlylookup/master_detail, so deleting a parent leaves children holding its id #22957's merge). ACCEPT is on this card. Contract review PASS 6127347853 on20a5c41cfb.mainat that commit: the squash carries the accepted change exactly. All eight files have hunks identical to the accepted head.mainnow holds: an option-B artifact built before a retirement boots atos dev -aandos start, and everypackages[].manifestbody is converted per the artifact's own window. For example, a dashboard'srefreshInterval: 300is served asrefreshIntervalSeconds: 300under its own package, and the conversion is named once in the boot log. A window-shut artifact is still refused.completedby the PR'sFixes #23027.pm:dispatchedcomes off in this act.mainrun is read against the 10-second per-file rule.