Skip to content

ADR-0141 runtime: pre-install dependency check (dependency_missing, 422) at the three install doors, plus dependency-declared namespace co-owners and the dependency-ordered rehydrate #25792

Closed

Description

@objectstack-fleet

Filing gate: ③ maintainer-directed — the runtime card ADR-0141 §Execution plan item 1 names, owed by landed work (PR #25717, merge afe6ab9ccc)
Ruling: #25403 comment 6369704387 (B′, maintainer 「同意」 2026-10-23T16:28:30Z): "Then one runtime card for the pre-install check (XS–S) with the console's refusal text, plus docs"; comment 6377678758 (letter A, 「同意 #8」 2026-10-24T00:40:55Z): "objectql S (one more source for coOwners, plus a test), riding with the B′ runtime pre-install card"
Business reason: the ruling's own words — 「如果这样,hotcrm 岂不是真实的可以分拆出很多模块」/「而每个包可以独立安装」. Today an app that depends on another installs anyway and fails later at its first cross-app save; and a HotCRM module shipped as its own artifact (revenue first, hotcrm#2271) is refused by the namespace gate next to HotCRM. Without this card, composite products keep shipping as one artifact.
Reader: the domain:services / objectql lane, one PR touching packages/metadata-core, packages/metadata-protocol, packages/runtime/src/domains/packages.ts, packages/cloud-connection, packages/objectql, packages/services/service-package, and the Console refusal text
Dedupe (open AND closed): "pre-install dependency check dependency_missing install refuse missing dependency" → 0; "ADR-0141 inter-app dependency runtime card" → 7, none a runtime card (#25403 is the decision card); "namespace co-owner declared dependency install gate object name uniqueness cross-artifact" → 1 (#14122, closed, the ADR-0130 in-artifact case)
Size: S (ADR-0141's estimate). Siblings: author-side range rule card (released with or before this one) and snapshot-alignment card, cut together from the same plan.

What to build (ADR-0141, docs/adr/0141-inter-app-dependency-declared-checked-before-install.md)

  1. D2 — the pre-install check. One predicate, called at the three install acts and nowhere else:

    • packages/metadata-protocol/src/protocol-package-install.ts#installPackage;
    • the bare-registry fallback of POST /api/v1/packages (packages/runtime/src/domains/packages.ts);
    • POST /api/v1/marketplace/install-local (packages/cloud-connection/src/marketplace-install-local-plugin.ts), before the manifest service sees the artifact.

    Each entry (id, range) of the installed package's own manifest.dependencies is satisfied when id is installed (SchemaRegistry.getPackage) at a manifest.version inside range, or is delivered by the same artifact (presence only). Otherwise the whole install is refused before anything registers: status 422, a code registered in the ADR-0112 D3 ledger, and one AppCompatibilityCheck issue per unmet entry (severity: 'error', category: 'dependency_missing', message naming the id, the range and what was found). Every unmet entry is reported in one answer. An unparsable range or version does not satisfy (fail closed). ⛔ Not at the re-registration paths (code packages at init/start, the sys_packages rehydrate, the install-local kernel:ready rehydrate), and ⛔ not at PATCH /api/v1/packages/:id.

  2. The range reader. One sibling function in packages/metadata-core/src/protocol-handshake.ts, sharing the handshake's alternative/comparator parsing, at exact-version granularity. The card states the grammar it supports: at least ^, ~, >=, an x wildcard, || and the space intersection, so 「HotCRM 6.x」 reads. assertProtocolCompat's verdicts and ADR-0087 D1's never-false-rejection contract stay unchanged. ⛔ No fourth reader (rangeAdmitsMajor, parseRangeFloor, SemanticVersionManager are the three that exist).

  3. The Console refusal text, in the author's terms (「请先安装 HotCRM 6.x」), from the refusal's facts.

  4. D5 — dependency-declared co-owners. A second source for the co-owner set the namespace gate and refuseCoOwnedObjectNameCollision both read (packages/objectql/src/registry.ts, NamespaceConflictError): a package that declares X and uses X's namespace co-owns it with X, and with X's other declared dependents that use it (sibling arm). One-way, read from either end, whichever registers first. Tests extend the ADR-0130 D3 gate test: a cross-artifact pair and a sibling pair (a dependent defining an object name X owns, and a sibling defining a name the other sibling owns, are refused; assert code and status, message worded per co-ownership source), and the upgrade case (X reinstalled beside its module is admitted; X newly defining a name the module owns is refused).

  5. D5 — rehydrate order. The sys_packages and install-local rehydrates sort their own batch through resolvePluginOrder (packages/core/src/plugin-order.ts), ledger entries as nodes. A cycle degrades to source order with a logged refusal and never throws out of the hydrate.

  6. D8 items 2 and 3. A stated fate for optional (no record spelling; a snapshot never produces it and D2 never reads it until decided), and for AppManifestSchema.dependencies (project from the record, or retire through the ADR-0087 conversion layer, decided on a measured reader count).

  7. Docs (§Execution plan item 4). The package-authoring guide states how to declare a dependency, the range grammar, what the install refusal means, that two modules of one app need not declare each other, and that os validate cannot see a dependency's objects.

First readings (ADR-0141 §Open items 2, 3, 4, 6)

  • 2: whether a dependent on one hydrate path can have its parent on a later one, and whether the kernel:ready seals in packages/objectql/src/plugin.ts run before or after the ledger rehydrate on the same hook.
  • 3: a declared id that names no installable package, if HotCRM's manifests carry one. Report it; D2 does not exempt it.
  • 4: what enabled: false on X should mean for a dependent, measured against the save door's closure and the data door today.
  • 6: that the artifact os build writes for a standalone module carries type: 'app', and what each install door does today with a top-level package of another type.

Out of scope

D3's deferred items (uninstall protection, upgrade interception, topological/cascade install, owner-side publish marker, marketplace version negotiation, judging a post-install dependencies edit). D8 item 1 (record-to-array snapshot conversion) is the snapshot-alignment card. HotCRM-side work (D4 readings, D7 preconditions) is the HotCRM lane, hotcrm#2271.

Activity

  1. added
    area:devpathThe road — create, dev, verify, publish/install, connect an agent, iterate
    domain:serverDomain lane — seat card indexed by label:pm:seat
    enhancementNew feature or request
    on Oct 25, 2026
  2. objectstack-fleet commented on Oct 25, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade — enhancement · priority:p2 · domain:server · area:devpath · pm:queue · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-25T13:52Z

    ⛔ Not a claim, ⛔ not a dispatch.


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 5 · card thread
    Session: session_01R1q2xdtYgU6FKrZ7LgjPCC (the seat); the card thread is session_01NndsH9Qm7k6AS2i2fWbjNA
    Account: os-project-manager (the seat's linked user; the card's assignee)
    Branch: claude/project-thread-78l2jg (the card thread's harness-pinned branch; it names no issue, so this claim is the identity record)
    Worktree: objectstack-issue-25792 (a linked worktree in the card thread's container)
    Domain: domain:server
    Seat: domain:server#1
    Ruling-ref: triage 6398501495; #25403 B′ (6369704387) and letter A (6377678758); scope = the card's items 1, 2, 4, 5, 7; item 3 rides objectui's card; item 6 goes to open_questions
    File surface: the card's reader list (packages/metadata-core/src/protocol-handshake.ts, packages/metadata-protocol/src/protocol-package-install.ts, packages/runtime/src/domains/packages.ts, packages/cloud-connection/src/marketplace-install-local-plugin.ts, packages/objectql/src/registry.ts, packages/services/service-package/**, packages/core/src/plugin-order.ts callers), the package-authoring docs, their tests, one changeset; in packages/spec/** only the new code's row in packages/spec/src/api/error-code-ledger.zod.ts (contract review by the seat). ⛔ No ui/**, ⛔ no spec key change (stop on breach; explain in the report)
    Container & model: M, mode:thread, default model and effort (set at thread open)
    Clause-②: no (narrowing: an install whose declared dependency is unmet is refused at the three install acts; measured and declared by the dev)
    Metadata-protocol: none
    Responsibility: n/a — ADR-0141 runtime card owed by landed work (PR #25717)
    Thread-read: 6398501495
    Premise: holds (read 2026-10-26T06:09Z at origin/main 9ca36b163e: card open, unassigned, pm:queue, no other seat's claim)
    Serial constraints cleared: no open PR names #25792; #25744 (this seat) also edits packages/objectql/src/registry.ts (registerObject, a different function from the co-owner gate); whichever lands second merges main

  4. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 25792,
      "status": "needs_decision",
      "branch": "claude/project-thread-78l2jg",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/25986",
      "session": "session_01NndsH9Qm7k6AS2i2fWbjNA",
      "premise_still_valid": true,
      "summary": "PR #25986 (draft, head 12daab9542, Part of #25792) delivers card items 1, 2 and 7. It adds the ADR-0141 D2 pre-install dependency check at exactly the three install acts: the protocol installPackage (which is also the composed arm of POST /api/v1/packages), the no-protocol fallback arm of POST /api/v1/packages, and POST /api/v1/marketplace/install-local (inline and cloud-snapshot branches). A package with an unmet entry gets 422 DEPENDENCY_MISSING, with one AppCompatibilityCheck issue and one unmet fact per entry. It also adds one fail-closed range reader beside the D1 handshake (rangeAdmitsVersion; shared comparator peel, D1 verdicts unchanged), the ledger row (the only packages/spec edit), and the docs. Size measured: the D2 part is about 1,500 changed lines (18 files, +1500/-42), over the ~900 split point, so items 4 and 5 (D5 co-owner source and rehydrate order) are the next PR, and #25792 stays open. Item 3 has no ui/** edit. Item 6 is in open_questions with no spec key change. HotCRM at d91c7b0 passes unchanged: service, marketing and revenue each declare { 'app.objectstack.hotcrm': '^6.0.0' } and are composed with the 6.0.0 app, so the app counts as delivered (presence only), and ^6.0.0 also admits 6.0.0 if a module ships standalone. Departures: (1) Clause-② measured `yes (narrowing)`, not the claim's `no (narrowing ...)`, because the diff adds a ledger code and new metadata-core exports. (2) Two files sit outside the claim's listed surface, both machine-required: scripts/adr-anchors/packages__metadata-core__src__protocol-handshake.ts.json (an ADR anchor for the edited file) and scripts/docs-audit/handwritten-docs.json (the roster entry check:docs-audit-scope demanded for the new docs page).",
      "tests": "Red first: protocol-package-install-dependencies.test.ts failed on main (2 failed) and passes after wiring. New suites: metadata-core protocol-handshake-dependencies.test.ts 17, metadata-protocol protocol-package-install-dependencies.test.ts 4, runtime packages-install-dependency-missing.test.ts 6 (composed and fallback arms), cloud-connection marketplace-install-local-dependency-missing.test.ts 7. On head 12daab9542, after a full build (os-verify-lock VERDICT command-exit 0, 119/119 tasks): spec Test Files 722 passed, Tests 21884 passed | 1 todo; metadata-core 20 files / 817 passed; metadata-protocol 266 passed | 3 skipped files, 21537 passed | 19 skipped; cloud-connection 57 files / 729 passed; runtime Test Files 448 passed, Tests 7188 passed | 19 skipped (VERDICT command-exit 0). Typecheck exit 0 for all five packages. Before the core rebuild, runtime typecheck failed with TS2305 (markAppAutomationElevation) and 42 tests failed in app-automation-identity-write-refusal.integration.test.ts; both came from a stale @objectstack/core dist after merging main, and both cleared after the rebuild. Ablations (scripts/ablation-replace.mjs, each restore checked against HEAD): fallback-arm call removed, 2 failed; install-local check disabled, 4 failed; presence-only arm deleted, 1 failed; shared comparator peel deleted, 9 failed (5 in the new suite, 4 in the D1 handshake suite).",
      "gates": "Derived list from dispatch-gates.mjs --commands: 127 families, each run with its exit code captured before any pipe, then reconciled with `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran gates-ran.txt`: exit 0, '127 derived, 127 run, 0 NOT-MEASURED, 0 UNRUN'. First pass: four non-zero. check-plugin-teardown-shape --self-test exit 3 (shallow clone; 0 after git fetch --unshallow). check:skill-examples and check:dual-build-cjs-loads exit 3 (no build; 0 after the full build). check:docs-audit-scope exit 1, a real miss: the new page was not in handwritten-docs.json. It was fixed with `check-audit-scope.mjs --write` and now exits 0. Also scoped eslint on the touched packages exits 0, and check:nul-bytes exits 0. The gates ran at b1ed3d7420. The final head 12daab9542 adds only a clean merge of origin/main, with no change to this PR's own files. CI check-runs are not cited yet because the PR was just opened; the seat holds its events.",
      "dogfood_battery": "12daab9542 · os-verify-lock: VERDICT command-exit 1 · held the lock 1022s · Test Files 1 failed | 295 passed | 3 skipped (299) · Tests 1 failed | 2522 passed | 18 skipped (2541). The one failure: test/authz-probe-blind-spot.test.ts > packages/runtime/src/domains/i18n.ts — every positive control is still present in THAT file: control \"handleI18nRequest\" moved, expected 3, got 4. It is red on main too: neither packages/runtime/src/domains/i18n.ts nor the census test differs from origin/main, and the count moved with #25956. It is not this PR's failure.",
      "mcp_calls": "0 — no MCP GitHub calls",
      "api_writes": "2 — relay strokes: (1) pr_create via fleet-write repository_dispatch (request fw-20261026T080426Z-b519b6, run 40289773646 success, read-back 6642/6642 bytes identical) creating #25986; (2) this os-dev-report comment on #25792 via post-stamped.mjs. Plus git push (not REST). Write budget 3/3: push, pr_create, report. One REST GET (a read, no write) read back the PR (draft true, assignee os-project-manager).",
      "open_questions": [
        {
          "question": "Item 6a: what is the fate of PackageDependencySchema (marketplace/package-version.zod.ts: an array of { packageId, versionRange, optional }), the second dependency spelling, whose `optional` arm no install path honours? D2 reads only ManifestSchema.dependencies ({ id: range }) and fails closed. Measured at 12daab9542: zero readers of PackageDependencySchema, `versionRange` or `optional` outside packages/spec (packages/, ui/, examples/, apps/; the only hits are D2's own doc comments and a negative test that pins the array shape as unreadable-declaration).",
          "options": [
            "A Retire PackageDependencySchema (with `optional`) now, as its own spec PR with retiredKey() tombstones and the spec-property-retirement kit.",
            "B Keep it and wire `optional` into D2 (a missing optional entry is a warning, not a refusal), so the marketplace version record becomes a second input to the check.",
            "C Leave it declared and unwired, and record it as a known implementation gap."
          ],
          "recommendation": "A. Business need: zero readers and no producer measured, and HotCRM uses only the record spelling, so it serves no real scenario. Long-term soundness: one dependency spelling (ManifestSchema.dependencies) that the runtime enforces, which is contract-first; B adds a second input and a lenient arm to a gate that ADR-0141 made fail-closed. Preventing AI mistakes: a declared `optional` that nothing enforces is exactly a capability an AI can declare that the runtime does not honour, and C keeps that trap; A removes it and turns any use into a loud parse refusal. Startup scope: retire immediately, with no staged window and no named external user, and no new gate. Cost: A is one spec retirement PR (about 1 day with the kit); B is a D2 extension plus docs (about 1-2 days) and widens the surface."
        },
        {
          "question": "Item 6b: what is the fate of AppManifestSchema.dependencies (system/app-install.zod.ts: z.array(z.string()), 'other apps that must be installed first')? It is a third, versionless dependency spelling. Measured at 12daab9542: zero readers of AppManifestSchema outside packages/spec.",
          "options": [
            "A Retire AppManifestSchema.dependencies now (tombstone it to point at manifest.dependencies), in the same spec PR as 6a.",
            "B Wire it into D2 as presence-only entries (any version).",
            "C Leave it declared and unwired."
          ],
          "recommendation": "A. Business need: no reader, no producer, no HotCRM use. Long-term soundness: the install check judges one declaration, and a versionless list duplicates manifest.dependencies with weaker semantics. Preventing AI mistakes: an AI writing it gets nothing enforced today (C), and B would let it skip ranges, which is lenient on the consumer side; A makes the record spelling the only one and refuses the other loudly. Startup scope: immediate retirement, no staged window, no new gate. Cost: a small addition to the 6a retirement PR."
        },
        {
          "question": "Declaration line: should the PR keep `Clause-②: yes (narrowing)` (measured), or take the claim's `no (narrowing ...)`? Metadata-protocol is `none` either way. That line is borderline because a dependency range outside the grammar (e.g. `latest`) still parses but is now refused at install; precedents #25954 and #25964 read a run-time refusal as `none`.",
          "options": [
            "A Keep `Clause-②: yes (narrowing)`: the diff adds public surface (DEPENDENCY_MISSING ledger code, new @objectstack/metadata-core exports) and narrows install acceptance.",
            "B Change it to `no (narrowing: ...)`, treating the new exports as internal plumbing. The seat would edit the PR body and the changeset."
          ],
          "recommendation": "A, because AGENTS.md counts a new ledger code and new public exports as a surface change, and the BREAKING banner, ADR-0087 marker and full dogfood run that the (narrowing) arm requires are already in place. If the seat rules B, the PR body edit goes through the seat (the dev writes the body once), and the changeset line is a one-line edit."
        }
      ],
      "out_of_scope_findings": [
        "carrier: #25956 (merged) — main's dogfood battery is red on test/authz-probe-blind-spot.test.ts, row packages/runtime/src/domains/i18n.ts: control handleI18nRequest expected 3, got 4. Neither file differs between origin/main and 12daab9542. Noted, not filed. Dedupe words: authz-probe-blind-spot, handleI18nRequest, i18n census, positive control moved.",
        "carrier: the D5 PR (next PR on #25792) — First readings 2 and 6 were not measured here, because they belong with D5 and the HotCRM lane. Noted, not filed."
      ]
    }

    Generated by Claude Code

  5. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed (D2)

    PR #25986 merged at 2026-10-26T08:58Z by the merge queue as 3446d43d40 on main (Part of #25792; ACCEPT 6411478575, contract review 6411460417).

    Reading Probe Result
    Queue membership git ls-remote origin 'refs/heads/gh-readonly-queue/main/pr-25986-*' 0 refs (left the queue)
    origin/main by content git grep -c DEPENDENCY_MISSING origin/main -- packages/metadata-core/src/protocol-handshake.ts present
    Ancestry git merge-base --is-ancestor 3446d43d40 origin/main holds

    Generated by Claude Code

  6. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 25792,
      "status": "done",
      "branch": "claude/project-thread-78l2jg",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/26054",
      "session": "session_01NndsH9Qm7k6AS2i2fWbjNA",
      "premise_still_valid": true,
      "summary": "PR #26054 is draft, head 10c01a4181, Part of #25792, 17 files, +885/-41. It delivers card items 4 and 5 (ADR-0141 D5) and the two #25986 review notes, on a branch reset to origin/main 3518664cb6. Item 4: SchemaRegistry.installPackage builds one co-owner map (coOwnersOf), read by both the namespace gate and refuseCoOwnedObjectNameCollision. Its sources are artifact (unchanged); dependency (P declares an installed X holding N, or a sitting owner of N declares P, so the relation reads from either end); and sibling (S and P both directly declare one installed package holding N). DUPLICATE_ARTIFACT_OBJECT_NAME is worded per source and carries coOwnership. NAMESPACE_CONFLICT names the declaration as the fix. Item 5: the new @objectstack/core orderHydrationBatch sorts through resolvePluginOrder with optional edges; cycle-internal edges are dropped, the members keep source order and are logged, and nothing throws. Both the sys_packages hydrate (row = node) and the install-local kernel:ready rehydrate (ledger entry = node; a declared id resolves to the entry carrying it) use it. Review notes: an undecided union with a readable installed version now reports unreadable-range, and install-local no longer counts the wrapper id as delivered (DeclaredDependencyCheck.deliveredIds removed; unreleased). Docs: app-dependencies.mdx and the plugin-spec callout. Pushing: the first push was refused by check:commit-card-trailers because the unpublished commits carried `Part of #25792`. I reworded them with a soft reset onto the merge base before the first push, so no published history was rewritten. The remote branch had been deleted after #25986 landed, so no force push was needed.",
      "tests": "Red first, on main: the registry-artifact-co-ownership.test.ts D5 describe had 5 failed / 10 passed (the sixth case is a negative control); hydration-dependency-order.test.ts (service-package, real SchemaRegistry) had 2 failed; marketplace-install-local-rehydrate-order.test.ts had 2 failed; the found-label case and the wrapper-id case had 1 failed each. All are green after wiring. Package suites after a full build (VERDICT command-exit 0, 119/119 tasks), all typecheck exit 0: core 120 files / 2774 tests; objectql 470 / 8748; service-package 10 / 127 (re-run on the final head 10c01a4181); cloud-connection 58 / 732; metadata-core 20 / 818. Ablations (scripts/ablation-replace.mjs, every restore blob == HEAD): parent arm removed, 3 failed; reverse arm disabled, 2 failed (either end, upgrade); sibling arm removed, 1 failed; sibling arm reading any shared declared id instead of an installed parent holding N, 1 failed; sys_packages hydrate in list order, 1 failed; install-local rehydrate in listed order, 2 failed; cycle edges kept, 1 failed; found-label fix reverted, 1 failed.",
      "gates": "dispatch-gates --commands derived 106 families, each run with its exit code captured before any pipe; `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran d5-gates-ran.txt` exits 0 with '106 derived, 106 run, 0 NOT-MEASURED, 0 UNRUN'. One family found a real miss on first run: check-registry-log-declared exited 1 because the service-package suite now builds a SchemaRegistry. It was fixed by adding OS_REGISTRY_LOG to the vitest.config.ts env (commit 10c01a4181) and now exits 0. Re-deriving on 10c01a4181 gives the same 106 commands. Scoped eslint on the 13 touched .ts files: exit 0. check:nul-bytes: exit 0. CI check-runs are not cited because the PR was just opened; the seat holds its events.",
      "dogfood_battery": "968c670094 (the final head 10c01a4181 differs only by the service-package vitest.config.ts env line) · os-verify-lock: VERDICT command-exit 1 · held the lock 1153s · Test Files 1 failed | 295 passed | 3 skipped (299) · Tests 1 failed | 2522 passed | 18 skipped (2541). The one failure is test/authz-probe-blind-spot.test.ts > packages/runtime/src/domains/i18n.ts: control handleI18nRequest expected 3, got 4. This is main's red since #25956, the same row reported on #25986, still present at origin/main 3518664cb6. This PR touches neither file.",
      "mcp_calls": "0 — no MCP GitHub calls",
      "api_writes": "2 — relay strokes: (1) pr_create via fleet-write repository_dispatch (request fw-20261026T101417Z-1cfc41, run 40305827913 success, read-back 8289/8289 bytes identical) creating #26054; (2) this os-dev-report comment on #25792 via post-stamped.mjs. Plus git push (not REST). Write budget 3/3: push, pr_create, report. Reads only: REST GETs of #25792, the review comment 6411460417 and the new PR (draft true, assignee os-project-manager).",
      "open_questions": [],
      "out_of_scope_findings": [
        "carrier: #25956 (merged) — main's dogfood battery is still red on test/authz-probe-blind-spot.test.ts, row packages/runtime/src/domains/i18n.ts (handleI18nRequest expected 3, got 4) at origin/main 3518664cb6. Noted, not filed. Dedupe words: authz-probe-blind-spot, handleI18nRequest, i18n census, positive control moved.",
        "carrier: ADR-0141 Open items 2 — First reading 2, from the code and not instrumented. ObjectQLPlugin's kernel:ready seals and the install-local ledger rehydrate both run on kernel:ready in hook-registration order, and the install-local plugin declares no ordering edge against ObjectQLPlugin. Each hydrate is now sorted within itself. One case stays open by design: two siblings in sys_packages (hydrated at service-package start) whose parent is only in the install-local ledger (hydrated later at kernel:ready); the second sibling is refused NAMESPACE_CONFLICT. Noted, not filed.",
        "carrier: the HotCRM lane (hotcrm#2271) — First reading 6 (the type of the artifact `os build` writes for a standalone module) was not measured here. The D5 companion precondition holds at hotcrm d91c7b0: service, marketing and revenue each declare app.objectstack.hotcrm. Noted, not filed."
      ]
    }

    Generated by Claude Code

  7. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed

    PR #26054 merged at 2026-10-26T11:37Z by the merge queue as be7897799b on main (Part of #25792; ACCEPT 6413749310).

    Reading Probe Result
    Queue membership git ls-remote origin 'refs/heads/gh-readonly-queue/main/pr-26054-*' 0 refs
    origin/main by content git cat-file -e origin/main:packages/core/src/hydration-order.ts present
    Ancestry git merge-base --is-ancestor be7897799b origin/main holds

    State: card stays open (Part of), pm:dispatched stays.


    Generated by Claude Code

  8. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim amendment: PM loop round 5 · card thread · item 3 (the Console refusal text)
    Session: session_01R1q2xdtYgU6FKrZ7LgjPCC (the seat); the card thread is session_01NndsH9Qm7k6AS2i2fWbjNA (same thread as D2 #25986 and D5 #26054)
    Account: os-project-manager (the seat's linked user; the card's assignee)
    Branch: claude/project-thread-78l2jg (the card thread's harness-pinned branch, restarted from origin/main after #26054 merged)
    Domain: domain:server
    Seat: domain:server#1
    Ruling-ref: #25403 B′ (6369704387): "one runtime card for the pre-install check … with the console's refusal text". The original claim routed item 3 to objectui's card. objectui merged into this repo's ui/ on 2026-10-26, and no objectui card was filed (dedupe below), so item 3 now rides this card.
    Scope: item 3 only. The Console renders the 422 DEPENDENCY_MISSING refusal at its install surfaces in the author's terms (「请先安装 HotCRM 6.x」), built from the refusal's own facts (one line per dependency_missing issue: id, range, what was found), with i18n keys in every locale the Console ships.
    File surface: ui/packages/app-shell/src/console/marketplace/** (marketplaceApi.ts, MarketplacePackagePage.tsx, MarketplacePage.tsx, MarketplaceRowActions.tsx), ui/packages/app-shell/src/views/metadata-admin/PackagesPage.tsx, ui/packages/i18n/src/locales/*.ts (new keys only), their tests, one changeset. ⛔ No packages/** server change, ⛔ no packages/spec/**, ⛔ no docs/adr/** (stop on breach; explain in the report)
    Container & model: M, mode:thread, model and effort as the thread was opened
    Clause-②: no (a client rendering of an existing refusal; no server behavior changes)
    Metadata-protocol: none
    Responsibility: n/a (ADR-0141 runtime card, owed by landed work PR #25717)
    Dedupe: objectui issues "Console install refusal text missing dependency ADR-0141" → 0; objectstack open "Console install refusal text DEPENDENCY_MISSING 请先安装" → only this card
    Premise: holds (read 2026-10-26T11:39Z at origin/main be7897799b: ui/** holds no DEPENDENCY_MISSING / dependency_missing reader; the server side landed in #25986)
    Serial constraints cleared: no open PR touches the marketplace files or PackagesPage.tsx; #25987, #26040, #26044, #26045, #26048 and #26055 add keys to ui/packages/i18n/src/locales/*.ts (additive; whichever lands second merges main)
    Remains on the card after this slice: item 6 (#26000, domain:spec)

  9. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 25792,
      "status": "done",
      "branch": "claude/project-thread-78l2jg",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/26123",
      "session": "session_01NndsH9Qm7k6AS2i2fWbjNA",
      "premise_still_valid": true,
      "summary": "PR #26123 is draft, head f76c765089, Part of #25792, 21 files, +734/-10. It delivers card item 3 (claim amendment 6415110715) on a branch restarted from origin/main be7897799b, with origin/main merged in once (merge base f6c619329a). The Console now draws the 422 DEPENDENCY_MISSING refusal at the three install surfaces that go through install-local: the package page Install, the catalog page org install on a self-hosted runtime, and Setup -> Packages -> Import. Each shows a heading saying nothing was installed, then one line per details.unmet[] entry built from its own facts (dependency id, range, found, installedVersion), e.g. zh-CN '• 请先安装 app.objectstack.hotcrm 6.x。' and en '• Install com.acme.billing ^2.1.0 first. Version 1.4.0 is installed.'. New module console/marketplace/dependencyMissing.ts: readDependencyMissing is all-or-nothing, so an unknown found or an incomplete entry keeps the server message; dependencyMissingText; useDependencyMissingText. installLocal() now carries details on its error; PackagesPage apiJson carries code and details (no existing caller reads either). The org strip and the import note gained whitespace-pre-wrap. Keys marketplace.install.dependencyMissing.{heading,absent,outOfRange,unreadableRange,unreadableVersion,unreadableDeclaration} are in all ten locale packs. Setup -> Packages reads them through the hook because its engine.* table is en/zh only. The dependency is shown by package id, not a display name such as 'HotCRM': the refusal's facts carry no display name, and adding one would be a server change outside this claim. Two departures from the listed surface, neither in packages/**, packages/spec/** or docs/adr/**: (1) scripts/sdui-manifest.record.json. The i18n locale packs are registry sources, so check-sdui-manifest exited 1 until the recorded digest was regenerated with node scripts/gen-sdui-manifest-node.mjs; sdui.manifest.json is byte-identical. It was regenerated again to resolve the merge-main conflict on that line. Every other open PR that adds locale keys will hit the same line. (2) The changeset was first graded minor. CI Check Changeset refused minor under Metadata-protocol: none, so it is now patch for @object-ui/app-shell and @object-ui/i18n (commit f76c765089). Declarations stay as claimed: Clause-②: no, Metadata-protocol: none.",
      "tests": "4 new files, 12 tests. Red first, with the wiring reverted (the source patch removed and re-applied): 5 failed / 7 passed. The passing ones are the controls and the pure reader/text cases. The 5 that failed: installLocal carries details; the package page lines; the org-install lines; the import door in zh and in en. After wiring: 12/12. Ablations (scripts/ablation-replace.mjs, after commit, every restore blob == HEAD), all red: hook removed at the import door, 2 failed; hook removed at the org install, 1 failed; details dropped in installLocal, 2 failed; details dropped in apiJson, 2 failed; whitespace-pre-wrap dropped on the org strip, 1 failed. Wider runs under os-verify-lock: marketplace/ + views/metadata-admin/ + packages/i18n gave Test Files 727 passed, Tests 8276 passed / 14 skipped (VERDICT command-exit 0). After merging main: the 4 new files + packages/i18n gave 91 files, 1365 passed / 13 skipped. app-shell tsc --noEmit exit 0. The ui i18n checks all exit 0: check-i18n-call-site-keys (re-run after the merge), en-drift, dead-keys, designer-table-parity. The first full build exited 2 on a TS18049 null narrowing in dependencyMissing.ts; fixed before commit. One local note: ui/scripts/check-changeset-presence.mjs exits 1 because it still reads ui/.changeset, while @object-ui changesets now live in the root .changeset (as #26037 did). It is not a CI gate here, and CI Check Changeset reads the root.",
      "gates": "dispatch-gates --commands derived 34 families on the first commit and 46 after the sdui record commit. Each was run with its exit code captured before any pipe. First sweep: check-sdui-manifest exit 1 (a real miss, fixed as above), all others exit 0. After merging main, the full 46 were re-derived and re-run: node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran i3-ran2.txt exits 0 with '46 derived, 46 run, 0 NOT-MEASURED, 0 UNRUN', all 46 exit 0. Scoped eslint over the 9 touched app-shell files and the 10 locale packs: red first (exit 1, 'waitFor' unused import in a new test), then exit 0 with 0 errors (only warnings already in those files). CI on head f76c765089: 61 check-runs, 54 success, 7 skipped, 0 failed. The one earlier red, Check Changeset on 2a7e4c4776 (the minor grade), is fixed by f76c765089.",
      "dogfood_battery": "Not run locally: the diff touches only ui/** client code, the locale packs, one changeset and the sdui record. CI 'Dogfood Regression Gate' is success on f76c765089.",
      "mcp_calls": "0 — no MCP GitHub calls",
      "api_writes": "2 — relay strokes: (1) pr_create via fleet-write repository_dispatch (request fw-20261026T125503Z-cbe662, run 40326238152 success, read-back 5299/5299 bytes identical) creating #26123, assignee os-project-manager. An earlier dispatch was refused locally with zero writes because pr_create takes no 'draft' key; the PR opened as draft anyway. (2) This os-dev-report comment on #25792 via post-stamped.mjs. Plus 2 git pushes (not REST). Reads only: REST GETs of the PR, its check-runs and the Check Changeset annotations.",
      "open_questions": [],
      "out_of_scope_findings": [
        "carrier: #25831 (ui/.changeset aftercare) — ui/scripts/check-changeset-presence.mjs still reads ui/.changeset, so locally it reds any ui change whose changeset sits in the root .changeset. Noted, not filed. Dedupe words: changeset-presence, ui/.changeset, root changeset, changesetConfigPath.",
        "carrier: the sdui manifest record — every PR that edits a ui registry source (including the locale packs) moves scripts/sdui-manifest.record.json's single digest line. Concurrent PRs (#25987, #26040, #26044, #26045, #26048, #26055 add locale keys) will each conflict there and must regenerate after merging main. Noted, not filed.",
        "carrier: ADR-0141 D2 refusal facts — details.unmet[] carries no display name for the dependency, so the Console says 'app.objectstack.hotcrm 6.x' rather than 'HotCRM 6.x'. A display name would need a server change. Noted, not filed."
      ]
    }

    Generated by Claude Code

  10. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed

    PR #26123 merged at 2026-10-26T18:21Z by the merge queue as 934810cc57 on main (Part of #25792; ACCEPT 6417558550).

    Reading Probe Result
    Queue membership git ls-remote origin 'refs/heads/gh-readonly-queue/main/pr-26123-*' 0 refs
    origin/main by content git cat-file -e origin/main:ui/packages/app-shell/src/console/marketplace/dependencyMissing.ts present
    Ancestry git merge-base --is-ancestor 934810cc57 origin/main holds

    State: card stays open (Part of), pm:dispatched stays.


    Generated by Claude Code

  11. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim amendment: PM loop round 8 · card thread · item 6 (ADR-0141 D8 items 2 and 3)
    Session: session_01R1q2xdtYgU6FKrZ7LgjPCC (the seat); the card thread is session_01BkRZmjDUobsYmkGEjwjH9U (a new thread; the item 1–5 and item 3 thread has wrapped)
    Account: os-project-manager (the seat's linked user; the card's assignee)
    Branch: claude/project-thread-tzosbt (the card thread's harness-pinned branch; it names no issue, so this claim is the identity record)
    Worktree: the card thread's own container checkout
    Domain: domain:server
    Seat: domain:server#1
    Ruling-ref: #25403 B′ (6369704387) and ADR-0141 §D8 as merged (afe6ab9ccc): the runtime card owes "a stated fate for optional" and "a stated fate for AppManifestSchema.dependencies … decided with a measured reader count". A fate that needs a maintainer call is reported needs_decision on the four axes, not picked.
    Scope: item 6 only. Items 1, 2, 4, 5 landed in #25986 and #26054, item 3 in #26123, and item 7 is on main in content/docs/deployment/app-dependencies.mdx.
    File surface: packages/spec/src/marketplace/package-version.zod.ts (PackageDependencySchema.optional), packages/spec/src/system/app-install.zod.ts (AppManifestSchema.dependencies), their tests, the ADR-0087 conversion layer where a retirement needs it, the regenerated content/docs/references/** pages, a dated addendum line in ADR-0141 §D8, and a changeset. ⛔ No ResolvedPackageDependencySchema. ⛔ No snapshot conversion (D8 item 1, its own card).
    Container & model: S, mode:thread, default model and effort (set at thread open)
    Clause-②: measured on the diff — a retirement is no (narrowing) with the narrowing arm's evidence. The seat runs a CONTRACT_REVIEW_TIER review before landing (packages/spec/src/**).
    Metadata-protocol: measured on the diff
    Responsibility: two published dependency shapes have no stated meaning beside the record D2 reads, so an author or AI can declare a dependency the runtime never honours (ADR-0141 §D8)
    Thread-read: 6424112245
    Premise: holds (read 2026-10-27T00:51Z at origin/main 6390fe52ce: card open, pm:dispatched, assignee os-project-manager; the seat's first reading found no reader of AppManifestSchema outside packages/spec and the generated reference docs)
    Serial constraints cleared: the dev re-measures open PRs on the spec files it touches and runs git merge-tree against any overlap before pr_create.

  12. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 25792,
    "status": "done",
    "branch": "claude/project-thread-tzosbt",
    "pr": "#26401",
    "session": "session_01BkRZmjDUobsYmkGEjwjH9U",
    "premise_still_valid": true,
    "summary": "PR #26401 (draft, head c65c564, Fixes #25792, the card's last item) decides ADR-0141 D8 items 2 and 3 on the measured reader count: both retire at once. PackageDependencySchema.optional and AppManifestSchema.dependencies are retiredKey() tombstones with prescriptions pointing at manifest.dependencies; both keys are in RETIRED_KEYS_BY_MAJOR[18]; one D3 semantic entry dependency-unread-spellings-retired plus its STEP18_RATIONALE fragment; no D2 conversion (neither shape is a stack collection member, the kernel/Manifest:capabilities precedent); acceptRetiredDefaultResidue not adopted (no producer emits either default). Measured at origin/main 6390fe5: 0 readers and 0 writers of either key outside packages/spec (packages/, ui/, examples/, apps/), no import of AppManifest outside packages/spec, and 0 in HotCRM at 9464673, whose three modules declare the record (live control). ADR-0141 D8 gains a dated addendum with the counts (Tier H, so the PR needs the maintainer's approval). Generated artifacts and the two reference pages are regenerated; three spec fixtures that authored a retired key now write the live shape. Changeset @objectstack/spec minor, BREAKING, FROM-TO table, marker registered dependency-unread-spellings-retired. Declarations as ordered: Clause-② no (narrowing), Metadata-protocol narrowed. One judgement for the contract review: no snapshot carried today changes (publish uploads the record shape), but the addendum fixes D8 item 1's future projection to packageId and versionRange only.",
    "tests": "Red first: the new packages/spec/src/marketplace/dependency-spellings-retirement.test.ts on main 6390fe5 gave 12 failed | 2 passed (the passing two are the matcher anti-vacuity case and the no-conversion case); its tree-scoped absence pin named exactly five authoring sites, all in packages/spec (the two schemas and three tests). Green: that file plus the three edited tests, src/marketplace/ and scripts/step18-rationale-merge.test.ts gave 10 files / 124 tests passed; the file under --project repo 14 passed. pnpm --filter @objectstack/spec test: Test Files 727 passed, Tests 21998 passed | 1 todo (os-verify-lock VERDICT command-exit 0). pnpm --filter @objectstack/spec typecheck: exit 0 (the two @ts-expect-error tombstone lines compile under tsconfig.test.json). No separate ablation: the change is the tombstones, and the red-first run on main is the run without them.",
    "gates": "dispatch-gates --commands derived 118 families at 6548121 and the same 118 at c65c564. Each ran with its exit code captured before any pipe. First pass: 107 exit 0; 10 exit 3 and 1 exit 1 (check:generated naming a stale api-surface, check:dts-closure) while a concurrent dogfood closure build rewrote packages/spec/dist and packages/lint/dist; all 11 re-ran on the settled build and exited 0. One real miss: check:cross-package-test-inputs exit 1, the new absence pin escapes its package but was not in vitest.repo-tests.json; fixed in c65c564, re-run exit 0. dispatch-gates --repo objectstack-ai/objectstack --ran (record with exit codes): exit 0, '118 derived, 118 run, 0 NOT-MEASURED, 0 UNRUN', 'a DERIVED zero'. Scoped eslint --no-inline-config over the 9 touched .ts files exit 0; check-nul-bytes exit 0; spec check:generated 14/14 up to date after --fix regenerated check:docs only. Open-PR overlap: #26159 and #26138 share packages/spec/authorable-surface/system.json; a driver-free merge-tree in a throwaway bare clone answers exit 0 with a tree id for both. CI check-runs not cited: the PR was just opened and the seat holds its events.",
    "dogfood_battery": "c65c5645cb · os-verify-lock VERDICT command-exit 1 · held the lock 1147s · Test Files 2 failed | 297 passed | 3 skipped (302) · Tests 10 failed | 2521 passed | 18 skipped (2549). Both failures are main's: test/authz-conformance.test.ts (6 tests, 'packages/rest/src/rest-route-ledger.ts: docblock says 80 rows, the table holds 81') and test/authz-probe-blind-spot.test.ts (4 tests, control 'this.routeManager.register(' in rest-server.ts expected 62, got 63). The PR touches nothing under packages/rest or packages/qa; the last change to both rest files in the base is 6a2866a (#26361, merged 2026-10-26T23:58Z), and origin/main b33dd25 has no later change to them.",
    "mcp_calls": "4 — mcp__github__issue_read x4 (get and get_comments on #25792 and on #26000); no write tool",
    "api_writes": "2 — relay strokes: (1) pr_create via fleet-write repository_dispatch (request fw-20261027T021005Z-bb0ef5, run 40436852678 success, read-back 12198/12198 bytes identical, assignee os-project-manager) creating #26401; (2) this os-dev-report comment on #25792 via post-stamped.mjs. Plus 4 git pushes (not REST). Write budget 3 kinds: push, pr_create, report. Reads only otherwise: REST GETs of claim comment 6430963835, the open-PR list and files, and #26401.",
    "open_questions": [],
    "out_of_scope_findings": [
    "class: a · reach: the full dogfood battery on main (CI's scheduled and push runs) · evidence: test/authz-conformance.test.ts 'rest-route-ledger.ts: docblock says 80 rows, the table holds 81' and test/authz-probe-blind-spot.test.ts 'this.routeManager.register( expected 62, got 63' at base 6390fe5, untouched by this PR · carrier: #26361 (merged; added the route) · noted, not filed; for the main-red convention · dedupe words: rest-route-ledger docblock 80 rows, authz-probe-blind-spot routeManager.register, approval lock read route",
    "carrier: cloud — not measured: how the control plane parses sys_package_version.manifest_json. If it parses with PackageManifestSchema and stores array entries carrying optional, the new tombstone refuses them on the next spec bump. Per the cloud ruling, a cloud-lane card is the seat's to file · dedupe words: manifest_json parse, PackageManifestSchema optional, cloud snapshot dependencies",
    "carrier: #25794 — D8 item 1's record-to-array conversion must now project packageId and versionRange only (no optional); the ADR-0141 D8 addendum says so · noted, not filed",
    "carrier: #26000 — overlap: its 6b (AppManifestSchema.dependencies) and the optional part of 6a are delivered by #26401; the rest of 6a (retiring PackageDependencySchema whole) still collides with D8 as the spec seat's retriage comment 6430707402 says · for triage's re-scope · noted, not filed",
    "carrier: none — AppManifestSchema as a whole and IAppLifecycleService have no reader or implementer outside packages/spec (dormant; outside item 6) · in the PR's Acceptance notes only"
    ]
    }


    Generated by Claude Code

  13. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Contract review

    Served-tier: CONTRACT_REVIEW_TIER
    Head-sha: c65c5645cbeb0a7137e73bedd962003e40bc71e3
    Local-runs: none
    Scope: packages/spec/src/marketplace/package-version.zod.ts, packages/spec/src/migrations/entries/retired-keys/18.marketplace__PackageDependency__optional.ts, packages/spec/src/migrations/entries/retired-keys/18.system__AppManifest__dependencies.ts, packages/spec/src/migrations/entries/semantic/18.dependency-unread-spellings-retired.ts, packages/spec/src/migrations/registry.ts.template, packages/spec/src/system/app-install.zod.ts, docs/adr/0141-inter-app-dependency-declared-checked-before-install.md + packages/spec/src/shared/retired-key.ts, packages/spec/src/migrations/entries/retired-keys/18.kernel__Manifest__capabilities.ts, packages/spec/src/marketplace/dependency-spellings-retirement.test.ts, packages/spec/src/kernel/package-dependency-dual-source.test.ts, packages/spec/src/system/app-install.test.ts, packages/spec/src/contracts/app-lifecycle-service.test.ts, packages/spec/authorable-surface/{marketplace,system}.json, packages/spec/authorable-defaults/{marketplace,system}.json, packages/spec/vitest.repo-tests.json, scripts/cross-package-test-inputs.mjs, packages/core/src/artifact-packages.ts, packages/cli/src/commands/package/publish.ts, packages/metadata-core/src/protocol-handshake.ts, .changeset/25792-retire-dependency-spellings.md, content/docs/references/marketplace/package-version.mdx, content/docs/references/system/app-install.mdx. On the narrowing face no path of the diff is a remainder.

    Inputs read: card #25792 body and all 11 comments (claim 6409455733, amendments 6415110715 and 6430963835, dev reports 6411207761 / 6413551415 / 6416975195 / 6432027506, landed records); PR #26401 body, 19-file list, net diff origin/main...pr-26401; #26000 body and retriage ask 6430707402; #25794 body; the check-runs on the head; HotCRM fetched to 9464673.

    ① Derived judgments

    1. PackageDependencySchema.optional → retiredKey() tombstone — right. The schema is a non-strict z.object under lazySchema, so a bare deletion would have been a silent strip (the skill §2 rule); the tombstone makes the removal loud in both channels. Accept-set change: any authored value, the old default false included, is refused at ['optional'] (inside a snapshot at ['dependencies', i, 'optional']) with the prescription; absence stays absence and the parsed entry no longer materializes optional: false. Published-type change: PackageDependencyParsed loses optional; the z.input type carries the retired mark (tsc channel, compiled by tsconfig.test.json include: ["src/**/*"], so the @ts-expect-error pins are not phantom checks).
    2. AppManifestSchema.dependencies → retiredKey() tombstone — right, same reasoning (non-strict object). Every value is refused, [] and the record spelling { id: range } included (the test pins that the record spelling is not silently re-read at this key); absence stays absence, AppManifestParsed loses dependencies (previously materialized []). D8 item 3 offered projection-or-retirement "decided with a measured reader count"; zero readers makes projection a shape nobody reads, so retirement is the right arm.
    3. Prescriptions — right. Both follow the five conventions: fully-qualified key in backticks first, was removed in @objectstack/spec 18 (ADR-0049 enforce-or-remove, ADR-0141 D8) (the house spelling, 34 sibling sites spell 18 (), a dash clause on why it was inert, Delete the key. plus the live mechanism (manifest.dependencies, the D2 check). No os migrate meta sentence — correct, because no conversion covers either surface; adding it would have been the false claim the class pin forbids.
    4. No D2 conversion — right. Neither a marketplace snapshot nor an app install manifest is a stack collection member, so a conversion is a transform with no seam that runs; the cited precedent 18.kernel__Manifest__capabilities.ts says exactly this and was read. The new test pins ALL_CONVERSIONS names neither surface.
    5. acceptRetiredDefaultResidue not adopted — right on the measured population, with one unmeasured arm (③.1). The residue stage exists for a materialized default in already-built artifacts. The only producer that could materialize optional: false is a parse of PackageManifestSchema over an array-shaped dependencies; at the head no file outside packages/spec imports PackageManifestSchema, PackageVersionSchema or PackageDependencySchema (only prose: a comment in packages/core/src/artifact-packages.ts, a comment in ui/.../waitForServedApp.ts, CHANGELOGs), publish.ts uploads dist/objectstack.json as it is, and the record shape is what every measured artifact carries (artifact-packages.ts#declaredDependencyIds treats an array as declaring nothing). So no emitted population exists in this repository or HotCRM. AppManifestSchema is parsed nowhere outside spec, so dependencies: [] was never emitted.
    6. Measured counts re-measured at the head — they hold. Outside packages/spec across packages/, ui/, examples/, apps/: 0 readers of either key, 0 imports of AppManifest/AppManifestSchema, 0 writers of optional beside versionRange. HotCRM at 9464673 (fetched; the local checkout was at a4b3560, re-measured at both): 0 of either; src/{marketing,revenue,service}/index.ts declare { 'app.objectstack.hotcrm': '^6.0.0' } in the record — the live control. The dependencies: ['com.objectstack.plugin-auth'] arrays in packages/apps/{account,setup,studio}/src/index.ts are type: 'plugin' manifest headers (the kernel plugin-validator string[] shape), not AppManifest, so they are not writers of the retired key.
    7. Registration — right. Two retired-key entries named per entries/README.md (major, category, def and key joined by __, e.g. 18.marketplace__PackageDependency__optional.ts), values spelled exactly as the authorable-surface rows minus [RETIRED]; one D3 semantic entry per family with non-empty reason and acceptanceCriteria; a STEP18_RATIONALE fragment with a unique order: 110; registry.ts is generated and gitignored, so correctly absent from the diff. Changeset marker registered dependency-unread-spellings-retired names an id this diff adds (Check Changeset: success).
    8. Generated surfaces — right for the tombstone route. authorable-surface rows gain [RETIRED], the two authorable-defaults rows leave, both reference pages regenerated; api-surface/, json-schema.manifest/ and authorable-surface.base.json do not move, which is the expected reading for a key-level tombstone (the def survives). The liveness ledger walks no marketplace/PackageDependency or system/AppManifest shape (no such ledger file), so no row to flip (Spec property liveness: success).
    9. Fixtures — right. package-dependency-dual-source.test.ts keeps ['optional','packageId','versionRange'] as the cloud-side key set with a note that optional stays as a tombstone (a tombstone keeps the key in the walked shape); the two other fixtures drop the key. The absence pin is tree-scoped over packages/, examples/, skills/, content/, scripts/, every root declared under @objectstack/spec in scripts/cross-package-test-inputs.mjs and in turbo's test:repo inputs, and the file is in vitest.repo-tests.json. ui/ and apps/ are outside the pin's declared radius (the bound is stated in the pin); they are covered by the grep measurement above, not by the pin.
    10. ADR-0141 D8 addendum — right as an addendum, dated, carrying the counts and both fates, and correcting the projection to packageId + versionRange only. The original D8 paragraph above it still reads "optional = false"; the addendum below governs. Note: docs/adr/** is Tier H, so the maintainer's approving review is owed on this PR independently of this record.

    ② Semver level

    @objectstack/spec minor with the BREAKING banner, the FROM → TO table and the one-line fix (Delete the key.) — matches what the diff publishes: two authorable keys tombstoned, two parsed types narrowed, no export removed. major is refused in the launch window (check-changeset-no-major), so minor + BREAKING is the correct spelling. Clause-②: no (narrowing) and Metadata-protocol: narrowed appear line-initial in both the PR body and the changeset body and match the kind: a key tombstoned is narrowed, and the level is at least minor on the moved package. The narrowing arm's evidence is present: red-first on main 6390fe52ce (12 failed / 2 passed), the tree-scoped absence pin, and the dogfood battery on the head (2 failed files, both packages/rest ledger counts anchored at #26400 and red on main itself; this PR touches nothing under packages/rest or packages/qa).

    ③ Boundary flags

    1. Cloud sys_package_version.manifest_json reader — unmeasured; escalate, not a condition of this PR. If objectstack-ai/cloud parses snapshots with PackageManifestSchema from the published spec and persists the parsed output, stored rows would carry optional: false and the tombstone would refuse them on the next spec bump — the feat(spec): retired-defaulted-key tolerance — the retired default parses as inert residue and strips; non-default values keep the loud refusal (#12497 class rule) #12840 class, whose ruling was recorded on objectstack-ai/cloud#1685. ADR-0141 D8 item 1: one record-to-array conversion so marketplace snapshots carry dependencies in PackageDependencySchema shape — read cloud's manifest_json reader first #25794's step 1 already owes a reading of that parser; the seat should widen that reading to "does the parser persist array entries carrying optional", or file it as its own cloud-lane card per the cloud ruling, and, if yes, land acceptRetiredDefaultResidue(PackageDependencySchema, { optional: false }) in a cloud-lane child before the bump reaches cloud. Not a condition here because every measured snapshot carries the record shape, so no array entry exists to have materialized the default.
    2. ADR-0141 D8 item 1: one record-to-array conversion so marketplace snapshots carry dependencies in PackageDependencySchema shape — read cloud's manifest_json reader first #25794's body is now stale on item 2 ("optional = false"): the addendum fixes the future projection to packageId and versionRange only. The seat amends ADR-0141 D8 item 1: one record-to-array conversion so marketplace snapshots carry dependencies in PackageDependencySchema shape — read cloud's manifest_json reader first #25794's body before it dispatches; the ADR governs, the card follows.
    3. spec: retire the two unread dependency spellings, PackageDependencySchema (with optional) and AppManifestSchema.dependencies, now that ADR-0141 D2 judges manifest.dependencies alone #26000 overlap — for triage's re-scope. 6b and the optional half of 6a are delivered by this PR; the rest of 6a (retiring PackageDependencySchema whole) collides with D8 and ADR-0141 D8 item 1: one record-to-array conversion so marketplace snapshots carry dependencies in PackageDependencySchema shape — read cloud's manifest_json reader first #25794, as retriage ask 6430707402 says. After this lands, spec: retire the two unread dependency spellings, PackageDependencySchema (with optional) and AppManifestSchema.dependencies, now that ADR-0141 D2 judges manifest.dependencies alone #26000 is either closed as superseded or re-scoped to what remains, by triage.
    4. "No snapshot carried today changes" — verified. publish.ts uploads the compiled artifact; D2 reads the manifest record only; no reader of PackageManifestSchema exists outside spec.
    5. Report 6432027506 open_questions: empty — nothing further to answer. Its out-of-scope findings 1–5 are covered by ②'s dogfood reading and flags 1–3 above; the dormant AppManifestSchema / IAppLifecycleService (no implementer) is noted, not changed, and outside item 6.
    6. Check-runs on the head at review time (last poll 2026-10-27): 36 success, 9 skipped, 0 failure, 8 in progress (Build Core, Test Core 3/4/5/6/7, Type Check · workspace, Type Check UI); the seat re-reads the head's check-runs before landing, as the queue guard requires every check green. Governed Surface Queue Guard, Check Changeset, Spec property liveness, Dogfood Regression Gate and every Lint gate are green. No red is attributable to this PR; the two dogfood files red on main (dogfood: main's Dogfood Regression Gate (1/3) and (3/3) are red since #26361 — the authz census still pins rest-route-ledger.ts at 80 rows and rest-server.ts this.routeManager.register at 62; #26361 made them 81 and 63 #26400) name packages/rest ledger counts only.

    Implemented-by: claude/project-thread-tzosbt
    Reviewed-by: session_01R1q2xdtYgU6FKrZ7LgjPCC

    VERDICT: PASS

  14. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed

    PR #26401 merged at 2026-10-27T03:16Z by the merge queue as 436d284069 on main (Fixes #25792; ACCEPT 6432355909).

    Reading Probe Result
    Queue membership git ls-remote origin 'refs/heads/gh-readonly-queue/main/pr-26401-*' 0 refs
    origin/main by content git cat-file -e origin/main:packages/spec/src/migrations/entries/semantic/18.dependency-unread-spellings-retired.ts present
    Ancestry git merge-base --is-ancestor 436d284069 origin/main holds

    State: this card closed by Fixes (completed); pm:dispatched removed.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

area:devpathThe road — create, dev, verify, publish/install, connect an agent, iteratedomain:serverDomain lane — seat card indexed by label:pm:seatenhancementNew feature or requestpriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions