Repository navigation
feat(lint)!: os lint refuses a flow region the engine refuses to register (flow-region-shape-invalid) - #29495
Conversation
… the engine refuses to register (#29049) The ADR-0031 region check that os validate and os build ran through a CLI-only walk (flow-region-shape.ts) is now an authoring rule calling the spec's validateControlFlow, so all three commands judge it from the registry; the CLI walk is retired. Claude-Session: https://claude.ai/code/session_01ADHkmQjqkXDjc5jhjFfHt3 Co-authored-by: Claude <noreply@anthropic.com>
… like every registry reader Claude-Session: https://claude.ai/code/session_01ADHkmQjqkXDjc5jhjFfHt3 Co-authored-by: Claude <noreply@anthropic.com>
…hangeset; correct the two pending region changesets Claude-Session: https://claude.ai/code/session_01ADHkmQjqkXDjc5jhjFfHt3 Co-authored-by: Claude <noreply@anthropic.com>
|
domain:spec#4 · os-steve · session_01ADHkmQNap7ryQcyQXocWct Contract-review-claim: session_01ADHkmQNap7ryQcyQXocWct · head 52b05b8 · 2026-11-02T17:10Z The lane seat reviews this head itself at |
Resolve packages/lint/src/authoring-rules.ts: keep both the flow-region-shape-invalid entry and main's lintConnectorCredentialLiterals entry; recount author-time rules 61 -> 62 in the four CLI transcripts. Claude-Session: https://claude.ai/code/session_01ADHkmQjqkXDjc5jhjFfHt3 Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 2 package(s): 36 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 8 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 37 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 216998fb6103f6c60808cf72f64363545a06f2e2 && git checkout 216998fb6103f6c60808cf72f64363545a06f2e2
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin c88467d4adc417e668474e1b6198a5a7c503ac3f b3d46b7b436a2572979af7115d3fa082505a63ca && git checkout -B drift-repro c88467d4adc417e668474e1b6198a5a7c503ac3f && git merge --no-ff b3d46b7b436a2572979af7115d3fa082505a63ca
node scripts/docs-audit/affected-docs.mjs --json c88467d4adc417e668474e1b6198a5a7c503ac3f
|
|
domain:spec#4 · os-steve · session_01ADHkmQNap7ryQcyQXocWct Contract reviewServed-tier: ① Derived judgmentsRead on the net diff
② Semver level
The two pending changesets ③ Boundary flags
④ Design judgmentThe narrowing read against the published spec on Attempts to break it, each read in the tree:
Must-fix: none. Should-fix (none blocks):
Nothing in the tree is judged on the PR body's account of it; the readings above are from the diff and the cited code. The head carries no check-run yet (①): this PASS is a verdict on the contract and the design, and the queue's own "every check green" conjunct still has to be met on Implemented-by: VERDICT: PASS |
|
domain:spec#4 · os-steve · session_01ADHkmQNap7ryQcyQXocWct Contract reviewServed-tier: Delta review. Head ① Derived judgments
② Semver levelUnchanged from record 6583593915: ③ Boundary flagsUnchanged from record 6583593915: rule id option A ④ Design judgmentUnchanged from record 6583593915: the narrowing makes the spec's own refusal ( Must-fix: none. Should-fix (carried over, neither blocks): (1) send the r2 PR body so 维护者速读 no longer routes the rule id to the maintainer; (2) on #29233, wire the publish door through this registry row and retire the entry's Implemented-by: VERDICT: PASS |
|
domain:spec#4 · os-steve · session_01ADHkmQNap7ryQcyQXocWct Head-sha: 维护者速读
席位意见:可落地。契约复审在 52b05b8 判 PASS(6583593915),合 main 后的 head 由同一复审者 delta 复审 PASS(6584199633)。CI 除按设计变红的 Check Changeset 外全绿。 席位核对 — ACCEPT ·
|
⛔ merge queue 构建失败 — 先分诊,再决定要不要重排队列构建 41498375728 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集), 分类: 失败的 job(日志抽取,best effort):
跨 PR 相同签名(24h,按失败测试文件聚合):
历史信号:
分诊清单:
Generated by Claude Code · merge-queue-triage workflow (#4859) |
Requested by Steve · project thread
Fixes #29049
Clause-②: no (narrowing)
Metadata-protocol: none
维护者速读
@objectstack/lint的作者期规则表,新规则flow-region-shape-invalid,直接调用 spec 的validateControlFlow;os lint从此也拒收这类流程。os validate/os build拒收,os lint照样放行;作者跑 lint 看到绿灯,部署后引擎注册时拒收该流程,触发器永不触发。os lint收窄(BREAKING 横幅);validate / build 的通过与拒收结论不变,只是错误改由规则表报出、--json行的形状随之变化(该形状尚未发布)。回滚即还原本 PR。packages/lint/src/validate-flow-region-shape.ts(规则本体)、packages/lint/src/authoring-rules.ts新条目、packages/cli/test/validate-flow-region-shape.test.ts(三命令同一夹具)。pnpm --filter @objectstack/cli exec vitest run --project unit test/validate-flow-region-shape.test.ts,预期 5/5 通过,其中三条用例对 validate、build、lint 同一夹具均退出 1 并给出同一规则 id。What changes
flow-region-shape-invalid(packages/lint/src/validate-flow-region-shape.ts;validateFlowRegionShapeandFLOW_REGION_SHAPE_INVALIDexported from the root barrel). Eachflows[]record goes through the two calls the engine'sregisterFlowmakes:FlowSchema, thenvalidateControlFlowfrom@objectstack/spec/automation. No copy of the region logic. Message:Flow 'NAME' would be refused at registration:plus the engine's sentence, verbatim. Gating,input: 'parsed', all three commands.FlowSchemarefuses is a finding ("could not be judged", with the schema's issues), never a skip. A non-record entry (null, a string) is skipped like every registry reader skips it (non-record-object-entry.test.tsreds otherwise); the flows after it keep their own index.surfaces: CLI_ONLY, with asurfaceReason). Whether a metadata publish refuses a malformed region is sibling card metadata: a flow whose loop / parallel / try_catch region is malformed or cyclic reads valid as a draft and publishes, then the engine refuses to register it #29233 (domain:server); this PR does not decide it.packages/cli/src/utils/flow-region-shape.tsand its unit test are deleted;validate.tsstep 3a-vi andcompile.tsstep 3b-iii-ter are reduced to a pointer comment. validate and build now get the check from the registry, so the defect is reported once (measured below).os explain flow-region-shape-invalid: new entry inrule-explanations.ts.Running author-time rules (61)(recounted from the merged registry: validate 61, build 61)..changeset/29049-flow-region-shape-lint-rule.md(@objectstack/lint+@objectstack/clipatch, BREAKING banner,adr-0087: not-required (no-migration-prescription)).Forced test edits
packages/cli/test/authoring-rule-command-parity.test.ts: theflow region shape (#28893)row leftOUTSIDE_THE_TABLEand is now aCASESrow (rule: 'flow-region-shape-invalid',blindTo: ['lint']), as that table's docblock prescribed.OUTSIDE_THE_TABLEheld only that row, so the table, its docblock and itsit.eachare removed (an emptyit.eachwould register no test).packages/cli/test/validate-build-gate-parity.test.ts: thefindFlowRegionRefusalsrow is removed fromSHARED_NON_REGISTRY_GATES; neither command calls it now, and the ledger's own stale-row check requires that.packages/cli/test/validate-flow-region-shape.test.ts: still green, with three edits. (1) A refusal is now a rule finding (rule,where,path,message,hint), undererrorsforos validate --jsonand underissuesforos build --json(the rule table's envelope), not the old gate'sVALIDATION_ERRORrow withflow. (2) The 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 assembled-artifact case located the row atpackages[1].manifest.flows[0]; the rule table judges the artifact's folded union first (as it does every collection), so the finding is atflows[1]withwhere: flow 'rgn_ops_lead_time'. Still exactly one row, from the second package. (3) Each case now also runsos lintin process on the same fixture.packages/cli/src/utils/flow-region-shape.test.ts(deleted with its subject): its three 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 refused-parse cases moved topackages/lint/src/validate-flow-region-shape.test.ts. Thepackages[]location case has no rule-level counterpart (the rule reads the stack it is handed); its door-level half is the assembled case above.Tests
One test per command on the same fixture:
packages/cli/test/validate-flow-region-shape.test.tsrunsos validate --json,os build --jsonandos lint --jsonin process on each ofduly,nested,singleBranchandassembled; all three exit 1 withrule: flow-region-shape-invalidand the engine's sentence, build writes nodist/, and the joined control passes validate and build with no finding of the rule under lint. The validate leg also asserts the refusal appears exactly once (no echo from a second gate).Readings on head
52b05b8e(after mergingorigin/mainat7b1ae338, closure rebuilt):Consumer suites
pnpm --filter @objectstack/lint test: Test Files 190 passed (190), Tests 7843 passed (7843);typecheckclean.pnpm --filter @objectstack/cli exec vitest run --project unit: Test Files 373 passed (373), Tests 5263 passed (5263) (includesvalidate-flow-region-shape,validate-build-gate-parity,explain-rule-id, thepublished-subpath-*pins afterpnpm --filter @objectstack/cli build);typecheckclean.pnpm --filter @objectstack/cli exec vitest run --project integration test/authoring-rule-command-parity.test.ts: Tests 12 passed (12), includingflow-region-shape-invalid gates on all three commands.pnpm --filter @objectstack/spec exec vitest run src/automation: Test Files 45 passed (45), Tests 1745 passed (1745).Ablation (one, on the line the tests pin)
Mutation:
validateControlFlow(result.data);inpackages/lint/src/validate-flow-region-shape.tsguarded behind an always-falseglobalThis.ABLATION_29049_MARKERread, viascripts/ablation-replace.mjs(anchor 1 -> 0, blob46bdaf4b-> changed), lint rebuilt,ablation-dist-preflightfound the marker in 4 dist files.validate-flow-region-shape.test.ts: 4 failed, 3 passed (red as expected; the 3 green are the refused-parse and control cases, which do not reach the call).validate-flow-region-shape.test.ts: 4 failed, 1 passed: validate, build and lint all PASS the malformed flow with the rule disabled, which also proves no second gate refuses it any more (the retired walk is gone).authoring-rule-command-parity.test.ts:flow-region-shape-invalid gates on all three commandsfailed, 11 passed.46bdaf4b== HEAD,git diff HEADempty, lint rebuilt, preflight--absentclean on 20 files and a clean tree; both CLI files green again (5/5, 12/12).A first attempt replaced the call with a bare expression; that build failed, so the marker never reached
dist/and its readings are discarded (not counted).Measured out of scope
A flow whose top-level graph cycles (start -> a -> b -> a, b -> end) draws no finding; the same cycle inside a
loopbody drawsloop 'L' body: ... region contains a cycle. So this rule does not cover #29051.Acceptance notes
os lintnow also reports a flow record the flow schema refuses (the fail-closed finding).os validateandos buildalready refuse such a flow at their schema step, so no command accepts what it refused before beyond the region case;os lintreaches it because it never parses.detectCyclesis not exported, andvalidateControlFlowchecks cycles inside regions only. Measured: this rule does not cover the top-level cycle.lanes/server.mdreported in triage 6579387723 is not touched.Verification
Gates on head
52b05b8e:node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderived 92 commands; all 92 run and reconciled with--ran(92 derived famil(ies) accounted for — 88 run, 4 NOT-MEASURED).Non-zero exits
node scripts/check-empty-changeset.mjs --base origin/mainexit 1, by design: this PR corrects two PENDING changesets whose sentences it makes false (DELIBERATE CORRECTION class)..changeset/28893-flow-region-shape-at-validate-build.mdno longer says the doors callfindFlowRegionRefusalsand print aVALIDATION_ERRORrow; it now names the ruleflow-region-shape-invalidand thatos lintreports it too..changeset/29059-flow-region-gate-hardening.mdnow says the unjudgeable flow is refused with aflow-region-shape-invalidfinding instead of aVALIDATION_ERRORrow. Restoring them would republish false sentences; this needs confirming on the PR.Check Changesetis not a required context.check:dual-build-cjs-loadsandcheck:skill-examples(packages outside this closure have nodist/, e.g.client-react),check:i18n-coverage(the showcase example needs an unbuilt connectordist/),check-plugin-teardown-shape --self-test(shallow clone cannot reach its pinned fixture commit). None reads a file this PR changes for its verdict; CI runs them on a full build.check-changeset-no-major --eventexit 0 (LEVEL AXIS:no (narrowing)+none, patch);check-adr-0087-registrationexit 0 (not-required (no-migration-prescription)).Dogfood (required by the
(narrowing)arm) on head52b05b8e,pnpm --filter @objectstack/dogfood testunder the lock after building@objectstack/dogfood^...:VERDICT command-exit 1· Test Files 2 failed | 393 passed | 3 skipped (398) · Tests 4 failed | 3255 passed | 28 skipped (3287).The 4 failures are not this PR's (control measured)
test/flow-runas.dogfood.test.ts:runAs:'system' ELEVATES(trigger answers 404RECORD_NOT_FOUNDonrunas_note),runAs:'user' DE-ELEVATES,runAs:'system' READS.test/declarative-endpoint-anonymous-guest.dogfood.test.ts:carries the guest position …(flowContextslength 0, expected 1).packages/lint/src,packages/cli/srcandpackages/cli/testrestored toorigin/main7b1ae338(this PR's diff removed from both packages), lint and cli rebuilt, the same two files: the same 4 failed, 8 passed. So they fail onmain's code without this diff; reported to the seat as a main-red candidate. Tree restored to HEAD and rebuilt (cleangit status).Generated by Claude Code