Skip to content

lint: os lint stays blind to a flow region the engine refuses to register; validate and build refuse it since #28893 through a CLI-only gate #29049

Closed

Description

@objectstack-fleet

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

Filing gate: ① out-of-scope finding of the #28893 dev (os-dev-report 6570600658), split off by this seat's placement ruling (A) and filed for domain:devx. Graded by this seat: p3. validate and build already refuse it, so only os lint and any lint-rule consumer miss it. Graded under 「定级你可以自己决定」 (Leehom, 2026-11-01).

Reach: an author running os lint (or a tool built on the authoring-rules registry) on a flow whose loop / parallel / try_catch region is not single-entry/single-exit, or whose parallel has one branch.
Reader who acts: the domain:devx seat (packages/lint).

What happens

PR #29043 (#28893) adds packages/cli/src/utils/flow-region-shape.ts. It runs FlowSchema and then the spec's validateControlFlow for os validate and os build, the way #28727's findExtensionFieldSuffixRefusals does. The authoring-rules registry (runAuthoringRules, shared by validate, build and lint) does not carry it, so os lint still passes such a flow. cli/test/authoring-rule-command-parity.test.ts records this as a blindTo row in its OUTSIDE_THE_TABLE table.

Wanted

  • An authoring rule in packages/lint that calls the spec's validateControlFlow (⛔ no copy), with the engine's message, so all three commands see it.
  • Then retire the CLI-only gate, or keep it as a delegate, and move the parity row into CASES.
  • One test per command.

Blocked-by: #28893 (PR #29043).

Dedupe: searched lint flow region single-exit; none. New.

Links back: #28893, PR #29043, #28727.

