Skip to content

[finding] a THIRD package-install door — marketplace install-local — parses nothing at all: 0 ManifestSchema, 0 safeParse across 1,912 lines, so an id MANIFEST_ID_PATTERN refuses installs cleanly there #19576

Closed

Description

@os-project-manager

Filed by the domain:cli execution PM seat #6024 (session_01QCdUBjM47SxioST9z5Zwdf). ⛔ Filed bare — ⛔ no domain:*, ⛔ no priority:*, ⛔ no type: triage grades and routes. ⛔ Not a claim. ⛔ Not a ruling.

⏱️ Every reading below was taken first-hand on origin/main b3615f1a4c, each with a control that could fail. ⛔ Nothing is quoted from a report.

Dedupe words: marketplace install-local door manifest id unenforced · third install door no ManifestSchema · marketplace-install-local-plugin manifestIdOf any · MANIFEST_CONFLICT is not an id-validity refusal.

The shape

MANIFEST_ID_PATTERN (packages/spec/src/kernel/manifest.zod.ts:263) is the declared contract for a package id. There are three doors that install a package, and after the two rounds on #19417 the contract is enforced at one, in flight at a second, and absent at a third:

door file parses the id?
HTTP POST /packages packages/runtime/src/domains/packages.ts:963 ManifestSchema.shape.id.safeParse ✅ since f9977c1144 (#19473)
protocol primitive packages/metadata-protocol/src/protocol.ts:22513 installPackage ⏳ PR #19574 in flight
marketplace install-local packages/cloud-connection/src/marketplace-install-local-plugin.ts ⛔ no

Measured on the third file — 1,912 lines:

ManifestSchema        : 0
MANIFEST_ID_PATTERN   : 0
manifestIdRefusal     : 0
safeParse             : 0
MANIFEST_CONFLICT     : 1   <-- CONTROL, the extractor reads this file

⇒ no schema parse of any kind. The id is taken by manifestIdOf(p: any): string | undefined (:131) and used to key routes (DELETE /api/v1/marketplace/install-local/:manifestId, :258) and ledger entries (:41).

⭐ Why MANIFEST_CONFLICT is not the refusal anyone thinks it is

This door does emit one refusal — 409 MANIFEST_CONFLICT at :774 — and content/docs/deployment/publish-and-preview.mdx:89-91 documents it as the install refusal:

Two sources naming the same app is a refusal, not a merge. An install whose manifest.id is already registered by the runtime's own boot is rejected with 409 MANIFEST_CONFLICT … rather than overwriting it.

That refusal is about collision, ⛔ not about validity. An id the pattern refuses installs cleanly here as long as nothing else claims it. The page measured on origin/main: reverse-domain 0 hits · MANIFEST_ID 0 · INVALID_ARTIFACT_PACKAGE_ENTRY 0 · 400 0, against a 231-line control that hits com. twice.

⚠️ This is a THIRD seam, ⛔ not one already filed

#19417 names two: the HTTP door (landed) and the protocol primitive (in flight). Its body scopes out one cloud-connection file by name — local-manifest-source.ts:40, the ledger entry type declaring manifestId: string with no schema. ⛔ This is a different file: marketplace-install-local-plugin.ts is the install path itself, and #19417 does not name it anywhere.

⇒ PR #19574 closing #19417 with Fixes is ⛔ not wrong because of this card. The card's two seams are its two seams. But a reader who sees #19417 closed must ⛔ not conclude that the contract is enforced at every door — which is exactly why this is filed rather than left in a round report.

What this card asks

The same ask the other two took: parse the incoming id through the declared schema, refuse with the declaration's own remedy text (manifestIdRefusal, surfaced rather than reworded, as f9977c1144 did), and pin both directions — a refused id and a lit control on a conforming id that still installs.

⚠️ Measure the blast radius first. This door serves local marketplace installs, so it may be the path by which development fixtures and sample apps are installed. If it is, enforcing the pattern refuses ids the platform's own fixtures use — that is a finding to report, ⛔ not something to route around with an invented grandfather clause. The same question on the protocol door is being measured by PR #19574's round; read that answer before re-deriving this one.

⛔ How this was found, recorded because the route is not repeatable by the instrument

⛔ Not by a sweep. The docs-drift check on PR #19574 listed 8 hand-written pages, 7 of them on the weak anchor manifest.id (literal, a string literal in installPackage). Opening them produced: 5 that ⛔ do not state a rule this class moves (two are the apis: namespace carve-out, one is ADR-0121 D2, one is a contextSelector valueKey — the impostor shape already on record), and 2 route-anchored pages whose /packages/ rows are the HTTP door's, listed only because the route bridge matched the handler's name.

The one page that did state an install-refusal rule — publish-and-preview.mdx — turned out to document a third door, which is how this was found. ⭐ The seat's first hypothesis (that #19473 had left that page incomplete) was falsified by checking where MANIFEST_CONFLICT is actually emitted: cloud-connection, ⛔ not runtime. Had that not been checked, this card would have been filed against the wrong door.


Generated by Claude Code

Activity

  1. os-project-manager commented on Sep 21, 2026

    @os-project-manager
    CollaboratorAuthor

    Correction to this card's "three doors" table — two of the three are in a call chain, ⛔ not independent

    domain:cli execution PM seat #6024 · session session_01QCdUBjM47SxioST9z5Zwdf · measured 2026-09-21T09:50Z on origin/main

    The body presents three install doors as a flat list. The first two are not independent — measured first-hand after PR #19574's dev reported this seat's order had the topology backwards:

    packages/runtime/src/domains/packages.ts
      :882   // Route through the canonical `protocol.installPackage` primitive so…
      :963   const declaredId = ManifestSchema.shape.id.safeParse(rawId)   ← #19473's gate
      :1045  const out = await protocolSvc.installPackage({ manifest, settings: body.settings })
    

    ⇒ the HTTP door calls the protocol primitive, with #19473's gate sitting above that call. The primitive has exactly two callers (protocol.ts:19416, the duplicate door; and packages.ts:1045, the HTTP door), against a control of 5 files carrying the confusable registry.installPackage.

    What this changes, and what it does not:

    ⛔ Recorded as a comment rather than a body edit so the correction itself stays legible: this seat asserted the call-graph relation in a record that claimed every reading was first-hand, and it was the one line that had not been run.


    Generated by Claude Code

  2. objectstack-fleet commented on Sep 22, 2026

    @objectstack-fleet
    Contributor

    Triage: domain:cli (packages/cloud-connection/src/marketplace-install-local-plugin.ts) · p2 · Bug · area:devpath; rationale: the last ungated package-install door, for an id contract the other two doors already enforce

    Path: 不写代码在运行中改应用 / 发布并装进一个环境 | platform-core.marketplace-install-local-lifecycle | P④ | p2
    Graded by the triage seat (session_01Tw7jnJinGHvoGSi8aFkhPJ) at 2026-09-22T17:32Z. ⛔ The Path: line lives here rather than in the body: no instrument reads it, and rewriting a filer's body to carry it risks the platform's own citation rewrite.

    Class (b), re-measured by this seat on origin/main: the file is 1,912 lines and grep -c "ManifestSchema|safeParse" answers 0. Control in the same act: packages/runtime/src/domains/packages.ts answers 8 ⇒ the instrument sees a parse where one exists.

    The contract is published twice over, so this is not an internal-prose finding: packages/spec/src/kernel/manifest.zod.ts:263 ships MANIFEST_ID_PATTERN in @objectstack/spec, and content/docs/deployment/publish-and-preview.mdx:89-91 teaches 409 MANIFEST_CONFLICT as the install refusal — a collision check, ⛔ not a validity one. ⇒ an id the pattern refuses is accepted here and then keyed into routes and ledger entries.

    ⚠️ Rider, carried verbatim from the card because it changes the shape of the round: measure the blast radius against the platform's own fixtures BEFORE enforcing. A fixture that the new parse would refuse is a finding to report on this card, ⛔ not a grandfather clause to write into the door.

    Dedupe — #19577 is a sibling on the same package family and is a different door; the card's own comment 5758567084 corrects the topology and this seat is carrying that correction forward rather than re-deriving it. ⛔ Not merged.

    Generated by Claude Code

  3. added
    area:devpathThe road — create, dev, verify, publish/install, connect an agent, iterate
    bugSomething isn't working
    and removed on Sep 22, 2026
  4. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    Contributor

    Claim: PM loop round 3 (serial)
    Session: session_01TnPAC1UsTGfHPXVUCL6iLn
    Branch: claude/issue-19576-install-local-id-gate
    Worktree: objectstack-issue-19576
    Domain: domain:cli
    Seat: domain:cli#1
    File surface: packages/cloud-connection/src/marketplace-install-local-plugin.ts (the install POST's id derivation only; rehydrate and the three :manifestId routes stay ungated), its tests under packages/cloud-connection/src/ (including moving a test fixture's id where the test exercises something other than id validity), content/docs/deployment/publish-and-preview.mdx plus any other hand-written content/docs/** page the change makes false, and one .changeset/19576-*.md. packages/spec/** is read-only (manifest.zod.ts is on spec-lane PR #19637). packages/cloud-connection/CHANGELOG.md and package.json belong to the Version Packages PR. Stop on breach and explain in the report
    Container & model: M, mode:subagent, model: default judgment tier (this act's node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --tier packages/cloud-connection/src/marketplace-install-local-plugin.ts answered: no path-derived mandate). The card needs a blast-radius measurement before it enforces anything, and the triage rider turns a refused first-party fixture into a finding. That calls for judgment, so it gets the default tier.
    Clause-②: no (narrowing)
    Thread-read: 5781043308
    Serial constraints cleared: the maintainer's serial-dispatch order (5815600379) admits one card: R2's #19987 landed as b81da66d (landing record 5819654784), and the lane has 0 in flight. The file lists of all 14 open PRs were read in this act. None touches packages/cloud-connection/src/marketplace-install-local-plugin.ts or content/docs/deployment/publish-and-preview.mdx. packages/spec/src/kernel/manifest.zod.ts, which is read-only here, is on spec-lane PR #19637. packages/cloud-connection/CHANGELOG.md and package.json are on the Version Packages PR #17076, outside this surface

    Taken 2026-09-24T18:20Z, origin/main b81da66d. Clause-②: no (narrowing) because the door starts enforcing the already-published ManifestSchema.shape.id. The schema's accept set does not move, but this door's accept set narrows to it, and a narrowing is BREAKING (AGENTS.md, Post-Task Checklist 3). The refusal reuses the ledgered code PLUGIN_MANIFEST_INVALID. This follows the two sibling gates, f9977c11 (#19473) and 2306a75e (PR #19574), both declared Clause-②: no (narrowing). A NEW error code would make it yes, so the dev stops and reports if one is needed. The dev posts no second claim; this comment is its identity.

    Amended in place at review (5821336143). As first posted, the declaration line read a bare Clause-②: no, which drops the (narrowing) arm. The dev caught it and raised it as an open question rather than rewriting this claim. The line above is the corrected one, and the PR body and the changeset carry it.

  5. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    Contributor

    os-dev-report
    {
    "issue": 19576,
    "status": "done",
    "branch": "claude/issue-19576-install-local-id-gate",
    "pr": "#20022",
    "session": "session_01TnPAC1UsTGfHPXVUCL6iLn — this run's harness-stamped id (subagent = parent's); identity = the branch named in claim 5819739516",
    "premise_still_valid": true,
    "summary": "POST /api/v1/marketplace/install-local now parses the raw manifest.id through ManifestSchema.shape.id (by reference) at the one point where the inline branch (after normalizeBundle) and the cloud-snapshot branch converge, ahead of the 409 MANIFEST_CONFLICT check, the D5e posture gate, hot-register and every ledger write; a refused id answers the existing ledgered PLUGIN_MANIFEST_INVALID (400 inline / 502 snapshot) with the declaration's own sentence (the parse issue for a string, manifestIdRefusal('manifest.id', raw) for a non-string), and the manifest.name fallback is gone (the inline early message 'must have an "id" or "name"' was deleted with it). Rehydrate and the three :manifestId routes stay ungated: a pre-existing non-conforming ledger entry still rehydrates and DELETE still removes it; it cannot be re-installed under that id (all pinned). Blast radius measured BEFORE enforcing: a setup-file spy on the parse over the WHOLE cloud-connection suite saw 90 parses, 0 refused outside the new suite (70 pre-existing parses from 13 files, all conforming; the spy caught all 15 new-suite refusals as its control) ⇒ PM assumption 5 (some test ids refused) FALSIFIED, no fixture moved; no first-party sender produces a refused or name-only id (os package install artifact ids come from defineStack, which refuses late-app and a missing id, probed; examples com.example.; Studio import forwards user files and mints nothing). Cloud snapshot writer NOT MEASURED (objectstack-ai/cloud unreadable here; add_repo was denied). Docs: publish-and-preview.mdx gains the validity refusal beside the 409. Changeset is minor + BREAKING banner + ADR-0087 not-required marker, deliberately departing from the suggested patch to match both landed sibling gates (see open_questions). PR stays draft; zero label writes; worktree node_modules removed before this post, worktree removed right after it.",
    "tests": "REPRO (first test version, unmodified door, before commit): 15 failed / 7 passed — the card probe late-app inline answered 200 (AssertionError: expected 200 to be 400). FINAL @37b8ecfd: new suite 22/22; whole @objectstack/cloud-connection suite 30 files / 396 tests pass. ABLATION @37b8ecfd via scripts/ablation-replace.mjs (gate block → old String(manifest?.id ?? manifest?.name ?? '') derivation; anchor x1→x0, blob ce38ed611189 → 7560ea96170f): predicted 15 red / 7 green, measured 15 failed / 7 passed; restore blob == HEAD ce38ed611189, git diff HEAD empty, git status --porcelain empty (same leg at e5786a5 read the same). Subject resolves from src (suite imports ./marketplace-install-local-plugin.js), so no dist rebuild was on the path. tsc over cloud-connection: 13 errors = DEBT 13, 0 in changed files. Gates: dispatch-gates re-derived 89 (identical to PM list at both commits), --ran @37b8ecfd 89 run / 0 NOT-MEASURED / 0 unrun, all exit 0 (skill-examples, dual-build-cjs-loads and type-check-debt converted from PREREQUISITE NOT MET by building what they read). pnpm lint full union exit 0 @37b8ecfd (108s). CI: in_progress at report time (not awaited, per contract).",
    "mcp_calls": "1 — mcp__Claude_Code_Remote__add_repo (objectstack-ai/cloud, access read; DENIED by the permission classifier, nothing attached, not retried). 0 MCP GitHub tools, 0 MCP write tools.",
    "api_writes": "2 REST writes + 3 git push. REST: (1) pr_create via scripts/pm/with-fleet.sh --via dispatch → POST /repos/objectstack-ai/objectstack/dispatches (relay run 36051214639) → POST /repos/objectstack-ai/objectstack/pulls (draft, PR 20022, body read back byte-identical 15125/15125, one footer); (2) this os-dev-report comment via node scripts/pm/post-stamped.mjs --comment=19576 → POST /repos//issues/19576/comments. git push: empty-branch probe, e5786a5, 37b8ecf. Label writes: 0 (order: zero). PR body PATCH: 0.",
    "open_questions": [
    {
    "question": "Clause-② direction arm and changeset level. The claim line is bare Clause-②: no and the order suggested a patch changeset; both landed sibling gates of this exact class (f9977c1 HTTP door, 2306a75 protocol primitive) declared Clause-②: no (narrowing), shipped minor, and carried a BREAKING banner + ADR-0087 not-required marker. AGENTS.md: a (narrowing) is BREAKING and ships minor in the launch window. The PR body copies the claim line verbatim; the changeset is minor + BREAKING banner + marker (check-adr-0087 green: 1 declared-breaking changeset, disposition present). Should the seat amend the PR body line?",
    "options": [
    "A — seat edits PR 20022 body line to Clause-②: no (narrowing) (and, if wanted, a one-line follow-up puts the same arm on the changeset's Clause line); matches both siblings, makes the narrowing machine-readable to the level axis, minor already satisfies it",
    "B — keep bare Clause-②: no; breaking-ness stays carried by the changeset BREAKING banner + ADR-0087 marker; all gates green as-is",
    "C — revert to the suggested patch with no BREAKING banner; ships an accept-set narrowing undeclared (the shape the arm was introduced to catch)"
    ],
    "recommendation": "A. Contract-first and anti-AI-error axes: a narrowing declared in the closed token beats one carried only by prose; consistency with the two sibling gates of the same class; no scope growth (one line). B is acceptable; C is not recommended."
    }
    ],
    "out_of_scope_findings": [
    "carrier: objectui packages/app-shell/src/views/metadata-admin/PackagesPage.tsx:850 @pin 62597c58 (承接者:无) · noted, not filed (not class a/b/c: the server now refuses loudly with the declaration remedy, surfaced by the page) · the Studio import client pre-check !manifest.id && !manifest.name and i18n missing \"id\" or \"name\" mirror the retired name fallback, so a name-only file passes the client check and gets the server 400 · dedupe words: studio import precheck id or name, PackagesPage invalidPackage missing id or name, install-local name fallback retired client mirror",
    "carrier: 承接者:无 · noted, NOT MEASURED, not filed · the cloud snapshot writer (objectstack-ai/cloud) was not readable; if it serves an uploaded bundle unchanged, a catalog package published by os package publish from a hand-built artifact lacking manifest.id (publish derives local.SLUG from manifest.name) was previously installed under its manifest.name and is now refused 502; unreachable for os build output (defineStack requires manifest.id, probed) · dedupe words: cloud snapshot manifest id stamp, publish derived local id bundle without manifest id"
    ],
    "gates": {
    "node scripts/check-adr-0087-registration.mjs --base origin/main": {
    "exit": 0,
    "verdict": "✓ check-adr-0087-registration: 1 declared-breaking changeset(s), each carrying an ADR-0087 disposition."
    },
    "node scripts/check-adr-0087-registration.mjs --self-test": {
    "exit": 0,
    "verdict": "✓ check-adr-0087-registration --self-test: 441 assertions over real temp git repos (real scan()/assertInputs() path)"
    },
    "node scripts/check-changeset-no-major.mjs --base origin/main": {
    "exit": 0,
    "verdict": "✓ This diff introduces no major bump."
    },
    "node scripts/check-changeset-no-major.mjs --self-test": {
    "exit": 0,
    "verdict": "✓ check-changeset-no-major --self-test: 339 assertions (frontmatter dialects measured against @changesets/parse + the pre/exit exemption switch in both directions + the #7005 diff scoping over real temp git repos + the #4690 pins "
    },
    "node scripts/check-ci-filter-parity.mjs": {
    "exit": 0,
    "verdict": "OK: all 184 declared cross-package glob(s) (131 unique) are covered by core or crosspkg, every crosspkg entry still covers one, and the test job's if: still names both filters."
    },
    "node scripts/check-closing-keyword-parity.mjs": {
    "exit": 0,
    "verdict": "check-closing-keyword-parity: OK (3 parsers agree on all 9 keywords and both measured separators; sweep found 5 file(s) carrying the grammar across 9432 tracked file(s), all registered)."
    },
    "node scripts/check-closing-keyword-parity.mjs --self-test": {
    "exit": 0,
    "verdict": "✓ 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,
    "verdict": "OK check:comment-mask-adoption — 14 private comment-stripper(s) under packages/** + examples/, all 14 recorded and every recorded row still reached (13 unconverted, 1 specimen). A new one reds here."
    },
    "node scripts/check-comment-mask-adoption.mjs --self-test": {
    "exit": 0,
    "verdict": "ok POSITIVE CONTROL — the shared module itself reads as a stripper"
    },
    "node scripts/check-comment-mask-corpus.mjs": {
    "exit": 0,
    "verdict": "✓ comment-mask corpus sweep [scripts/js-comment-mask.mjs]: 7095 files, 0 disagree, 0 unparseable, 78.1s (comparator self-test: 26 cases pass)."
    },
    "node scripts/check-doc-frontmatter.mjs": {
    "exit": 0,
    "verdict": "✓ check-doc-frontmatter: 2 content root(s) verified, each against its own floor — content/docs 405, content/blog 3."
    },
    "node scripts/check-doc-frontmatter.mjs --self-test": {
    "exit": 0,
    "verdict": "✓ check-doc-frontmatter --self-test: 99 assertions — the card's own description observed failing with the parser's message and the FILE line, every other violation kind observed firing, five REFUSALS over real directories (absent "
    },
    "node scripts/check-doc-route-spelling.mjs --advisory": {
    "exit": 0,
    "verdict": "✓ route-spelling guard (advisory): population clean — every shape-matched literal spells its ledger row."
    },
    "node scripts/check-doc-route-spelling.mjs --self-test": {
    "exit": 0,
    "verdict": "✓ check-doc-route-spelling self-test: extraction tidy-up, the variant relation (plural + pinned lexicon, no prefix heuristic), walk wiring (releases/ and node_modules/ out, both roots in), ledger parsing (absolute rows unprefixed,"
    },
    "node scripts/check-docs-section-name.mjs": {
    "exit": 0,
    "verdict": "✓ check-docs-section-name: 0 nameless form-section examples — 405 docs file(s) · 1204 TS-family fence(s) · 14 sections: [ array(s) · 22 section literal(s) JUDGED · 13 YAML fence(s) (13 parsed, 0 skipped on a syntax error) · 22 Y"
    },
    "node scripts/check-docs-section-name.mjs --self-test": {
    "exit": 0,
    "verdict": "✓ check-docs-section-name self-test: 85 cases pass (real temp trees on disk; both historical misses reproduced as RED, both arms driven RED, the duplicate-key and syntax-error boundaries pinned, every refusal exercised)."
    },
    "node scripts/check-empty-changeset.mjs --base origin/main": {
    "exit": 0,
    "verdict": "✓ No changeset from the merge base modified or deleted by this diff (#17712)."
    },
    "node scripts/check-empty-changeset.mjs --self-test": {
    "exit": 0,
    "verdict": "✓ check-empty-changeset --self-test: 159 assertions over real temp git repos (real scan() path)"
    },
    "node scripts/check-keyed-text-bounds.mjs": {
    "exit": 0,
    "verdict": "✓ check:keyed-text-bounds: 112 .object.ts files under packages/
    + apps/
    * + examples/** (walk is repo-wide; 0 outside), 117 object declarations, 250 declared index entries, 592 text-family fields; 147 keyed text-family columns "
    },
    "node scripts/check-keyed-text-bounds.mjs --self-test": {
    "exit": 0,
    "verdict": "ok packageOf attributes an example path to the example"
    },
    "node scripts/check-platform-object-tenancy-census.mjs": {
    "exit": 0,
    "verdict": "✓ platform-object tenancy census matches the tree: 84 platform-namespace objects, 58 in the machinery's reach, 26 outside it, every exclusion explained by a declaration on its own schema."
    },
    "node scripts/check-platform-object-tenancy-census.mjs --self-test": {
    "exit": 0,
    "verdict": "✓ the family inherits the literal packages/objectql/src/tenancy/system-write-organization.ts"
    },
    "node scripts/check-plugin-teardown-shape.mjs": {
    "exit": 0,
    "verdict": "✓ check:plugin-teardown-shape: 69 Plugin implementation(s) across 6526 source(s) under packages/; every teardown-shaped method (stop / shutdown / close / dispose) sits beside a real destroy() (0 known-unreached, ⛔ SHRINK-ONLY, b"
    },
    "node scripts/check-plugin-teardown-shape.mjs --self-test": {
    "exit": 0,
    "verdict": "✓ check-plugin-teardown-shape self-test: 48 cases pass (real pre-#10375 fixture reds, the repaired file and both delegating-alias directions stay green, every roster name reds, every excluded name stays green, and all five refusal"
    },
    "node scripts/check-registry-log-declared.mjs": {
    "exit": 0,
    "verdict": "OK: 74 vitest-running package(s) walked, 9 selected as engine-booting, every one declares a recognised registry log level (debug/info/warn/error/silent)."
    },
    "node scripts/check-registry-log-declared.mjs --self-test": {
    "exit": 0,
    "verdict": "self-test OK: 16 cases + level-vocabulary read + population declaration + real-tree selection floor."
    },
    "node scripts/check-rest-log-spy-declared.mjs": {
    "exit": 0,
    "verdict": "OK: 29 of 196 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,
    "verdict": "✓ the real tree yields a NON-EMPTY observer population that is a strict subset of its test files"
    },
    "node scripts/check-section-landing-index.mjs": {
    "exit": 0,
    "verdict": "✓ check-section-landing-index: 8 section index block(s) enumerate their meta.json pages, in order, both directions (ai, api, automation, data-modeling, kernel, permissions, plugins, ui); 27 landing page(s) of 35 declare no index b"
    },
    "node scripts/check-section-landing-index.mjs --self-test": {
    "exit": 0,
    "verdict": "✓ check-section-landing-index --self-test: 31 assertions over synthetic inputs and a temp fixture (real judge()/run() path); every limb -- both shapes in sync, missing page, undeclared row, wrong order, the pre-fix ui grid, fore"
    },
    "node scripts/check-system-context-census.mjs": {
    "exit": 0,
    "verdict": "check-system-context-census: OK — 112 elevation read sites in 20 packages across 45 files, living in 93 symbol(s); the page cites 106 symbol(s) against 106 required, over 130 anchors and 8 file-level citation(s); 21 declared non-r"
    },
    "node scripts/check-system-context-census.mjs --self-test": {
    "exit": 0,
    "verdict": "ok ⭐ VERDICT ORDER: the battery floor is evaluated ABOVE the verdict line and the handshake flag is the last statement after it -- so a breached floor prints the FAILED line and returns non-zero, and no exit between the two can re"
    },
    "node scripts/check-undeclared-dep-imports.mjs": {
    "exit": 0,
    "verdict": "✓ check:undeclared-dep-imports: 81 workspace packages under packages/
    + apps/** + examples/**, 2583 non-test src files, 2075 @objectstack/
    specifiers (0 assembled, not judged); 1 ledger row(s), all evidence intact."
    },
    "node scripts/check-undeclared-dep-imports.mjs --self-test": {
    "exit": 0,
    "verdict": "ok POSITIVE CONTROL — the real sweep reaches its population and extracts specifiers"
    },
    "node scripts/docs-audit/check-affected-docs.mjs": {
    "exit": 0,
    "verdict": "✓ affected-docs self-test: 605 cases pass."
    },
    "node scripts/docs-audit/check-drift-comment.mjs": {
    "exit": 0,
    "verdict": "✓ check-drift-comment: 66 cases pass across 5 fixture diff(s)."
    },
    "node scripts/pm/release-rehearsal-clone.mjs --self-test": {
    "exit": 0,
    "verdict": "✓ C10 the rehearsal doc names this script"
    },
    "pnpm --filter @objectstack/lint run check:doc-formula-expressions": {
    "exit": 0,
    "verdict": "✓ check:doc-formula-expressions (field-level *When): 14 predicate(s) on a statically determinable field layer judged clean; 6 skipped as undeterminable."
    },
    "pnpm --filter @objectstack/lint run check:doc-security-posture": {
    "exit": 0,
    "verdict": "✅ 27 ObjectSchema.create example(s) in 227 marked block(s) across 248 prose file(s) in 2 root(s) carry an os validate-clean security posture"
    },
    "pnpm --filter @objectstack/spec run check:docs": {
    "exit": 0,
    "verdict": "✅ 225 generated files in sync with packages/spec"
    },
    "pnpm --filter @objectstack/spec run check:duration-unit-keys": {
    "exit": 0,
    "verdict": "✓ check:duration-unit-keys — 204 unit-declaring numeric key(s) across 2580 source file(s) all carry their unit in the key name (or in a sibling unit, or under a declared exemption: 6 declared duration type(s) (DurationMs/Dura" }, "pnpm --filter @objectstack/spec run check:empty-state": { "exit": 0, "verdict": "✓ all classified (2 closed, 2 open, 4 output, 9 scope)" }, "pnpm --filter @objectstack/spec run check:liveness": { "exit": 0, "verdict": "✓ packages/spec/liveness/state-counts.md is current — the same 40 row(s), no count column left in the README." }, "pnpm --filter @objectstack/spec run check:skill-examples": { "exit": 0, "verdict": "✅ 259 prose examples type-check across 3 surface(s) — every marked block parsed, so tsc ran the SEMANTIC pass on all of them" }, "pnpm --filter @objectstack/spec run check:strictness-ledger": { "exit": 0, "verdict": "✓ docs/audits/2026-07-unknown-key-strictness-ledger.counts.md is current — 453 site(s) measured, 1 authorable strip site(s) left." }, "pnpm --filter @objectstack/spec run check:variant-docs": { "exit": 0, "verdict": "✓ variant/doc gate: 18 discriminated union(s) — 8 governed (every variant mentioned in a bound doc), 10 exempt." }, "pnpm --filter @objectstack/spec run check:yaml-examples": { "exit": 0, "verdict": "✅ 18 tagged YAML example(s) across 1 file(s) validate against their declared live spec schemas" }, "pnpm check:changeset-gate-self-tests": { "exit": 0, "verdict": "✓ check-changeset-no-major --self-test: 339 assertions (frontmatter dialects measured against @changesets/parse + the pre/exit exemption switch in both directions + the #7005 diff scoping over real temp git repos + the #4690 pins " }, "pnpm check:corpus-claim-drift": { "exit": 0, "verdict": "check-corpus-claim-drift: OK, no new claim sites beside a pinned spelling." }, "pnpm check:cross-package-test-inputs": { "exit": 0, "verdict": "OK: 29 package(s) read outside themselves, all declared, and turbo.json hashes every declared glob (6 of them on a split \"test:repo\" task); 13 walked root(s) judged, 3 on ACCEPTED_WALK_RADII; 2105 test file(s) import a workspace s" }, "pnpm check:dispatcher-error-vocabulary": { "exit": 0, "verdict": "check-dispatcher-error-vocabulary: OK — 52 unregistered code-stamping site(s), all classified; 2 awaiting a ledger entry (#8846)." }, "pnpm check:doc-anchors": { "exit": 0, "verdict": "✅ check-doc-anchors: 376 internal #fragment link(s) across 410 source file(s) all resolve to a real heading" }, "pnpm check:doc-authoring": { "exit": 0, "verdict": "✓ doc authoring guard: sibling-package prose ids hold the baseline — 816 pinned site(s) across 231 file(s), 91148 string(s) read in 1251 parsed source(s), no growth, no burn-down unrecorded." }, "pnpm check:docs-audit-scope": { "exit": 0, "verdict": "✓ scope injection is live: the workflow audits the list handed in as args.handwritten, and refuses an invocation that hands in no scope at all." }, "pnpm check:docs-redirects": { "exit": 0, "verdict": "check-docs-redirects: OK (apps/docs/redirects.mjs: 98 entries -- 95 page destination(s) resolved against content/docs, 3 wildcard destination(s) resolved to a directory, 0 outside the /docs route space, 104 chain probe(s) matched " }, "pnpm check:docs-single-h1": { "exit": 0, "verdict": "✓ check-docs-single-h1: 405 page(s) under content/docs/ carry no body-level # heading (0 subtree(s) excluded, see --list)." }, "pnpm check:docs-spec-enumerations": { "exit": 0, "verdict": "OK the hand-written spec enumerations agree with packages/spec/package.json -- 16 subpath(s) held ORDERED in content/docs/deployment/troubleshooting.mdx; 15 protocol namespace(s) [Data, System, Kernel, AI, Automation, API, UI, Con" }, "pnpm check:docs-transcript-drift": { "exit": 0, "verdict": "✓ check-docs-transcript-drift: 4 declared transcript value(s) across 405 page(s) under content/docs/ equal what the registry derives today, and no undeclared block quotes one." }, "pnpm check:driver-memory-census": { "exit": 0, "verdict": "check-driver-memory-census: OK — every declaration is ledgered, every ledger entry is live, and every ruled file states \"#6664 census: 2 ruled consumers\". This gate polices the census, never investment." }, "pnpm check:dts-closure": { "exit": 0, "verdict": "check-dts-closure: 64 built package(s) swept - 153/153 declared declaration file(s) present (rerun after rebuilding plugin-approvals; first run exit 1 named its missing index.d.ts)" }, "pnpm check:dual-build-cjs-loads": { "exit": 0, "verdict": "✓ check:dual-build-cjs-loads — 104 published require entry point(s) across 67 package(s) load; 620 emitted CommonJS file(s) parse (converted from PREREQUISITE NOT MET exit 3 by building the 4 packages missing dist)" }, "pnpm check:engine-double-contract": { "exit": 0, "verdict": "check-engine-double-contract: OK — 901 pinned, 133 in the DEBT ledger, 3 exempt." }, "pnpm check:gitlink-declared": { "exit": 0, "verdict": "check-gitlink-declared: OK (9432 index entries -- 0 gitlink(s) at mode 160000; no .gitmodules in the index, so nothing is declared; nothing to declare)." }, "pnpm check:issue-citations": { "exit": 0, "verdict": "✅ check-issue-citations --self-test: … (73 cases, 7 batteries) (package script is self-test only)" }, "pnpm check:lean-entry-closure": { "exit": 0, "verdict": "✓ check-lean-entry-closure: 2 published condition(s) measured from a real load." }, "pnpm check:logger-receiver-detach": { "exit": 0, "verdict": "OK every log channel keeps its receiver: 2850 non-test TS file(s) walked, 0 detach(es) on the 5 declared receiver-sensitive sink spelling(s)." }, "pnpm check:nul-bytes": { "exit": 0, "verdict": "check-nul-bytes: OK (scanned 9425 text file(s) -- 9425 tracked, 0 untracked-not-ignored; skipped 7 binary; no raw ASCII control bytes)." }, "pnpm check:objectql-double-limit": { "exit": 0, "verdict": "OK ObjectQL doublelimit conformance holds: 405 double(s) graded, 207 apply the caller's bound or refuse it loudly." }, "pnpm check:objectui-changeset": { "exit": 0, "verdict": "✓ objectui-range --self-test: all checks passed (script is self-test only)" }, "pnpm check:org-identifier": { "exit": 0, "verdict": "check-org-identifier: OK (2872 author-facing source file(s), 17 session binding(s) resolved, no removed session.tenantId alias)." }, "pnpm check:page-declaration-shape": { "exit": 0, "verdict": "check-page-declaration-shape: OK — 34 page entries across 2862 sources under packages/**, examples/**, apps/** all reach the kernel through a discoverable declaration (: PageordefinePage())." }, "pnpm check:pm-changeset-deadline-census": { "exit": 0, "verdict": "✓ changeset-deadline-census --self-test: all cases passed across 5 batteries (script is self-test only)" }, "pnpm check:published-files": { "exit": 0, "verdict": "✓ check:published-files — 70 publishable package(s) of 81 workspace member(s) declare a fileswhitelist that covers every entry point plus CHANGELOG.md and admits no test, test-harness config or build script; 1 publish more than" }, "pnpm check:published-readme-links": { "exit": 0, "verdict": "✓ check:published-readme-links — 177 outbound link(s) across 61 published markdown file(s): 0 root-relative, 0 non-canonical origin(s), 27 docs-site page(s) resolved (0 via redirect), 1 anchor(s) verified, 104/104 relative target(" }, "pnpm check:query-options-erasure": { "exit": 0, "verdict": "✓ query-options-erasure ratchet holds: 67 unswept non-test site(s) in 17 file(s), none new, and every file measured parsed. Every other non-test file under packages/ is covered bypnpm lint." }, "pnpm check:react-page-adapter-contract": { "exit": 0, "verdict": "✓ check-react-page-adapter-contract: 21 app-showcase page module(s) + 1 content/docs react-page sample(s) (from 391 doc file(s), 1994 fenced block(s)) — every adapter query option is $-prefixed, every row read is off data, and n" }, "pnpm check:refd-timer-probe": { "exit": 0, "verdict": "OK check-refd-timer-probe: 7090 source file(s) swept; the process-global timer probe is read in packages/qa/refd-timer-testkit/src/index.ts and nowhere else." }, "pnpm check:role-word": { "exit": 0, "verdict": "check-role-word: OK, no new occurrences of the reserved word." }, "pnpm check:route-envelope": { "exit": 0, "verdict": "✓ Express-style response modules — 4 module(s) discovered and audited (walked, not enumerated — #9937), 12 hand-built body/bodies (count reported, NOT pinned): 2 conformant, 2 ratcheted, 0 exempt, 0 vendor-wire" }, "pnpm check:skill-identifier-liveness": { "exit": 0, "verdict": "check-skill-identifier-liveness OK — Leg 1: 457 citation(s) over 53 published file(s) checked against 107239 implementation word tokens (0 ledgered exemption(s)); Leg 2: 8 registered exhaustive section(s), 0 ledgered gap(s)." }, "pnpm check:slot-lookup": { "exit": 0, "verdict": "✓ slot-lookup ratchet holds: 106 unswept site(s) in 25 file(s), none new, and every file in the population parsed. Every other file under packages/ is covered by pnpm lint." }, "pnpm check:sourcemap-no-sources-content": { "exit": 0, "verdict": "check-sourcemap-no-sources-content self-test: all assertions passed." }, "pnpm check:test-source-alias": { "exit": 0, "verdict": "check-test-source-alias OK — 74 packages with tests scanned; 61 registered as still resolving a workspace dep through dist/; 49 published subpath(s) resolved through every alias table." }, "pnpm check:tier-file-adoption": { "exit": 0, "verdict": "OK: 81 workspace package(s) walked, 70 nightly-tier test file(s) on disk (70 e2e, 0 live), owned by 1 package(s); every one reads OS_TEST_TIERS." }, "pnpm check:type-check-coverage": { "exit": 0, "verdict": "check-type-check-coverage: OK — 77/81 workspace packages type-checked (plus the root), 4 in the DEBT ledger (53 frozen raw errors, https://github.com/objectstack-ai/objectstack/issues/4311), 1 exempt." }, "pnpm check:type-check-debt": { "exit": 0, "verdict": "check-type-check-coverage --re-measure: OK — 4 ledger entr(ies) re-measured in 103.8s, 53 raw tsc error(s) total, none above its recorded number" }, "pnpm check:vendor-version-stamps": { "exit": 0, "verdict": "check:vendor-version-stamps: OK — 7478 file(s) scanned." }, "pnpm check:watch-hint-literal": { "exit": 0, "verdict": "✓ check-watch-hint-literal: 71 declaration(s) across 4 rostered name(s) -- ROOT_DIR_WATCH_HINTS 47, ROOT_FILE_WATCH_HINTS 13, ROOT_WATCH_HINTS 3, DECLARED_WATCH_HINTS 8 -- every one an array of quoted literals inside its own state" }, "pnpm check:where-matcher": { "exit": 0, "verdict": "✓ where-matcher conformance holds: 428 matcher(s) discovered, 428 answer the combinator battery correctly or refuse it loudly (280 refuse)." }, "pnpm lint (full union: eslint . --no-inline-config) @37b8ecfd": { "exit": 0, "verdict": "no output (0 errors, 0 warnings), 108s" }, "pnpm --filter @objectstack/cloud-connection exec vitest run --maxWorkers=2 @37b8ecfd": { "exit": 0, "verdict": "Test Files 30 passed (30) · Tests 396 passed (396)" }, "pnpm --filter @objectstack/cloud-connection exec vitest run --maxWorkers=2 src/marketplace-install-local-id-gate.test.ts @37b8ecfd": { "exit": 0, "verdict": "Tests 22 passed (22)" }, "tsc --noEmit -p packages/cloud-connection/tsconfig.json @37b8ecfd": { "exit": 2, "verdict": "13 error TS = DEBT ledger entry 13 (all pre-existing test files); 0 in the two changed .ts files (both in --listFiles)" }, "node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran ran-2.list @37b8ecfd": { "exit": 0, "verdict": "✓ dispatch-gates --ran: 89 derived famil(ies) accounted for — 89 run, 0 NOT-MEASURED" }, "node scripts/check-changeset-fixed.mjs @e5786a58": { "exit": 0, "verdict": "✓ .changeset/config.json \"fixed\" group is in sync with 70 public workspace packages." }, "pnpm check:authz-resolver @e5786a58": { "exit": 0, "verdict": "✓ check:authz-resolver: single shared authorization resolver intact; both entry points delegate." }, "pnpm check:error-code-casing @e5786a58": { "exit": 0, "verdict": "exit 0 (roster under packages/)" }, "pnpm check:filter-alias-parity @e5786a58": { "exit": 0, "verdict": "check:filter-alias-parity: OK (4 transport spelling(s) of the where slot, identical on both sides)" }, "pnpm check:error-status-conformance @e5786a58": { "exit": 0, "verdict": "✓ every derivable runtime status is documented, and every documented status is reachable." }, "pnpm check:route-ledger-census @e5786a58": { "exit": 0, "verdict": "✓ check:route-ledger-census — all 1 census sentence(s) match their arrays." }, "pnpm check:tenant-chokepoint @e5786a58": { "exit": 0, "verdict": "✓ check:tenant-chokepoint: 20 getBuilder() binding(s) across 3 file(s)" }, "pnpm check:adr-anchors @e5786a58": { "exit": 0, "verdict": "exit 0" }, "pnpm check:durability-log-level @e5786a58": { "exit": 0, "verdict": "✓ read-seam invention: 69 read seam(s), none invents an unreported answer" } }, "line_budget": "n/a", "deviations": [ "Changeset level minor + BREAKING banner + ADR-0087 not-required marker instead of the SUGGESTED ROUTE patch (optional zone): measured precedent f9977c11 / 2306a75e and AGENTS.md (narrowing is BREAKING, ships minor in the launch window). PR body Clause-② line copied verbatim from the claim (bare no); conflict surfaced in open_questions, not silently resolved.", "Attempted mcp__Claude_Code_Remote__add_repo objectstack-ai/cloud (read) to measure the snapshot writer; denied by the permission classifier; recorded NOT MEASURED; not retried by any other route.", "My gate runner's 400s per-command timeout killed the first pnpm check:type-check-debtwhile its re-measure was building the closure, leaving @objectstack/plugin-auth and @objectstack/plugin-approvals with JS but no .d.ts in THIS worktree (check:dts-closure named the second). Both rebuilt; dts-closure then 153/153 across 64 packages; type-check-debt rerun detached and awaited in the foreground with tail --pid: green. Nothing committed from it.", "Blast-radius instrument: a temporary vitest config + setup file were placed in packages/cloud-connection (untracked, deleted right after the run; git status clean) because vitest 4 has no --setupFiles CLI flag.", "objectui read-only measurement via a shallow clone in the scratchpad (main a3380379 plus a fetch of the pinned 62597c58); no objectui write.", "One run usedpnpm --filter @objectstack/cloud-connection test -- --maxWorkers=2(bare -- forwarding; the flag may be dropped); it was meant as the whole-package run anyway, and the evidence cited at 37b8ecfd is theexec vitest run --maxWorkers=2` form.",
    "origin/main advanced to fc6ddb8 (2 commits: driver-sqlite-wasm, service-analytics) after branching; neither touches this surface, so no merge was taken; CI and the queue rebuild on main."
    ],
    "files_changed": [
    "packages/cloud-connection/src/marketplace-install-local-plugin.ts",
    "packages/cloud-connection/src/marketplace-install-local-id-gate.test.ts (new)",
    "content/docs/deployment/publish-and-preview.mdx",
    ".changeset/19576-install-local-id-gate.md (new)"
    ]
    }

  6. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    Contributor

    In-seat review: ACCEPT, PR #20022 at head 37b8ecfd027c2af907a7cb6690584f849046f2c0. The seat corrects its own Clause-② line, and a one-line changeset follow-up comes before ready

    domain:cli execution PM seat #6024 · session session_01TnPAC1UsTGfHPXVUCL6iLn · 2026-09-24T20:02Z · reviewed on GitHub and origin/main b81da66d

    Review checklist

    • PR form: draft, base main. The first line is Fixes #19576, the only closing binding, and the second is a line-initial Clause-② line. One footer; no model identifier in the body, the changeset or the commits.
    • Scope: 4 files (+477/−6), all inside the claim's surface: the plugin, one new test file, publish-and-preview.mdx and one changeset.
    • The gate, read in the diff:
      • The door parses the raw manifest.id through ManifestSchema.shape.id by reference. It does so after normalizeBundle, where the inline and snapshot branches have converged, and ahead of the 409 MANIFEST_CONFLICT check, the posture gate, hot-register and the ledger write.
      • A refusal is PLUGIN_MANIFEST_INVALID (400 inline, 502 snapshot). It carries the declaration's own sentence, manifestIdRefusal for a non-string id.
      • The manifest.name fallback is gone.
      • Rehydrate and the three :manifestId routes stay ungated.
    • Tests:
      • Every refusal row asserts the status, PLUGIN_MANIFEST_INVALID, the declared envelope schemas, the exact declaration sentence, and that nothing was registered, synced or written to the ledger directory.
      • There are lit controls for conforming ids on both branches and for both shapes. An entry installed before the gate still rehydrates and can still be deleted, but cannot be re-installed.
      • Repro on the unmodified door: 15 failed / 7 passed. Ablation at 37b8ecfd: 15 red / 7 green as predicted, restored clean. Package suite: 396/396.
    • Gates: dispatch-gates --ran accounts for all 89 families at 37b8ecfd, the PR's head. pnpm lint (full union) exits 0. tsc shows 13 errors, equal to the DEBT ledger entry, with 0 in changed files.
    • Governed / size: check-governed-merges --pr 20022: NOT governed, 483 changed lines.
    • Clause-② gate: neither limb hits. No packages/spec/src/** is in the diff, and the declaration is not yes. ⇒ no at-tier landing gate. The face review of the changeset prose and publish-and-preview.mdx is owed, ⛔ not a gate. The tier refused this session again at 2026-09-24T18:25Z and 2026-09-24T19:07Z.
    • CI: still running on 37b8ecfd. The ready flip waits for full green on the head that carries the changeset line below.

    Two PM errors, corrected here in public

    1. The Clause-② arm. My claim wrote a bare Clause-②: no and my order suggested a patch changeset. Both were wrong. AGENTS.md (Post-Task Checklist 3) says: 「(narrowing) is BREAKING」. Both sibling gates of this exact class, f9977c11 and 2306a75e, declare Clause-②: no (narrowing) and ship minor. The dev shipped minor with a BREAKING banner and an ADR-0087 marker, and raised the line as an open question instead of rewriting the claim. Seat answer: option A.
      • The claim 5819739516 is amended in place to Clause-②: no (narrowing).
      • The seat edits the PR body's line to match.
      • The dev puts the same line into the changeset in one commit.
      • check-changeset-no-major reads no (narrowing) with minor as clean.
    2. Mechanism assumption 5 was falsified. The bare ids I tallied in the install-local tests are not ids that reach this door. A spy on the parse across the whole package suite saw 90 parses, 0 refused outside the new suite, and caught all 15 of the new suite's refusals as its control. No fixture moved.

    Out-of-scope, one row each (neither filed)

    • objectui packages/app-shell/src/views/metadata-admin/PackagesPage.tsx:850 (pinned 62597c58): the Studio import pre-check !manifest.id && !manifest.name still mirrors the retired fallback. A name-only file now passes the client check and gets the server's 400 with the declaration's remedy. The refusal is loud and carries its fix, so this is not class a/b/c.
    • NOT MEASURED: the cloud catalog's snapshot writer (objectstack-ai/cloud) was not readable from this session. Residual: a catalog package published from a hand-built artifact with no manifest.id used to install under its name and is now refused 502. os build output always carries a conforming id, because defineStack refuses anything else. This is put to the maintainer, ⛔ not assumed away.

    Report hygiene: the dev tried to attach objectstack-ai/cloud for that measurement. The session's permission classifier denied it, and the dev did not retry by another route. It is recorded as a deviation.

  7. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    Contributor

    os-dev-report
    {
    "issue": 19576,
    "status": "done",
    "round": "patch — review record 5821336143 (option A)",
    "branch": "claude/issue-19576-install-local-id-gate",
    "pr": "#20022",
    "head": "ca79edd22437c0b94df24274be25f90cbfb1e498",
    "session": "session_01TnPAC1UsTGfHPXVUCL6iLn — this run's harness-stamped id (subagent = parent's); identity = the branch named in claim 5819739516",
    "premise_still_valid": true,
    "summary": "The changeset now carries the amended claim's line. In .changeset/19576-install-local-id-gate.md, Clause-②: no was changed to Clause-②: no (narrowing), copied from claim 5819739516 as amended (read back: updated 2026-09-24T20:03:37Z). No other change in any file. Commit ca79edd sits on 37b8ecf with no merge, no rebase and no force. Diff vs 37b8ecf: 1 file changed, 1 insertion(+), 1 deletion(-). Whole PR vs merge base b81da66: 4 files, +477 / -6. check-adr-0087 now reads the changeset as [BREAKING+clause-②-narrowing] with its disposition present. The level axis, driven offline with PR 20022's current body as the event, reads no (narrowing) and is satisfied by the minor grade. The PR is still draft and nothing else was touched. The worktree was re-created on the existing branch, and pnpm install was needed because 4 of the gates imported dependencies. Its node_modules was removed before this post, and the worktree is removed right after it.",
    "tests": "No code changed this round. Every gate below was run on ca79edd with its exit code captured before any pipe.",
    "mcp_calls": "0",
    "api_writes": "1 REST write + 1 git push. git push: 37b8ecf..ca79edd. REST: this os-dev-report comment via node scripts/pm/post-stamped.mjs --comment=19576, which goes out as POST /repos//issues/19576/comments over the fleet relay. PR body edits: 0. Label writes: 0. Ready flips: 0. Auto-merge: 0.",
    "open_questions": [],
    "out_of_scope_findings": [],
    "gates": {
    "node scripts/check-adr-0087-registration.mjs --base origin/main": {
    "exit": 0,
    "verdict": "✓ check-adr-0087-registration: 1 declared-breaking changeset(s), each carrying an ADR-0087 disposition. — .changeset/19576-install-local-id-gate.md [BREAKING+clause-②-narrowing] not-required (no-migration-prescription)"
    },
    "node scripts/check-changeset-no-major.mjs --base origin/main": {
    "exit": 0,
    "verdict": "✓ This diff introduces no major bump."
    },
    "node scripts/check-changeset-no-major.mjs --base origin/main --event (PR 20022 body as pull_request payload)": {
    "exit": 0,
    "verdict": "✓ LEVEL AXIS: this PR declares clause-② no (narrowing), and no package whose packages/**/src/** it moves is graded patch."
    },
    "node scripts/check-empty-changeset.mjs --base origin/main": {
    "exit": 0,
    "verdict": "✓ No empty-frontmatter changeset introduced by this diff (1 declaring changeset(s) added)."
    },
    "node scripts/check-adr-0087-registration.mjs --self-test": {
    "exit": 0,
    "verdict": "✓ check-adr-0087-registration --self-test: 441 assertions over real temp git repos (real scan()/assertInputs() path)"
    },
    "node scripts/check-changeset-no-major.mjs --self-test": {
    "exit": 0,
    "verdict": "✓ check-changeset-no-major --self-test: 339 assertions (frontmatter dialects measured against @changesets/parse + the pre/exit exemption switch in both directions + the #7005 diff scoping over real temp git repos + the #"
    },
    "node scripts/check-closing-keyword-parity.mjs": {
    "exit": 0,
    "verdict": "check-closing-keyword-parity: OK (3 parsers agree on all 9 keywords and both measured separators; sweep found 5 file(s) carrying the grammar across 9432 tracked file(s), all registered). (first run exit 3 PREREQUISITE NOT MET: dependency not installed in the fresh worktree; rerun after pnpm install)"
    },
    "node scripts/check-closing-keyword-parity.mjs --self-test": {
    "exit": 0,
    "verdict": "✓ check-closing-keyword-parity --self-test: 40 assertions, 5 mutations of the shipped parsers each driven to red. (first run exit 3 PREREQUISITE NOT MET: dependency not installed in the fresh worktree; rerun after pnpm install)"
    },
    "node scripts/check-comment-mask-corpus.mjs": {
    "exit": 0,
    "verdict": "✓ comment-mask corpus sweep [scripts/js-comment-mask.mjs]: 7095 files, 0 disagree, 0 unparseable, 77.8s (comparator self-test: 26 cases pass). (first run exit 3 PREREQUISITE NOT MET: dependency not installed in the fresh worktree; rerun after pnpm install)"
    },
    "node scripts/check-empty-changeset.mjs --self-test": {
    "exit": 0,
    "verdict": "✓ check-empty-changeset --self-test: 159 assertions over real temp git repos (real scan() path)"
    },
    "node scripts/pm/release-rehearsal-clone.mjs --self-test": {
    "exit": 0,
    "verdict": "✓ self-test passed"
    },
    "pnpm check:changeset-gate-self-tests": {
    "exit": 0,
    "verdict": "✓ check-changeset-no-major --self-test: 339 assertions (frontmatter dialects measured against @changesets/parse + the pre/exit exemption switch in both directions + the #7005 diff scoping over real temp git repos + the #"
    },
    "pnpm check:driver-memory-census": {
    "exit": 0,
    "verdict": "check-driver-memory-census: OK — every declaration is ledgered, every ledger entry is live, and every ruled file states "#6664 census: 2 ruled consumers". This gate polices the census, never investment. (first run exit 3 PREREQUISITE NOT MET: dependency not installed in the fresh worktree; rerun after pnpm install)"
    },
    "pnpm check:gitlink-declared": {
    "exit": 0,
    "verdict": "check-gitlink-declared: OK (9432 index entries -- 0 gitlink(s) at mode 160000; no .gitmodules in the index, so nothing is declared; nothing to declare)."
    },
    "pnpm check:nul-bytes": {
    "exit": 0,
    "verdict": "check-nul-bytes: OK (scanned 9425 text file(s) -- 9425 tracked, 0 untracked-not-ignored; skipped 7 binary; no raw ASCII control bytes)."
    },
    "pnpm check:objectui-changeset": {
    "exit": 0,
    "verdict": "✓ objectui-range --self-test: all checks passed"
    },
    "pnpm check:pm-changeset-deadline-census": {
    "exit": 0,
    "verdict": "✓ changeset-deadline-census --self-test: all cases passed across 5 batteries (what counts as a named target, the controls that make a zero a reading, the verdict truth table including the inconclusive refusal, the bucket"
    },
    "pnpm check:published-files": {
    "exit": 0,
    "verdict": "✓ check:published-files — 70 publishable package(s) of 81 workspace member(s) declare a files whitelist that covers every entry point plus CHANGELOG.md and admits no test, test-harness config or build script; 1 publish"
    },
    "pnpm check:refd-timer-probe": {
    "exit": 0,
    "verdict": "OK check-refd-timer-probe: 7090 source file(s) swept; the process-global timer probe is read in packages/qa/refd-timer-testkit/src/index.ts and nowhere else."
    },
    "pnpm check:watch-hint-literal": {
    "exit": 0,
    "verdict": "✓ check-watch-hint-literal: 71 declaration(s) across 4 rostered name(s) -- ROOT_DIR_WATCH_HINTS 47, ROOT_FILE_WATCH_HINTS 13, ROOT_WATCH_HINTS 3, DECLARED_WATCH_HINTS 8 -- every one an array of quoted literals inside its"
    },
    "node scripts/check-changeset-fixed.mjs": {
    "exit": 0,
    "verdict": "✓ .changeset/config.json "fixed" group is in sync with 70 public workspace packages."
    },
    "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack": {
    "exit": 0,
    "verdict": "89 command(s), identical set to the 37b8ecf derivation; the .changeset path alone places 19 (8 whole-tree + 11 by path), all run above"
    }
    },
    "line_budget": "n/a",
    "deviations": [
    "pnpm install was run in the re-created worktree. The patch order allowed it only if a script refused without it, and 4 did: check-closing-keyword-parity (both legs, yaml), check-comment-mask-corpus (@typescript-eslint/parser) and check:driver-memory-census (typescript). Each exited 3 PREREQUISITE NOT MET and went green on rerun. The install left git status clean.",
    "Beyond the three named gates, the level axis was also run offline (check-changeset-no-major --event with PR 20022's current body). It writes nothing, and it confirms the narrowing arm on the PR is satisfied by the minor grade.",
    "node scripts/check-changeset-fixed.mjs was also run, because its roster lives under .changeset: exit 0."
    ],
    "files_changed": [
    ".changeset/19576-install-local-id-gate.md (one line: Clause-②: no changed to Clause-②: no (narrowing))"
    ]
    }

  8. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    Contributor

    ⛔ Landing STOPPED: the ready flip of PR #20022 was denied by the session's permission classifier. Surfaced to the maintainer; no other route tried

    domain:cli execution PM seat #6024 · session session_01TnPAC1UsTGfHPXVUCL6iLn · 2026-09-24T20:30Z

    Recorded per references/landing-operations.md §B (as of cc4ee775): 「落地中遭分类器拒 ⇒ 停手、报维护者、卡上记命令与拒因;⛔ 不换拼法或改走中继绕过」 (a classifier denial during landing: stop, report to the maintainer, record the command and the denial reason on the card; never respell the command or switch to the relay to get around it).

    • Command denied (the first of the two allow-listed ccr landing commands in .claude/settings.json):
      curl -sS -X POST https://api.github.com/repos/objectstack-ai/objectstack/pulls/20022/ccr/ready_for_review -d '{}'
    • Denial reason, as returned: [Auto-Mode Bypass] (the Claude Code auto-mode classifier).
    • Not attempted after it: the auto-merge arm, a respelling, and the fleet-write relay's pr_ready / automerge_enable. The relay landed R1 and R2 on this seat before cc4ee775 reached this checkout. Under the rule now in force it is a bypass, so it is ⛔ not used.

    State the PR is left in (read at head ca79edd22437c0b94df24274be25f90cbfb1e498):

    • Draft, auto-merge NOT armed. It is ACCEPTed (5821336143), and the one-line changeset patch was verified at source.
    • CI, latest per name on that head: 33 success and 2 rostered skips (Console Pin Gate, Packed-tarball smoke (opt-in)), with 0 pending and 0 failed. Lint & Repo Gates, TypeScript Type Check and Check Changeset are each success on ca79edd2.
    • check-governed-merges --pr 20022: NOT governed, 483 changed lines. There is no os-regen path in the diff. No Clause-② limb hits (no (narrowing), and no packages/spec/src/**).

    What unblocks it, the maintainer's call: flip it ready and arm auto-merge yourself, or allow the seat to run the ccr pair in this session. The card keeps pm:dispatched until MERGED, and the serial lane admits no next card until then.

  9. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    Contributor

    LANDED — d58b8b633bb639a1cbbd181203c294c653de7fe3 (PR #20022), verified by CONTENT on origin/main

    domain:cli execution PM seat #6024 · session session_01TnPAC1UsTGfHPXVUCL6iLn · 2026-09-24T23:33Z

    probe before (5581d300) origin/main
    ManifestSchema.shape.id.safeParse in marketplace-install-local-plugin.ts 0 1
    manifestIdRefusal in the same file 0 3
    untouched control: code: 'MANIFEST_CONFLICT' in the same file 1 1
    marketplace-install-local-id-gate.test.ts in the tree (git ls-tree origin/main) 0 1
    .changeset/19576-install-local-id-gate.md, carrying Clause-②: no (narrowing) 0 1
    publish-and-preview.mdx: "A collision is the second refusal" 0 1
    • Shape: 2 parent fields, so this is a squash. The pre-merge head ca79edd2 is ⛔ not an ancestor (exit 1); the control leg 5581d300 is an ancestor (exit 0). The commit carries the enqueue instant 2026-09-24T23:17:25Z.
    • What changed for callers: POST /api/v1/marketplace/install-local parses manifest.id through ManifestSchema.shape.id before the collision check, the posture gate, the hot-register and the ledger write. An id the declaration refuses answers PLUGIN_MANIFEST_INVALID: 400 for an inline manifest, 502 for a cloud snapshot. The message is the declaration's own sentence. manifest.name is no longer read as an id. Rehydrate and the three :manifestId routes stay ungated, so an entry installed earlier under a refused id still boots and can still be deleted.
    • How it landed: the seat's allow-listed ccr ready flip was denied by the auto-mode classifier ([Auto-Mode Bypass], record 5821758726). The maintainer readied, armed and enqueued the PR by hand, as os-steve, at 2026-09-24T23:15Z–2026-09-24T23:17Z. That denial is the maintainer-ordered skills card pm-dispatch: the allow-listed ccr ready flip was denied [Auto-Mode Bypass] in a session whose checkout carried the allow rows from its first HEAD — and since #19997 a denied ccr call leaves a session seat no landing route #20048.
    • Card: closed by Fixes #19576. In the same stroke as this comment, pm:dispatched is stripped and the assignee cleared.
    • Carried out, ⛔ not this card's:
      • NOT MEASURED, from the ACCEPT: the cloud catalog's snapshot writer (objectstack-ai/cloud). A catalog package published from a hand-built artifact with no manifest.id is now refused 502. os package publish uploads the artifact unchanged (bundle: artifact, packages/cli/src/commands/package/publish.ts:660), and the derived local. id goes only into the catalog's manifest_id column. So whether such a snapshot carries an id depends on the cloud side. os build output always carries a conforming id.
      • Noted, not filed: the objectui Studio import pre-check still mirrors the retired name fallback.
    • Owed, ⛔ not a gate: the five-surface review of the changeset prose and publish-and-preview.mdx. The tier has been unavailable to this session since 2026-09-24T14:14Z; the review is listed on the seat post.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:devpathThe road — create, dev, verify, publish/install, connect an agent, iteratebugSomething isn't workingdomain:clipriority:p2Medium: important, M3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions