Skip to content

automation: boot-time flow precedence still classifies its contenders from body stamps, not the loader's set (the remainder of #20761's ruling rule 1) #20864

Description

@objectstack-fleet

This card carries the precedence half of #20761's ruling rule 1. #20761 keeps nothing: PR #20853 delivers the rest of stage 2, and the card closes when it lands. An in-flight-derived sub-issue: it carries the parent's domain:cli and priority:p1 and goes straight to pm:queue. Filed by the domain:cli execution seat (#6024, session session_01VvcEokUG1tvVxkceYfR5XB). Part of #20761.

⚠️ Disclosure discipline (inherited from #20761): no request body, header or field spelling on this card or its PR.

The ruling it completes

Ruling B + A, recorded by triage in 5904938166, rule 1: "The engine's classification (the §7.3 guards, the toggle door, precedence) reads that set, not the stamps a flow body carries."

What remains

Direction

Route the precedence classifier through the same reader the engine now holds (setPackagedFlowSource / packagedFlowOwner), so the one server-held fact decides every reader rule 1 names.

Dedupe

It is new: the residual was first named by the PR #20853 record at f0de8fe69. The #20761 thread is its only prior mention.


Generated by Claude Code

Activity

  1. added
    bugSomething isn't working
    priority:p1High: required for production / M2
    area:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guards
    and removed on Sep 30, 2026
  2. 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, the in-flight-derived sub-issue of #20761 (closed by PR #20853 as 76bd58fac4). It carries ruling rule 1's remaining reader, boot-time flow precedence.
    Session: session_01VvcEokUG1tvVxkceYfR5XB
    Account: huangyiirene
    Branch: claude/issue-20864-precedence-loader-set
    Worktree: objectstack-issue-20864
    Domain: domain:cli
    Seat: domain:cli#1
    File surface (the domain:services files are declared cross-lane, as on #20761):


    Generated by Claude Code

  3. objectstack-fleet commented on Sep 30, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 20864,
      "status": "done",
      "branch": "claude/issue-20864-precedence-loader-set",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/20880",
      "session": "session_01VvcEokUG1tvVxkceYfR5XB",
      "premise_still_valid": true,
      "summary": "Boot-time flow precedence now classifies its same-named contenders by the loader's set: resolveFlowPrecedence(items, logger?, packagedFlowOwner?) and describeFlowContender(item, packagedFlowOwner?) take the PackagedFlowSource reader, a contender is packaged only when the set holds its name (inside a held name the registry's own per-entry artifact test, isCodeArtifactBody, tells the loader's entries from a same-named tenant row), and with no reader nothing is packaged (the engine's fail-closed answer). The plugin's one call site passes the engine's own packagedFlowOwner (fed by packagedFlowReader since init), so precedence and every other reader ask one source; the package-id tie-break now applies within the packaged rank only, so a body's own id no longer orders tenant-ranked contenders (they keep arrival order). Premise measured before the change at 72f8c3820 (scratch vitest, real SchemaRegistry): a body stamped as a package's for a name no set holds ranked package; on a real registry filled with the loader's and the hydration's shapes every code-shaped entry's name was in the set, so as the #20853 record read it is not reachable at boot (defence in depth). After the change the same registry shapes arm the same flows with the same receipts.",
      "tests": "All at head 6a028afb2b. pnpm --filter @objectstack/service-automation test: Test Files 159 passed, Tests 1995 passed. pnpm --filter @objectstack/service-automation typecheck: tsc clean; check:test-typecheck OK (test layer compiles, 0 debt). New src/flow-precedence-loader-set.test.ts: 11 pins (8 pure incl. fail-closed and one-question-per-contested-name, 3 boots of the real AutomationServicePlugin: held name via a protocol stand-in's packagedArtifactOwner, unheld name, no protocol service). src/flow-name-shadowing.test.ts (#11997) now hands precedence the registry artifact-view owner through engine.packagedFlowOwner as the pull does; expectations unchanged. Dogfood regression packages/qa/dogfood/test/flow-provenance-server-held.dogfood.test.ts: 13/13 passed against a rebuilt dist (turbo build of the dogfood closure, 63 tasks; dist marker grep counts 1 and 1 for both changed spots). Ablations (fix committed first; each via scripts/ablation-replace.mjs in WRAP mode plus a shell EXIT/INT/TERM restore trap on absolute paths; subject imported relatively from src so no dist on the path): A1 classifier ignores the set → 6 of 11 pins red; A2 boot pull stops passing the reader → 1 red (held-name plugin boot); A3 id tie-break orders tenant-ranked contenders → 1 red (arrival-order pin). Each: anchor 1 → 0, blob changed (a0495f243037 → 4212e0f53183, 0cbd6808a8aa → 46745880e437, a0495f243037 → d33a4cb2e9a3), restored blob == HEAD blob and git diff HEAD empty; final shell check re-proved both files' blobs equal HEAD. Direction: red as predicted in all three.",
      "mcp_calls": "0 — no MCP GitHub tool called (reads were single-card REST reads with curl; writes went through scripts/pm tools)",
      "api_writes": "3 — three POST /repos/objectstack-ai/objectstack/dispatches strokes through the fleet-write relay, executed as objectstack-fleet[bot]: (1) pr_create → POST /repos/objectstack-ai/objectstack/pulls (#20880, draft forced, 8716 bytes sent = stored); (2) label-write --assign huangyiirene → POST /repos/objectstack-ai/objectstack/issues/20880/assignees (read back: assignee huangyiirene; size/m was added by the size labeler, not this write); (3) post-stamped → POST /repos/objectstack-ai/objectstack/issues/20864/comments (this os-dev-report). Plus git push (not REST). No label writes: the dispatch named none and the diff carries a changeset.",
      "open_questions": [],
      "out_of_scope_findings": [
        "carrier: none (承接者:无) · noted, not filed — per-name reader limit: inside a name the set holds, a code-shaped entry that is not the loader's (a forged package id with no tenant marker) still ranks packaged under its own id and can win the lexicographic order (after-change measurement M2). Not reachable at boot: every code-shaped registry entry is the loader's or carries the artifact's grafted envelope (M3). Closing it needs a per-entry answer from metadata-protocol's packagedArtifactOwner, outside this card's surface. PR Acceptance note 1.",
        "carrier: none (承接者:无) · noted, not filed — a stored row of a held name hydrates with the artifact's envelope grafted, ranks packaged beside the loader entry, and kernel phase order arms the loader body; the receipt lists the package twice. Unchanged by this PR (M3 identical before and after); ADR-0126 §7.1 leaves shadow diagnostics to #11997. PR Acceptance note 2.",
        "carrier: the next PR touching FlowContender in packages/services/service-automation/src/engine.ts · noted, not filed — its docblock still describes the package source as the per-body artifact test alone; engine.ts is outside this card's file surface. PR Acceptance note 3."
      ],
      "gates": "Local, at 6a028afb2b: node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands (no paths; merge base 72f8c3820, 5 paths) → 61 commands, all run, every exit 0; --ran with exit-coded lines → 61 derived, 61 run, 0 NOT-MEASURED, 0 UNRUN. check:dual-build-cjs-loads first exited 3 PREREQUISITE NOT MET (8 unbuilt packages), measured exit 0 after building them (41 turbo tasks, all cache hits). The PM's pre-dispatch list (55 lines) is a subset; the 6 added families (engine-double-contract, objectql-double-limit, query-options-erasure, type-check-coverage, type-check-debt, where-matcher) all ran green. Extra: check:startup-registry-verdict and check:durability-log-level exit 0. Lint as a proven narrowing (not pnpm lint): population from eslint --print-config on the 4 changed TS files (6 rules src, 5 tests, none ignored; the changeset is outside the TS/JS files glob); --format json: 4 files, 0 errors, 0 warnings; invariance: no resolved config sets parserOptions.project or projectService and the custom rules read only the linted AST plus an untouched config-level baseline. CI on PR #20880 at 6a028afb2b, one read at report time: 31 check-runs, 9 success, 3 skipped, 19 in_progress, 0 failing → in_progress.",
      "deviations": [
        "Clause-② re-declared 'yes (widening)' (claim said 'no'): two barrel-exported functions gain an optional parameter; the claim instructed this re-declaration if the public surface grew. Changeset is minor.",
        "The package-id tie-break was restricted to the packaged rank (tenant-ranked contenders keep arrival order). It sits in flow-precedence.ts (claimed file) and aligns the code with the function's own docblock; without it a body's own id would decide which tenant-ranked contender is armed. Consequence stated in PR Acceptance note 4: a composition with no reader orders two packages of one bare name by arrival.",
        "Commit trailers are the model-free pair and the PR footer is the session-URL form, per AGENTS.md and the os-dev contract, not the harness attribution block.",
        "Two temporary measurement test files (src/zz-measure-20864.tmp.test.ts) were created in the worktree and deleted after each run; never committed.",
        "The worktree is removed (node_modules deleted first, git worktree remove without force)."
      ],
      "files_changed": [
        ".changeset/20864-precedence-loader-set.md",
        "packages/services/service-automation/src/flow-precedence.ts",
        "packages/services/service-automation/src/plugin.ts",
        "packages/services/service-automation/src/flow-name-shadowing.test.ts",
        "packages/services/service-automation/src/flow-precedence-loader-set.test.ts"
      ]
    }

    Generated by Claude Code

  4. objectstack-fleet commented on Sep 30, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT: PR #20880 at 6a028afb (boot-time flow precedence classifies contenders by the loader's set, the last reader of #20761's ruling rule 1)

    domain:cli seat · session_01VvcEokUG1tvVxkceYfR5XB · 2026-09-30T15:24Z · ⛔ no request body, header or field spelling

    • Contract review of record: 5914326577 on the PR, CONTRACT_REVIEW_TIER, head 6a028afb, PASS.
    • Seat verification on adoption: the record, the changeset, the PR body and every added test title carry 0 provenance-field spellings (scanned). A local git merge-tree against the current origin/main is clean.
    • Checklist:
      • Draft, base main, first line Fixes #20864, and Clause-②: yes (widening). The dev re-declared it from the claim's conditional no because two barrel-exported functions gain an optional parameter; the record judges it right.
      • 5 files, +407 / −34, all on the claim's surface. Changeset: @objectstack/service-automation minor.
      • NOT governed. 34 check-run names: 31 success and 3 skipped.
    • The record's residuals, none blocking:
      • Residual 1, unreachable at boot. Inside a name the set holds, a code-shaped entry that is not the loader's could still rank packaged. The record traced every producer of the precedence input after PR fix(automation): which flows are packaged is the loader's fact, and every flow written through an authoring door is tenant-authored (#20761) #20853 and found none that makes that shape. It is a hardening follow-up (a per-entry answer from the protocol, domain:engine's surface). Acceptance notes; not filed, since it has no reach.
      • Residual 2, an unmeasured source reading, carried by this seat. For a packaged name that also has a stored row (only pre-lock rows or the writable hatch can hold one now), the kernel:ready sync may re-arm the stored body after the boot pull armed the loader's. The filing gate asks for a measured reach, so this seat measures it on a real boot at its next free slot (a measure-only dispatch) and files it if it reproduces. It is recorded on the seat post so the wait is visible.
      • The describeFlowContender docblock overstates the in-name limit; it folds into Residual 1.
      • When two packages ship one bare name, the activation attribution names the set's owner while precedence arms the lexicographic first. Pre-existing, ADR-0048 §3.4. Noted.
    • Out-of-scope findings: the stale FlowContender docblock in engine.ts rides the next PR touching that type. Noted, not filed.
    • Next: land through the queue. At the merge, the seat closes this card if the Fixes does not, and removes pm:dispatched.

    Generated by Claude Code

  5. objectstack-fleet commented on Sep 30, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: PR #20880 → 27bf358ec7 (boot-time flow precedence classifies contenders by the loader's set)

    domain:cli seat · session_01VvcEokUG1tvVxkceYfR5XB · 2026-09-30T15:57Z · ⛔ no request body, header or field spelling


    Generated by Claude Code

  6. objectstack-fleet commented on Sep 30, 2026

    @objectstack-fleet
    ContributorAuthor

    Residual 2 reproduced on a real boot, and filed as #20913

    domain:cli seat · session_01VvcEokUG1tvVxkceYfR5XB · 2026-09-30T17:46Z · ⛔ no request body, header or field spelling · ⛔ no state change (this card stays closed)

    This closes the commitment in ACCEPT 5914340253.


    Generated by Claude Code

  7. added 2 commits that reference this issue on Oct 7, 2026
    27bf358
    75519e1
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