Activity

  1. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

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

    Unblocked · 2026-11-02T07:24Z

    #28893 landed (PR #29043, 5d3667e292), so the gate this card extends is on main. pm:blocked → pm:queue. Dispatch is paused at the maintainer's wrap-up, so it waits for the next seat round.

  2. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: first-touch audit · p3 kept · domain:devx → domain:spec · pm:queue · 2026-11-02T14:15Z

    ⛔ Not a claim.


    Generated by Claude Code

  3. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#4 · os-steve · session_01ADHkmQNap7ryQcyQXocWct

    Claim: PM loop round 1 · 2026-11-02T15:05Z
    Session: session_01ADHkmQNap7ryQcyQXocWct (seat); dev = card thread session_01ADHkmQjqkXDjc5jhjFfHt3
    Account: os-steve
    Branch: claude/project-thread-f473q9
    Worktree: the card thread's own checkout of objectstack-ai/objectstack
    Domain: domain:spec
    Seat: domain:spec#4
    File surface: packages/lint/src/ (a new rule file calling the spec's validateControlFlow, its AUTHORING_RULES entry in authoring-rules.ts, the barrel index.ts, rule-explanations.ts) and their tests; packages/cli/src/utils/flow-region-shape.ts and its callers in validate/build (retired or reduced to a delegate); packages/cli/test/authoring-rule-command-parity.test.ts (row moves from OUTSIDE_THE_TABLE to CASES) and the validate/build/lint tests; the four CLI transcripts' author-time rules (N) counts in content/docs/; root .changeset/ (stop on breach; explain in the report)
    Container & model: M, mode:thread, model: default — the tier is the PM's per-card call (floor sonnet · default opus · ceiling fable); a new authoring rule narrowing os lint's accept set, one CONTRACT_REVIEW_TIER review by the seat before the queue, effort: high
    Clause-②: no (narrowing)
    Metadata-protocol: none
    Responsibility: packages/lint's rule registry lacks the flow-region check that packages/cli gates validate and build with (flow-region-shape.ts, #28893) | the CLI-only gate covers os validate and os build, not os lint or other registry consumers | an author or tool running os lint on a flow with a malformed loop/parallel/try_catch region; validate and build already refuse it
    Thread-read: 6579387723
    Premise: holds (on origin/main 96fb7b5d, authoring-rule-command-parity.test.ts carries the flow region shape (#28893) row in OUTSIDE_THE_TABLE with blindTo: ['lint'], and no packages/lint/src file calls validateControlFlow); the dev measures first and stops with premise_still_valid: false if it does not reproduce
    Serial constraints cleared: PR #29424 (#29341, a new lint rule) also edits packages/lint/src/authoring-rules.ts, index.ts, rule-explanations.ts and the cli.mdx / validating-metadata.mdx rule counts; PR #29389 edits authoring-rules.ts; PR #29385 edits rule-explanations.ts. All append-style in shared registries, none a single-claim path: no serial hold; whichever lands later merges main and re-counts author-time rules (N).

    The new rule id is a new public name: the dev proposes it in open_questions and the seat takes it to the maintainer as needs-user-decision on the PR. Triage 6579387723 set the lane; the lanes/server.md routing line it reported is not resolved here. The delta is /mnt/project-files/spec-seat4/29049-delta.md (project share).

  4. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#4 · os-steve · session_01ADHkmQjqkXDjc5jhjFfHt3
    os-dev-report

    {
      "issue": 29049,
      "status": "done",
      "branch": "claude/project-thread-f473q9",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/29495",
      "session": "session_01ADHkmQjqkXDjc5jhjFfHt3",
      "premise_still_valid": true,
      "summary": "New gating authoring rule flow-region-shape-invalid (packages/lint/src/validate-flow-region-shape.ts) runs FlowSchema then the spec's validateControlFlow on each flows[] record, message = the engine's refusal behind the flow name; fail-closed on a schema-refused record (#29243 kept), non-record entries skipped like every registry reader. Registered on all three commands (CLI_ONLY surface: runtime publish is sibling #29233), barrel + os explain entry, four transcript counts 60 -> 61. The CLI-only walk flow-region-shape.ts is retired (validate/build read the registry; refusal reported once), parity row moved from OUTSIDE_THE_TABLE into CASES, one test per command on the same fixtures. Pending changesets 28893/29059 corrected (sentences this PR makes false).",
      "tests": "Head 52b05b8e (origin/main 7b1ae338 merged, closure rebuilt): lint test 190/190 files, 7843/7843; lint typecheck 0; cli unit 373/373 files, 5263/5263 (incl. validate-flow-region-shape 5/5, validate-build-gate-parity, explain-rule-id, published-subpath pins after cli build); cli typecheck 0; cli integration authoring-rule-command-parity 12/12; spec src/automation 45/45 files, 1745/1745. Ablation: validateControlFlow call guarded behind always-false globalThis.ABLATION_29049_MARKER via ablation-replace (anchor 1->0), lint rebuilt, dist-preflight marker in 4 files; lint rule test 4 failed/3 passed, cli validate-flow-region-shape 4 failed/1 passed (validate, build AND lint pass the malformed flow: no second gate left), parity 1 failed/11 passed; restore blob == HEAD 46bdaf4b, git diff HEAD empty, preflight --absent clean, both green again (5/5, 12/12). First attempt (bare expression) failed to build, marker never reached dist, readings discarded.",
      "gates": "dispatch-gates --commands on 52b05b8e: 92 derived, 92 run, --ran reconciled (88 run, 4 NOT-MEASURED). check-empty-changeset exit 1 by design (DELIBERATE CORRECTION of pending .changeset/28893-* and 29059-*). NOT MEASURED exit 3 prerequisite: check:dual-build-cjs-loads, check:skill-examples (unbuilt client-react etc.), check:i18n-coverage (showcase needs unbuilt connector-slack dist), check-plugin-teardown-shape --self-test (shallow clone). All others exit 0. Offline: check-changeset-no-major --event exit 0 (no (narrowing) + none, patch), check-adr-0087-registration exit 0. CI check-runs: in_progress at report time.",
      "dogfood_battery": "52b05b8e · VERDICT command-exit 1 · Test Files 2 failed/393 passed/3 skipped (398) · Tests 4 failed/3255 passed/28 skipped (3287) · failures: flow-runas.dogfood.test.ts x3 (runAs system ELEVATES 404 RECORD_NOT_FOUND runas_note; user DE-ELEVATES; system READS), declarative-endpoint-anonymous-guest.dogfood.test.ts x1 (flowContexts length 0). Control: lint/cli src+cli test restored to origin/main 7b1ae338 and rebuilt -> same 4 failed/8 passed. Not this PR's.",
      "mcp_calls": "0",
      "api_writes": "2 — relay pr_create (POST /repos/objectstack-ai/objectstack/dispatches, run 41481992103, PR #29495, 10783 bytes sent = stored); this os-dev-report comment via post-stamped. git push is not a REST write.",
      "line_budget": "12 files in packages: +345/-264 (lint +rule 122, rule test ~100, registry/barrel/explanations +54; cli -122 gate, -50 gate test, -~70 command blocks/ledger/parity table); 4 docs one-line count bumps; 3 changesets (1 new, 2 corrected).",
      "files_changed": [
        ".changeset/29049-flow-region-shape-lint-rule.md",
        ".changeset/28893-flow-region-shape-at-validate-build.md",
        ".changeset/29059-flow-region-gate-hardening.md",
        "packages/lint/src/validate-flow-region-shape.ts",
        "packages/lint/src/validate-flow-region-shape.test.ts",
        "packages/lint/src/authoring-rules.ts",
        "packages/lint/src/index.ts",
        "packages/lint/src/rule-explanations.ts",
        "packages/cli/src/commands/validate.ts",
        "packages/cli/src/commands/compile.ts",
        "packages/cli/src/utils/flow-region-shape.ts (deleted)",
        "packages/cli/src/utils/flow-region-shape.test.ts (deleted)",
        "packages/cli/test/authoring-rule-command-parity.test.ts",
        "packages/cli/test/validate-build-gate-parity.test.ts",
        "packages/cli/test/validate-flow-region-shape.test.ts",
        "content/docs/deployment/cli.mdx",
        "content/docs/deployment/validating-metadata.mdx",
        "content/docs/getting-started/build-with-claude-code.mdx",
        "content/docs/ui/react-pages.mdx"
      ],
      "deviations": [
        "PR body line 维护者速读/需要你做的 still says the rule id goes to the maintainer with needs-user-decision; written before the seat's correction. Corrected body r2 (also carries the dogfood reading) at /mnt/project-files/spec-seat4/29495-body-r2.md for the seat to send; dev does not PATCH.",
        "Session-context asked to set PR assignee/reviewer via MCP; skipped: the standard order forbids MCP writes and os-dev forbids PR assignees.",
        "check-empty-changeset red by design: two pending changesets corrected (DELIBERATE CORRECTION); needs the seat's confirmation, not a restore.",
        "os validate/os build --json row shape changes for this refusal (rule finding in errors / issues instead of the unreleased VALIDATION_ERROR row with flow); verdicts and exit codes unchanged."
      ],
      "open_questions": [
        {
          "question": "New public rule id for the region check (plus exports validateFlowRegionShape / FLOW_REGION_SHAPE_INVALID).",
          "options": [
            "A flow-region-shape-invalid (built with it)",
            "B flow-region-malformed",
            "C flow-region-unregisterable"
          ],
          "recommendation": "A: names the subject (flow region shape) and the verdict in the registry's existing -invalid spelling (approval-expression-invalid, expression-invalid)."
        },
        {
          "question": "Under os lint (which never parses) a flow record FlowSchema refuses is reported by this rule as 'could not be judged' (#29243 fail-closed). validate/build already refuse such a flow at the schema step. Keep, or skip under lint?",
          "options": [
            "A keep fail-closed on every command (built)",
            "B skip schema-refused records (would reopen the false-clean branch at validate/build if FlowSchema ever stops being idempotent)"
          ],
          "recommendation": "A: the engine refuses to register such a flow too, and the rule cannot tell which command runs it."
        }
      ],
      "security_findings": "none",
      "out_of_scope_findings": [
        "class: a · reach: maintainer-visible main-red candidate — pnpm --filter @objectstack/dogfood test on origin/main code (7b1ae338 lint/cli source, closure built) fails 4: flow-runas.dogfood.test.ts (runAs system ELEVATES: trigger answers 404 RECORD_NOT_FOUND on runas_note; user DE-ELEVATES; system READS) and declarative-endpoint-anonymous-guest.dogfood.test.ts (flowContexts length 0). Deterministic on re-run. Possibly from #29381 (objectql nested-write refusal, newest main commit) — not bisected. dedupe words: flow-runas dogfood RECORD_NOT_FOUND runas_note; anonymous guest flowContexts",
        "Top-level graph cycle (#29051): measured not covered — start->a->b->a, b->end draws no finding; the same cycle in a loop body draws 'region contains a cycle'. carrier: #29051"
      ]
    }

    Stamped 2026-11-02T17:09Z.


    Generated by Claude Code

  5. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#4 · os-steve · session_01ADHkmQNap7ryQcyQXocWct

    Claim revision · 2026-11-02T17:51Z · claim 6580500987

    This replaces the claim's sentence that "the seat takes [the new rule id] to the maintainer as needs-user-decision". There is no such rule: a new public name is the seat's call unless it carries a real product or contract fork. The seat ruled the id flow-region-shape-invalid in ACCEPT #29495 (comment), and no needs-user-decision label was ever set. The file surface is unchanged: the PR also corrected the pending .changeset/28893-* and .changeset/29059-*, inside the claimed root .changeset/.

  6. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#4 · os-steve · session_01ADHkmQjqkXDjc5jhjFfHt3
    os-dev-report

    {
      "issue": 29049,
      "status": "done",
      "branch": "claude/project-thread-f473q9",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/29495",
      "session": "session_01ADHkmQjqkXDjc5jhjFfHt3",
      "premise_still_valid": true,
      "summary": "Addendum (round 2): merged origin/main c88467d4 into the branch, head b3d46b7b. The only conflict was packages/lint/src/authoring-rules.ts, and both sides were append-only: I kept this card's validateFlowRegionShape entry and main's lintConnectorCredentialLiterals entry and their imports. Rule total recounted from the rebuilt lint dist: validate 62, build 62 (lint 60). The four CLI transcripts went from 61 to 62. No other count pins. Delta vs origin/main is unchanged: 19 files, +371/-270. Nothing else touched.",
      "tests": "Head b3d46b7b, after pnpm install --frozen-lockfile and a rebuild of the dogfood and cli closures (VERDICT exit 0). lint test: 192/192 files, 7884/7884 tests, including validate-flow-region-shape. cli unit: 374/374 files, 5271/5271 tests, including validate-flow-region-shape, explain-rule-id, validate-build-gate-parity and the published-subpath pins. cli integration authoring-rule-command-parity: 12/12. check:docs-transcript-drift exit 0 (4 declared transcript values equal the registry's).",
      "gates": "Round-1 gate set on 52b05b8e stands. This round touched only authoring-rules.ts (merge resolution) and the four transcript counts. check:docs-transcript-drift was re-run on b3d46b7b: exit 0. CI should now run, since the merge clears mergeable_state dirty.",
      "dogfood_battery": "b3d46b7b · VERDICT command-exit 0 · Test Files 397 passed / 3 skipped (400) · Tests 3277 passed / 28 skipped (3305) · no failures. The 4 round-1 failures (anchor #29426) are gone with main's fix merged.",
      "mcp_calls": "0",
      "api_writes": "1 — this addendum comment via post-stamped. git push is not a REST write.",
      "line_budget": "unchanged from round 1 apart from the merge: delta vs origin/main is 19 files, +371/-270",
      "files_changed": [
        "packages/lint/src/authoring-rules.ts (merge resolution)",
        "content/docs/deployment/cli.mdx",
        "content/docs/deployment/validating-metadata.mdx",
        "content/docs/getting-started/build-with-claude-code.mdx",
        "content/docs/ui/react-pages.mdx"
      ],
      "deviations": [],
      "open_questions": [],
      "security_findings": "none",
      "out_of_scope_findings": [
        "Round-1 dogfood main-red candidate withdrawn: per the seat it is anchor #29426, fixed by #29451. Dogfood is green on b3d46b7b."
      ]
    }

    Stamped 2026-11-02T18:03Z.


    Generated by Claude Code

  7. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#4 · os-steve · session_01ADHkmQNap7ryQcyQXocWct

    Landed · 2026-11-02T18:44Z

    PR #29495 merged as 7f368f56ec at 2026-11-02T18:43:42Z. It landed at head b3d46b7b, after contract review PASS 6583593915 on 52b05b8e, a delta PASS on the merge head (6584199633) and the seat's ACCEPT 6584225221. os lint, os validate and os build now refuse a flow whose loop, parallel or try_catch region the engine refuses to register, through the gating rule flow-region-shape-invalid. The CLI-only walk is retired. pm:dispatched removed in this stroke.

    Carried: review should-fix 2 to #29233 (6583607752), which asks it to wire the publish door through this registry row and retire its surfaceReason. The top-level graph cycle stays with #29051.

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

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions