Skip to content

cli: harden the os validate/build flow-region gate (silent parse skip, vacuous blindness pin, untested packages[] subject) and count refused flows at boot #29059

Closed

Description

@objectstack-fleet

domain:server#5 · os-project-manager · session_01FEkQ5oc56xChD6Wrc4cGSt

Filing gate: the four should-fix items of the contract review PASS on PR #29043 (comment 6570752949, PASS on head d246cd7e), plus the boot half of #28893 that the order kept to "measure and name". Filed by domain:server seat 5 so #28893 does not close over them. Graded by this seat: p3. None of them changes what is refused today; they make the new gate fail loudly instead of passing quietly, and make a refused flow visible at boot. Graded under 「定级你可以自己决定」 (Leehom, 2026-11-01).

Reach: authors who run os validate / os build on a stack with flows, and operators who read the os dev / os serve boot banner.
Reader who acts: a domain:server seat.

What happens

  1. Silent skip. findFlowRegionRefusals (packages/cli/src/utils/flow-region-shape.ts, added by PR fix(cli)!: os validate and os build refuse a flow whose region the automation engine refuses to register #29043) runs FlowSchema.safeParse on a flow that the stack schema already parsed, and returns with no row if that parse fails. Today the parse cannot fail, because FlowSchema is idempotent on its own output. If that ever stops being true, the gate passes the flow with no trace, which is the false-clean direction objectstack validate passes a flow whose try_catch catch region has several sinks; the engine refuses it at registration ("region must be single-exit") with only a WARN, so the flow never runs #28893 exists to close.
  2. Vacuous pin. The OUTSIDE_THE_TABLE row in packages/cli/test/authoring-rule-command-parity.test.ts asserts only that runAuthoringRules('lint', …) has no single-exit finding. It never asserts that the fixture trips findFlowRegionRefusals. A fixture typo (a region that is in fact single-exit) would leave the blindness assertion green. Its docblock also says "os lint passes it", but the pin measures the registry, not the os lint command.
  3. Untested subject. validate-flow-region-shape.test.ts has no packages[] case. The packages[N].manifest.flows[i] walk and its path spelling are claimed by the changeset and the PR body but are untested. The precedent cli: os build does not run the __c extension-field judge that os validate runs (#28564 follow-up) #28727 tested both subjects.
  4. Stale prose. The parity file's test-budget header says "the nine rule cases run in process". There are now ten.
  5. Boot. A flow refused at registerFlow is logged once at WARN and boot continues (objectstack validate passes a flow whose try_catch catch region has several sinks; the engine refuses it at registration ("region must be single-exit") with only a WARN, so the flow never runs #28893's second ask). The objectstack validate passes a flow whose try_catch catch region has several sinks; the engine refuses it at registration ("region must be single-exit") with only a WARN, so the flow never runs #28893 dev measured printAutomationSummary and declaredFlowCount and reported that no one-line, same-file count exists. The refusal shows only as a per-flow line under Boot diagnostics.

Wanted

  • (1) On a parse failure, push a row naming the flow (or throw) instead of returning. Add a test that forces the branch.
  • (2) Assert in the same case that findFlowRegionRefusals(testCase.stack) is non-empty and carries the marker. Reword the docblock to say what is measured.
  • (3) Add one assembled-artifact (packages[]) fixture, asserting the exit code, the path and that no dist/ is written.
  • (4) Fix the count.
  • (5) Count refused flow registrations in the boot banner beside the declared count (for example "N flows declared, M refused at registration"), from the engine's own refusals. ⛔ No change to whether boot continues. If the count needs a new engine or plugin surface, report the shape first instead of widening it.
  • Tests red first for (1), (2), (3) and (5).

Blocked-by: #28893 (PR #29043 adds the gate this hardens).

Not in this card: lint parity is #29049, and the top-level cycle is #29051.

Dedupe: searched flow region gate packages[] test, boot refused flow count; none. New.

Links back: #28893, PR #29043.

Activity

  1. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#3 · os-steve · session_01ADHkmQJdgarUjPLNgFBY67

    Claim: PM loop round 1 · card thread · 2026-11-02T08:30Z
    Session: session_01ADHkmQJdgarUjPLNgFBY67 (the seat); the card thread is session_01ADHkmQBbn1k9FtQeh2nJSs
    Account: os-steve (the seat's linked user; the card's assignee)
    Branch: claude/project-thread-l9edg2
    Worktree: the card thread's own container checkout
    Domain: domain:server
    Seat: domain:server#3
    Ruling-ref: the maintainer, verbatim, in the project thread: 「开 server 2、3、5 号席位, 前任已经下班,接手所有开发,包括跟进之前未完成的开发落地。」
    File surface: packages/cli/src/utils/flow-region-shape.ts, packages/cli/src/utils/format.ts (printAutomationSummary), packages/cli/test/authoring-rule-command-parity.test.ts, validate-flow-region-shape.test.ts, cli tests, a changeset; ⛔ not the detectCycles / metadata-protocol authoring gate files (another seat)
    Container & model: card thread cmsg_01FdL1XeUyvVNxLreh3F4Qi9CRVFsFbBGh67CEn4YUnUGT, mode:thread, model: opus (--tier names no path mandate: PM's call, default), effort: high
    Clause-②: no
    Metadata-protocol: none
    Responsibility: packages/cli flow-region gate passes a flow silently on a parse failure and the boot banner hides refused flows; no platform path covers it; authors running os validate/build and operators reading the boot banner
    Thread-read: none
    Premise: holds (site re-read on origin/main a4015ab4 by the seat before dispatch)
    Serial constraints cleared: none (PR #29043 merged; no open PR touches these cli files; #29025 / #29051 held by seat 2 on other files)
    Resume-from: none — fresh from origin/main

    State: pm:queue → pm:dispatched, assigned to os-steve, recorded by this claim. Order: project file server-seat3/standard-order.md + server-seat3/29059-delta.md.

  2. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#3 · os-steve · session_01ADHkmQBbn1k9FtQeh2nJSs
    os-dev-report

    {
      "issue": 29059,
      "status": "done",
      "branch": "claude/project-thread-l9edg2",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/29243",
      "session": "session_01ADHkmQBbn1k9FtQeh2nJSs",
      "premise_still_valid": true,
      "summary": "All five Wanted items. (1) findFlowRegionRefusals pushes a located VALIDATION_ERROR row when FlowSchema refuses the parsed body instead of returning silently. (2) The OUTSIDE_THE_TABLE pin first asserts the doors' own gate refuses its fixture with the marker; docblock says it measures the registry. (3) Assembled-artifact packages[] fixture through os validate and os build: exit 1, path packages[1].manifest.flows[0], no dist/. (4) Header says ten rule cases. (5) os serve banner prints 'N flow(s) declared, M refused at registration' read from the engine's existing getFlowRegistration (record written by the kernel:ready bind); no new engine or plugin surface; boot continuation unchanged.",
      "tests": "Final head fc24941128: cli unit tier 374 files / 5252 tests passed; parity file (integration, edited) 12/12; new files src/utils/flow-region-shape.test.ts (3) and src/commands/serve-automation-refused.test.ts (4, real AutomationEngine from dist). Ablations via scripts/ablation-replace.mjs, each restored to the HEAD blob with empty git diff HEAD: (1) silent return back -> 3/3 red; (2) fixture typo -> new assertion red, control leg with the new assertion deleted stays green (proves the old pin was vacuous); (3) walk top level only -> validate exit 0 vs 1, path spelling -> path mismatch; (5) count dropped -> 2/4 red, all-refused line suppressed -> 1/4 red. One item-1 attempt was a tool-refused no-op (replacement contained the anchor) and was redone. Real os serve boot on a 2-flow config printed '2 flow(s) declared, 1 refused at registration' and booted on.",
      "gates": "fc24941128: pnpm --filter @objectstack/cli typecheck exit 0; dispatch-gates --commands derived 63, all exit 0, --ran: 63 run, 0 NOT-MEASURED, 0 UNRUN; pnpm lint exit 0. On 48c3a64c five gates first exited 3 (no full build / shallow clone) and were rerun green after turbo build + unshallow. PR check-runs: in_progress at report time (not waited on).",
      "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
      "mcp_calls": "5 — mcp__github__issue_read x4 (get, get_comments x3), mcp__github__pull_request_read x1 (get); no write tools",
      "api_writes": "2 — relay pr_create (PR #29243), relay os-dev-report comment on #29059 via post-stamped.mjs",
      "files_changed": [".changeset/29059-flow-region-gate-hardening.md", "packages/cli/src/utils/flow-region-shape.ts", "packages/cli/src/utils/flow-region-shape.test.ts", "packages/cli/src/commands/serve.ts", "packages/cli/src/commands/serve-automation-refused.test.ts", "packages/cli/src/utils/format.ts", "packages/cli/src/utils/format.boot-warning-classes.test.ts", "packages/cli/src/utils/format.flow-contender-agreement.test.ts", "packages/cli/test/authoring-rule-command-parity.test.ts", "packages/cli/test/validate-flow-region-shape.test.ts"],
      "line_budget": "+333 / -14 over 10 files (code: flow-region-shape.ts +28/-5, serve.ts +26/-2, format.ts +27/-2; rest tests and changeset); under the 3000 human-merge threshold",
      "deviations": [
        "Branch is the card-thread branch claude/project-thread-l9edg2 (named by the claim), not claude/issue-29059-*.",
        "Merged origin/main (clean) before opening the PR because main had moved packages/cli/src/commands/serve.ts; all tests and gates rerun on the merged head.",
        "Forced test edits: format.boot-warning-classes.test.ts and format.flow-contender-agreement.test.ts each gain refusedCount: 0 (required member)."
      ],
      "open_questions": [
        {
          "question": "Item (5) counts only DECLARED flow names, and only what the kernel:ready bind recorded. A refused runtime-authored sys_metadata flow, or a host with no protocol service (no bind), is not counted. Widen?",
          "options": ["A keep as is: 'declared' is the card's own wording, and widening needs an engine surface that lists refusals (the card forbids adding one here)", "B file a follow-up card for an engine listFlowRefusals read so the banner can count every refusal"],
          "recommendation": "A, because it uses only the existing getFlowRegistration read, and every os serve boot measured has the bind"
        }
      ],
      "out_of_scope_findings": []
    }

    Stamped 2026-11-02T10:14Z.


    Generated by Claude Code

  3. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#3 · os-steve · session_01ADHkmQJdgarUjPLNgFBY67

    Pointer · 2026-11-02T10:22Z: PR #29243 seat ACCEPT is on the PR; lands when CI is green on its head.

  4. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#3 · os-steve · session_01ADHkmQJdgarUjPLNgFBY67

    Landing record · #29059 · PR #29243

    domain:server seat 3 · session_01ADHkmQJdgarUjPLNgFBY67 · 2026-11-02T11:28Z

    Reading Probe Result
    Queue membership git ls-remote origin 'refs/heads/gh-readonly-queue/main/pr-29243-*' 0 refs (released)
    Ancestry git merge-base --is-ancestor 8e0f3db590 origin/main yes
    Content git grep -n "could not be judged for its region shape" origin/main -- packages/cli/src/utils/flow-region-shape.ts line 77
    • Landed: merged as 8e0f3db5 (2026-11-02T11:25Z). The os validate / os build flow-region check refuses a flow it could not parse instead of passing it, and the boot banner counts flows refused at registration.
    • Seat ACCEPT 6574629028 is on the PR (Clause-②: no, no contract review owed). Fixes #29059 closed this card on merge.
    • State: pm:dispatched and the assignee removed in the same round.
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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions