Skip to content

access-security.packaged-flow-write-door-parity clauses 2 and 3 fail on main — detail withheld pending maintainer #20679

Description

@objectstack-fleet

QA-source: #20674 · access-security.packaged-flow-write-door-parity · c2, c3

Checklist run #20674 (acceptance card #12438, ADR-0126) scored clauses 2 and 3 of access-security.packaged-flow-write-door-parity (revision 1) as fail on main at 6bff748b (showcase, stock boot). Each was reproduced twice; clause 1 (the control leg) and clause 4 (no residue) passed.

Detail withheld pending maintainer, per docs/qa/platform-checklist/RUNNER.md rule 2 (the authentication/authorization carve-out). The reproduction stays with the runner session and was reported to the dispatching seat (domain:spec#5).

Full evidence chain for the rest of the run: #20674 (F-4).


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Sep 29, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade — bug · security · priority:p1 · domain:cli · area:access · pm:queue. Direction: the write door goes through the same locked-base check the metadata door applies

    Triage seat (objectstack-wide, seat post #6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-09-29T17:53Z. ⛔ Not a claim, ⛔ not a dispatch.

    Triage: the write door is served by packages/runtime ⇒ domain:cli, by the lane table. The engine is domain:services's, and it is consulted, not edited. The reproduction stays withheld, as the runner filed it, and this grade adds no request detail.

    What the public checklist item already states. access-security.packaged-flow-write-door-parity (docs/qa/platform-checklist/areas/access-security.json) describes the gap at the level of doors:

    • one automation write door accepts a change to a package-backed flow that the metadata door refuses under ADR-0126 §2's locked base;
    • its persona is an administrator who already holds manage_metadata.

    Why p1, not p0.

    • It is reached and reproduced twice on main, so by the security rule it is at least p1.
    • It is not p0: the actor is an administrator already authorized to author metadata. No lower tier and no other organization reaches it, and no data outside that administrator's own authority is exposed.
    • The harm is integrity. A shipped package's locked base can be changed or removed in place through one door, which the lock exists to prevent.

    Direction.

    • The automation write door (update and delete) calls the same locked-base predicate the metadata door uses, and answers the same ledgered refusal family. ⛔ Don't write a second lock implementation.
    • ⛔ Don't put the lock inside the engine's registration: boot registers packaged flows through it legitimately.
    • Pins: the checklist item's own clauses 2 and 3 flip to PASS, with clause 1 (the control) and clause 4 (no residue) still passing.
    • Release: a promised lock that one door doesn't keep. Recommended before the next release, and it is on the triage seat's release list.
  2. added
    area:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guards
    bugSomething isn't working
    priority:p1High: required for production / M2
    and removed on Sep 29, 2026
  3. objectstack-fleet commented on Sep 30, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1 of the domain:cli seat's session session_01VvcEokUG1tvVxkceYfR5XB (batch 3): priority:p1 · security, dispatched first at the maintainer's direction in this session's chat (「20679 20676 优先」)
    Session: session_01VvcEokUG1tvVxkceYfR5XB
    Account: huangyiirene
    Branch: claude/issue-20679-automation-door-package-lock
    Worktree: objectstack-issue-20679
    Domain: domain:cli
    Seat: domain:cli#1
    File surface:

    Stop on breach and explain in the report
    Container & model: M, mode:subagent, model: default tier (opus) (dispatch-gates --tier at 0d9349fe: no path-derived mandate; floor sonnet · default opus · ceiling fable). Security-boundary card, so it gets the judgment tier.
    Clause-②: yes (widening)
    Clause-② reading (re-read by the seat, amended in place): the refusal itself is a runtime authorization and integrity refusal, the negative boundary of clause ②. But the delivered diff adds one public method, packagedBaseRefusal, to the published ObjectStackProtocolImplementation (@objectstack/metadata-protocol). An added export member widens the public surface (references/lanes/spec.md: 「放宽接受集或扩大公开面的卡,不论多小,即条款②」), so the line is yes (widening): minor for @objectstack/metadata-protocol. No new error code; no packages/spec edit. The at-tier contract review is owed before enqueue.
    Thread-read: 5895656793
    Serial constraints cleared: #20726 (domain:services, claim 5903568667) holds the toggle route's DESCRIPTION region of packages/runtime/src/domains/automation.ts. That region is disjoint from the PUT and DELETE arms; only .objectui-sha is a single-claim path, so the later lander resolves any textual conflict. #20725 landed at 0d9349fe (PR #20759): it changed the engine's unregisterFlow (DELETE_RESTRICTED for a reachable packaged subflow), and this dispatch bases on it. None of the 10 open PRs touches automation.ts (file lists read in this act). #20676 (this seat, same round) edits dispatcher-plugin.ts and route-ledger.ts, disjoint files.

    Taken 2026-09-30T03:49Z, main 0d9349fe. The reproduction stays withheld as filed. The dev works from the public checklist item access-security.packaged-flow-write-door-parity (docs/qa/platform-checklist/areas/access-security.json) and the triage direction.


    Generated by Claude Code

  4. objectstack-fleet commented on Sep 30, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
     "issue": 20679,
     "status": "done",
     "branch": "claude/issue-20679-automation-door-package-lock",
     "pr": "https://github.com/objectstack-ai/objectstack/pull/20817",
     "session": "session_01VvcEokUG1tvVxkceYfR5XB — the dispatching PM session's id (subagent run; same id as the parent, per the dispatch).",
     "premise_still_valid": true,
     "summary": "Verified on origin/main before editing: PUT and DELETE /automation/:name ran only the manage_metadata gate and went straight to registerFlow / unregisterFlow. The /meta door's locked-base verdict for a flow was private to ObjectStackProtocolImplementation: isArtifactBacked plus isOverlayAllowed. It is enforced in the protocol on environment kernels and by SysMetadataRepository.assertAllowed at the write on host-config kernels. It is not spec's evaluateLockFor* (that is the ADR-0010 per-item _lock) and not the permission-set lock. Fix: packages/metadata-protocol gains one public method, packagedBaseRefusal({ type, name, operation }). The seat adopted it as a cross-lane surface and re-declared the claim as Clause-② yes (widening). saveMetaItem's and deleteMetaItem's inline package doors are lifted verbatim into two private helpers that both methods still call; diff -w on the lifted bodies reads 0 changed lines (80/80 and 12/12). packages/runtime automation.ts asks that verdict with the flow NAME only, before the engine is called, and relays the refusal (403 NOT_OVERRIDABLE, the producer's ledgered code and sentence). It does this at PUT /:name, at DELETE /:name, and, as the seat-adopted bounded in-place fix, at POST / onto an existing name. POST / onto a packaged name measured 200 with an overwrite at 3690c44b before the fix. The lock sits ahead of the engine's DELETE_RESTRICTED / 409. Customer flows, clone, toggle and the OS_METADATA_WRITABLE hatch stay open. A dogfood pin over the booted showcase shows checklist clauses 1-4 passing.",
     "tests": "Final head f5ea00604: every runtime automation-*.test.ts 25 files / 478 passed; runtime typecheck exit 0 (check:test-typecheck OK); protocol pins 27 passed; dogfood pin 5 passed after a runtime rebuild. At 3690c44b9 (merge over #20726): runtime local 292 files / 4215 passed, 1 skipped; runtime repo 727 passed; metadata-protocol 191 files passed, 3 skipped / 2801 passed, 19 skipped; typecheck exit 0; objectql, the 20 files pinning NOT_OVERRIDABLE / NOT_CREATABLE / ITEM_LOCKED, 362 passed; rest 3 files 41 passed. Ablations (commit first; scripts/ablation-replace.mjs with anchor hit and blob change; ablation-dist-preflight with the marker in dist for rebuilt legs; every restore proven by blob == HEAD, git diff HEAD empty, tree clean, marker absent): door helper disabled: runtime pin 7 red / 7 green, dogfood 3 red / 2 green; packagedBaseRefusal returns null (metadata-protocol rebuilt): protocol pin 6 red / 3 green, runtime pin 6 red / 8 green; re-raise discriminator widened: protocol pin 1 red / 8 green; POST call removed at f5ea00604: runtime pin 1 red / 14 green. All predicted before running; all pins green again after each restore.",
     "mcp_calls": "0 — no MCP GitHub tool used. Reads were REST GETs (issue, comments, pulls, check-runs); two SendMessage replies went to the coordinator, which are not GitHub calls.",
     "api_writes": "3 — all through the fleet-write relay as objectstack-fleet[bot], one repository_dispatch each: (1) pr_create → POST /repos/objectstack-ai/objectstack/pulls (draft #20817, body read back byte-identical, 11499 bytes); (2) label-write assign → POST /repos/objectstack-ai/objectstack/issues/20817/assignees (huangyiirene, read back MATCHES); (3) this os-dev-report comment → POST /repos/objectstack-ai/objectstack/issues/20679/comments. git push is not counted (it is not a REST write).",
     "open_questions": [],
     "out_of_scope_findings": [
      "class: b · reach: public door + exception: release-text. At f5ea00604, PUT /api/v1/automation/:name on the showcase's packaged flow answers 403 NOT_OVERRIDABLE with the shared sentence, and /meta gives the same verdict. That sentence names 'edit the source artifact and redeploy, or set OS_METADATA_WRITABLE'. ADR-0126 §2 (Regime C) says the in-place edit is 'refused loudly at the write door, the refusal naming the sanctioned path', and for a packaged flow the sanctioned path is clone (§7.1) and the enable/disable switch (§7.2). Neither is named. Seam: ADR-0126 §2 → runtime: packages/metadata-protocol/src/protocol.ts refusePackagedBaseOverride / refusePackagedBaseRemoval NOT_OVERRIDABLE sentences (relayed by packages/runtime/src/domains/automation.ts refusePackagedFlowBaseChange) · evidence: dogfood pin output at f5ea00604 · dedupe words: packaged flow refusal sentence sanctioned path clone NOT_OVERRIDABLE",
      "carrier: none (承接者:无) · noted, not filed. On the showcase composition the /meta door is served by the REST server, and its refusal envelope is { error: string, code }. /automation (the dispatcher) answers { success, error: { code, message } }. So one refusal arrives in two envelopes. Pre-existing; recorded in the PR's Acceptance notes.",
      "carrier: none (承接者:无) · noted, not filed. @objectstack/rest declares @objectstack/metadata-protocol as a devDependency, and its built dist inlines a copy of ObjectStackProtocolImplementation. Pre-existing; recorded in the PR's Acceptance notes."
     ],
     "gates": {
      "node scripts/check-adr-0087-registration.mjs --base origin/main": "exit 0 · ✓ check-adr-0087-registration: this PR adds no declared-breaking changeset (1 non-breaking changeset(s) seen).",
      "node scripts/check-adr-0087-registration.mjs --self-test": "exit 0 · ✓ check-adr-0087-registration --self-test: 441 assertions over real temp git repos (real scan()/assertInputs()",
      "node scripts/check-changeset-no-major.mjs --base origin/main": "exit 0 · · no `pull_request` payload was available to read a declaration from",
      "node scripts/check-changeset-no-major.mjs --self-test": "exit 0 · ✓ check-changeset-no-major --self-test: 339 assertions (frontmatter dialects measured against @changesets/pars",
      "node scripts/check-ci-filter-parity.mjs": "exit 0 · OK: all 187 declared cross-package glob(s) (134 unique) are covered by `core` or `crosspkg`, every `crosspkg` ",
      "node scripts/check-closing-keyword-parity.mjs": "exit 0 · • packages/spec/CHANGELOG.md -- 7574210 bytes exceeds the sweep's 2097152-byte cutoff for UNREGISTERED files",
      "node scripts/check-closing-keyword-parity.mjs --self-test": "exit 0 · ✓ check-closing-keyword-parity --self-test: 40 assertions, 5 mutations of the shipped parsers each driven to r",
      "node scripts/check-comment-mask-adoption.mjs": "exit 0 · OK check:comment-mask-adoption — 14 private comment-stripper(s) under packages/** + examples/**, all 14 record",
      "node scripts/check-comment-mask-adoption.mjs --self-test": "exit 0 · PASS check-comment-mask-adoption --self-test (0 failure(s))",
      "node scripts/check-comment-mask-corpus.mjs": "exit 0 · ✓ comment-mask corpus sweep [scripts/js-comment-mask.mjs]: 7610 files, 0 disagree, 0 unparseable, 123.5s (comp",
      "node scripts/check-empty-changeset.mjs --base origin/main": "exit 0 · ✓ No changeset from the merge base modified or deleted by this diff (#17712).",
      "node scripts/check-empty-changeset.mjs --self-test": "exit 0 · ✓ check-empty-changeset --self-test: 159 assertions over real temp git repos (real scan() path)",
      "node scripts/check-issue-citations.mjs": "exit 0 · ✅ check-issue-citations: every citation this change adds resolves (or is a declared cross-repo reference).",
      "node scripts/check-keyed-text-bounds.mjs": "exit 0 · ⚠ The delta is information, not a verdict — this population grows AND shrinks for good reasons, and only the f",
      "node scripts/check-keyed-text-bounds.mjs --self-test": "exit 0 · PASS check-keyed-text-bounds --self-test (0 failure(s))",
      "node scripts/check-platform-object-tenancy-census.mjs": "exit 0 · ✓ platform-object tenancy census matches the tree: 82 platform-namespace objects, 56 in the machinery's reach,",
      "node scripts/check-platform-object-tenancy-census.mjs --self-test": "exit 0 · ✓ check-platform-object-tenancy-census self-test: all checks pass (82 objects, 26 outside the machinery)",
      "node scripts/check-plugin-teardown-shape.mjs": "exit 0 · ✓ check:plugin-teardown-shape: 67 Plugin implementation(s) across 7040 source(s) under packages/**; every tear",
      "node scripts/check-plugin-teardown-shape.mjs --self-test": "exit 0 · ✓ check-plugin-teardown-shape self-test: 48 cases pass (real pre-#10375 fixture reds, the repaired file and bo",
      "node scripts/check-registry-log-declared.mjs": "exit 0 · examples/app-showcase — S1 constructs a SchemaRegistry in its tests",
      "node scripts/check-registry-log-declared.mjs --self-test": "exit 0 · self-test OK: 16 cases + level-vocabulary read + population declaration + real-tree selection floor.",
      "node scripts/check-rest-log-spy-declared.mjs": "exit 0 · OK: 30 of 233 test file(s) beside packages/rest/src/log.ts observe the fault log, and every one of them declar",
      "node scripts/check-rest-log-spy-declared.mjs --self-test": "exit 0 · check-rest-log-spy-declared self-test reached its verdict: 23 case(s), 0 failure(s).",
      "node scripts/check-system-context-census.mjs": "exit 0 · check-system-context-census: OK — 106 elevation read sites in 19 packages across 45 files, living in 89 symbol",
      "node scripts/check-system-context-census.mjs --self-test": "exit 0 · check-system-context-census --self-test: all cases passed",
      "node scripts/check-undeclared-dep-imports.mjs": "exit 0 · ⚠ The delta is information, not a verdict — the floors are `›=` and cannot see an upward drift at all, which i",
      "node scripts/check-undeclared-dep-imports.mjs --self-test": "exit 0 · PASS check-undeclared-dep-imports --self-test (0 failure(s))",
      "node scripts/docs-audit/check-affected-docs.mjs": "exit 0 · → the unreachable rows themselves: this command with --json",
      "node scripts/docs-audit/check-drift-comment.mjs": "exit 0 · ✓ check-drift-comment: 66 cases pass across 5 fixture diff(s).",
      "node scripts/pm/release-rehearsal-clone.mjs --self-test": "exit 0 · ✓ self-test passed",
      "pnpm --filter @objectstack/spec run check:duration-unit-keys": "exit 0 · ✓ check:duration-unit-keys — 199 unit-declaring numeric key(s) across 2748 source file(s) all carry their unit",
      "pnpm --filter @objectstack/spec run check:empty-state": "exit 0 · ✓ all classified (2 closed, 2 open, 4 output, 9 scope)",
      "pnpm --filter @objectstack/spec run check:liveness": "exit 0 · (not a completeness claim about the 531 child key(s) under the declared blanket verdicts above — those are rec",
      "pnpm --filter @objectstack/spec run check:strictness-ledger": "exit 0 · untriaged: 1200 object site(s) across 9 director(ies)",
      "pnpm --filter @objectstack/spec run check:variant-docs": "exit 0 · ✓ variant/doc gate: 18 discriminated union(s) — 8 governed (every variant mentioned in a bound doc), 10 exempt",
      "pnpm check:changeset-gate-self-tests": "exit 0 · ✓ check-changeset-no-major --self-test: 339 assertions (frontmatter dialects measured against @changesets/pars",
      "pnpm check:cross-package-test-inputs": "exit 0 · OK: 29 package(s) read outside themselves, all declared, and turbo.json hashes every declared glob (6 of them ",
      "pnpm check:dispatcher-error-vocabulary": "exit 0 · [#15723] the ARGUMENT POSITION of new APIError( … ) and APIError.from( … ) IS now in this gate's population — ",
      "pnpm check:doc-authoring": "exit 0 · ✓ doc authoring guard: sibling-package prose ids hold the baseline — 794 pinned site(s) across 227 file(s), 92",
      "pnpm check:driver-memory-census": "exit 0 · check-driver-memory-census: OK — every declaration is ledgered, every ledger entry is live, and every ruled fi",
      "pnpm check:dts-closure": "exit 0 · check-dts-closure: 71 built package(s) swept - 167/167 declared declaration file(s) present across 71 package(",
      "pnpm check:dual-build-cjs-loads": "exit 0 · ⚠ The delta is information, not a verdict — this population grows AND shrinks for good reasons, and only the f",
      "pnpm check:durability-log-level": "exit 0 · ✓ read-seam invention (#5186 + #6451 + #9165, 3 package roots, vocabulary find/findOne/count): 68 read seam(s)",
      "pnpm check:engine-double-contract": "exit 0 · ⚠ The delta is information, not a verdict — this population grows AND shrinks for good reasons, and only the f",
      "pnpm check:filter-alias-parity": "exit 0 · check:filter-alias-parity: OK (4 transport spelling(s) of the `where` slot, identical on both sides: $filter, ",
      "pnpm check:gitlink-declared": "exit 0 · check-gitlink-declared: OK (9424 index entries -- 0 gitlink(s) at mode 160000; no .gitmodules in the index, so",
      "pnpm check:issue-citations": "exit 0 · ✅ check-issue-citations --self-test: grammar narrowed, qualifier a closed set of repositories, four 404 causes",
      "pnpm check:lean-entry-closure": "exit 0 · Admitted set held exactly (15 packages); 6 denied names absent.",
      "pnpm check:logger-receiver-detach": "exit 0 · control corpus fired on all five detach shapes in this same run, and stayed silent on the measured `console` a",
      "pnpm check:nul-bytes": "exit 0 · check-nul-bytes: OK (scanned 9417 text file(s) -- 9417 tracked, 0 untracked-not-ignored; skipped 7 binary; no ",
      "pnpm check:objectql-double-limit": "exit 0 · baseline key set verified against 8acdae9: no files added.",
      "pnpm check:objectui-changeset": "exit 0 · ✓ objectui-range --self-test: all checks passed",
      "pnpm check:org-identifier": "exit 0 · check-org-identifier: OK (3044 author-facing source file(s), 17 session binding(s) resolved, no removed sessio",
      "pnpm check:page-declaration-shape": "exit 0 · blind spot: 1 computed carrier(s) no source scan can enumerate — examples/app-crm/objectstack.config.ts:86.",
      "pnpm check:pm-changeset-deadline-census": "exit 0 · ✓ changeset-deadline-census --self-test: all cases passed across 5 batteries (what counts as a named target, t",
      "pnpm check:published-files": "exit 0 · ✓ check:published-files — 69 publishable package(s) of 80 workspace member(s) declare a `files` whitelist that",
      "pnpm check:query-options-erasure": "exit 0 · baseline key set verified against 8acdae9: no files added.",
      "pnpm check:refd-timer-probe": "exit 0 · 1 code site(s), all inside the approved module, which is present and still reads it.",
      "pnpm check:route-envelope": "exit 0 · read/write discriminator: 11 file(s) skipped as fetch readers (77 zero-argument `res.json()` call(s), none swe",
      "pnpm check:slot-lookup": "exit 0 · baseline key set verified against 8acdae9: no files added.",
      "pnpm check:sourcemap-no-sources-content": "exit 0 · check-sourcemap-no-sources-content: 68 built package(s) swept - 518 map(s), none embed source text.",
      "pnpm check:test-source-alias": "exit 0 · check-test-source-alias OK — 73 packages with tests scanned; 60 registered as still resolving a workspace dep ",
      "pnpm check:tier-file-adoption": "exit 0 · @objectstack/cli — 75 file(s); imports readTierMode, selectTierFiles from scripts/nightly-tiers.mjs (via packa",
      "pnpm check:type-check-coverage": "exit 0 · ⚠ The delta is information, not a verdict — this population grows AND shrinks for good reasons, and only the f",
      "pnpm check:type-check-debt": "exit 0 · surplus: none — every entry sits exactly at its measurement, so any new error is red.",
      "pnpm check:watch-hint-literal": "exit 0 · ✓ check-watch-hint-literal: 71 declaration(s) across 4 rostered name(s) -- ROOT_DIR_WATCH_HINTS 47, ROOT_FILE_",
      "pnpm check:where-matcher": "exit 0 · baseline key set verified against 8acdae9: no files added.",
      "node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran ran.list": "exit 0 · ✓ dispatch-gates --ran: 67 derived famil(ies) accounted for — 67 run, 0 NOT-MEASURED (a DERIVED zero — all 67 recorded an exit code and none of them is 3). Derived at f5ea00604, merge base 8acdae9d8.",
      "pnpm check:error-code-casing (roster, error-code family)": "exit 0",
      "pnpm --filter @objectstack/spec check:error-code-provenance (roster, error-code family)": "exit 0 · OK — every registered-code stamp site is listed under its own owner key or carries a recorded waiver (330 sites: 313 listed, 17 waived)",
      "pnpm lint (narrowed, proven)": "eslint --no-inline-config --format json on the 6 changed .ts files at f5ea00604: 6 files linted, 0 errors, 0 warnings, exit 0. Population: eslint.config.mjs file globs (**/*.{ts,...} and packages/**/*.{ts,...}) cover all 6. Invariance: the config enables no type-aware linting (no parserOptions.project, no typed rules, stated in eslint.config.mjs) and its 4 plugins are local AST rules, so the diff cannot move an untouched file's verdict.",
      "CI on PR #20817 at f5ea00604 (read 08:30Z)": "in_progress: 32 check runs, 12 success, 3 skipped, 16 in_progress, 1 queued, 0 failed"
     },
     "deviations": [
      "Surface: the claim's stop-line ('stop if a new export is needed') conflicted with the dispatch ('fix producer-side outside packages/spec'). I followed the dispatch. The seat adopted the metadata-protocol method and its pins onto the claim and re-declared Clause-② yes (widening), which is copied as PR body line 2. The changeset stays metadata-protocol minor / runtime patch, with no BREAKING banner.",
      "Surface: the POST / arm was added as a bounded in-place fix at the seat's instruction, after I reported the measured create-overwrite. The claim was updated in the same round.",
      "Surface: per dispatch mechanism-assumption 5, there is a real-HTTP dogfood pin in packages/qa/dogfood/test/packaged-flow-write-door-parity.dogfood.test.ts. The fold-population pin packages/metadata-protocol/src/protocol.read-verb-canonical-fold.test.ts gained the new verb, because the derived population pins its size.",
      "PM mechanism assumptions: #1 confirmed. #2: the real predicate was private to the protocol class, not the spec helpers or the permission-set lock; reusing it needed a metadata-protocol public method but no spec export and no new code. #3 confirmed; the ordering is documented. #4: all pinned. #5: the dispatcher mounts the route (confirmed), and the dogfood pin exercises it over HTTP.",
      "The protocol pin resolves the verdict by the flow name. Asked mid-task by the seat: the verdict reads SchemaRegistry.getArtifactItem (packages/objectql/src/registry.ts:3919), the loader-registered PACKAGE_ID:NAME entries, filtered by isCodeArtifactBody on that registry entry. It never reads the request body or the engine's registered flow, and a pin proves body stamps decide nothing both ways.",
      "Merged origin/main 4 times (the last merge base is 8acdae9d8, which includes #20726). Not merged: the 2 later commits (#20779 clone mount on the dispatcher bridge, #20788 docs); they touch neither changed file.",
      "Tooling slip: one early metadata-protocol run used 'pnpm test -- --maxWorkers=2'. vitest dropped the flag, which ran the intended full suite with default workers. Later runs used exec vitest run. Two long foreground commands were auto-backgrounded by the harness at the 600s cap; I waited in the foreground with tail --pid and read the logs.",
      "check-changeset-no-major ran locally with no pull_request payload, so the Clause-② level axis was not read locally ('no pull_request payload was available'). CI reads the PR body; metadata-protocol is graded minor.",
      "CI is not awaited: it was in_progress at report time (see gates)."
     ],
     "files_changed": [
      "packages/runtime/src/domains/automation.ts",
      "packages/runtime/src/domains/automation-packaged-base-lock.test.ts",
      "packages/metadata-protocol/src/protocol.ts",
      "packages/metadata-protocol/src/protocol.packaged-base-refusal.test.ts",
      "packages/metadata-protocol/src/protocol.read-verb-canonical-fold.test.ts",
      "packages/qa/dogfood/test/packaged-flow-write-door-parity.dogfood.test.ts",
      ".changeset/20679-automation-door-package-lock.md"
     ]
    }

    Generated by Claude Code

  5. objectstack-fleet commented on Sep 30, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
     "issue": 20679,
     "status": "done",
     "branch": "claude/issue-20679-automation-door-package-lock",
     "pr": "https://github.com/objectstack-ai/objectstack/pull/20817",
     "session": "session_01VvcEokUG1tvVxkceYfR5XB — the dispatching PM session's id (subagent run; same id as the parent, per the dispatch).",
     "premise_still_valid": true,
     "summary": "Patch round 1: the contract review's changeset wording finding. New head e4006d685 (e4006d68594d433159b1a0e8addd9585e5929f59), one commit on top of the reviewed f5ea00604. In .changeset/20679-automation-door-package-lock.md, one sentence changed. Before: 'All three now answer `403` `NOT_OVERRIDABLE` for a packaged flow, with the same message the metadata door gives.' After: 'All three now answer a packaged flow with the same code and status the metadata door gives (`403` `NOT_OVERRIDABLE`), and with the same sentence wherever the metadata protocol's own package door answers.' The new wording is true on both topologies: on a host-config kernel /meta's refusal comes from SysMetadataRepository.assertAllowed with the repository's own sentence. The rest of the changeset is byte-identical (git diff: 1 insertion, 1 deletion, that line only), including the line-initial 'Clause-②: yes (widening)'. No code or test file touched, no PR body edit, no merge of main (PR reads mergeable: true after the push). The worktree was recreated on the existing branch after a fetch, with no new branch.",
     "tests": "No code or test changed in this round, so no test was re-run. The code and tests are unchanged from f5ea00604, the head the contract review passed; their readings are in the previous os-dev-report (comment 5907350558).",
     "mcp_calls": "0 — no MCP GitHub tool used. Reads were REST GETs (PR state after the push).",
     "api_writes": "1 — this os-dev-report comment → POST /repos/objectstack-ai/objectstack/issues/20679/comments, through the fleet-write relay as objectstack-fleet[bot]. git push is not counted (it is not a REST write).",
     "open_questions": [],
     "out_of_scope_findings": [],
     "gates": {
      "node scripts/check-empty-changeset.mjs --base origin/main": "exit 0 · ✓ No changeset from the merge base modified or deleted by this diff (#17712).",
      "node scripts/check-changeset-no-major.mjs --base origin/main": "exit 0 · ✓ This diff introduces no `major` bump. · no `pull_request` payload was available to read a declaration from (level axis read by CI from the PR body)",
      "node scripts/check-adr-0087-registration.mjs --base origin/main": "exit 0 · ✓ check-adr-0087-registration: this PR adds no declared-breaking changeset (1 non-breaking changeset(s) seen).",
      "pnpm check:doc-authoring": "exit 0 · ✓ doc authoring guard: sibling-package prose ids hold the baseline — 794 pinned site(s) across 227 file(s), 92127 string(s) read in 1260 parsed source(s), no growth, no burn-down unrecorded."
     },
     "deviations": [
      "None in this round. Scope was exactly the seat's instruction: one changeset phrase. The four named gates were run on the committed head before the push; every exit code is recorded in gates."
     ],
     "files_changed": [
      ".changeset/20679-automation-door-package-lock.md"
     ]
    }

    Generated by Claude Code

  6. objectstack-fleet commented on Sep 30, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT: PR #20817 at e4006d68 (the packaged-flow lock on the automation definition doors)

    domain:cli seat · session_01VvcEokUG1tvVxkceYfR5XB · 2026-09-30T09:28Z

    • Contract review of record: 5908296340 on the PR, CONTRACT_REVIEW_TIER, head e4006d68, PASS.
      • It is a delta re-review over the review of f5ea0060, whose one wording finding in the changeset was fixed in patch round 1 (one line).
      • The seat verified the load-bearing claim itself: diff -w of both lifted package doors against the merge-base reads 0 changed lines, and the code blobs are identical at both heads.
    • Checklist:
      • Draft, base main, first line Fixes #20679, second line Clause-②: yes (widening), and no other closing keyword (scanned).
      • 7 files, +1012 / −93, all on the claim's surface as amended in place three times. The amendments cover the cross-lane metadata-protocol method (with a notice to domain:engine, 5905804651), the POST / bounded in-place fix, and the two test-only pins.
      • Changesets: @objectstack/metadata-protocol minor, @objectstack/runtime patch.
      • check-governed-merges --pr 20817: NOT governed.
      • 35 check runs on the head: 32 success and 3 skipped. mergeable_state reads clean.
    • Checklist item access-security.packaged-flow-write-door-parity: clauses 1–4 pass over the booted showcase in the dogfood pin. Its "EXPECTED FAIL today" notes go stale on landing, a checklist-author follow-up. Noted, not filed.
    • Out-of-scope findings:
    • Next: land through the queue. At the merge, the seat closes this card if the Fixes does not, removes pm:dispatched, and releases automation: a flow created through the authoring door can assert package provenance, and the ADR-0126 guards and the activation ledger then treat it as package-shipped #20761 (Blocked-by: #20679) back to pm:queue for its stage 2.

    Generated by Claude Code

  7. objectstack-fleet commented on Sep 30, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: PR #20817 → 4b45afaed5 (the packaged-flow lock on the automation definition doors)

    domain:cli seat · session_01VvcEokUG1tvVxkceYfR5XB · 2026-09-30T09:54Z


    Generated by Claude Code

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

Metadata

Metadata

Assignees

Labels

area:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guardsbugSomething isn't workingdomain:clipriority:p1High: required for production / M2security

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions