Repository navigation
fix(lint)!: refuse a flow whose loop, parallel or try_catch region is malformed at publish - #29526
Conversation
…region (red on main) A flow whose loop / parallel / try_catch region the engine refuses to register reads valid as a draft and publishes. These pins assert the gate refuses it with the engine's sentence, on the draft read and at every publish door, with well-formed region controls. Claude-Session: https://claude.ai/code/session_01ADHkmSfgX3fNF6rAXudbtz Co-authored-by: Claude <noreply@anthropic.com>
…th the engine's sentence The runtime authoring gate now asks validateControlFlow, the judge the engine's registration calls, on the body parsed through FlowSchema as the engine parses it. A flow whose loop / parallel / try_catch region the engine refuses reads valid:false as a draft and is refused at every publish door, located on the container node at fault. Claude-Session: https://claude.ai/code/session_01ADHkmSfgX3fNF6rAXudbtz Co-authored-by: Claude <noreply@anthropic.com>
The control (three saves, three reads, a package publish, three registration polls) timed out at the default 5 s inside the full battery on a shared box; it passes alone. Claude-Session: https://claude.ai/code/session_01ADHkmSfgX3fNF6rAXudbtz Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): 1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
What this run could not see
Coarse fallback — 4 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 8b3b5d124a67baff7fceafe2c6a221ba2cec5927 && git checkout 8b3b5d124a67baff7fceafe2c6a221ba2cec5927
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 2789ba7d4d4eebc135df3b053ff4c66a20d6c1f2 2cb755efdb4bfd792eb46b6ca88f155a55fb63ff && git checkout -B drift-repro 2789ba7d4d4eebc135df3b053ff4c66a20d6c1f2 && git merge --no-ff 2cb755efdb4bfd792eb46b6ca88f155a55fb63ff
node scripts/docs-audit/affected-docs.mjs --json 2789ba7d4d4eebc135df3b053ff4c66a20d6c1f2
|
|
domain:server#2 · os-steve · session_01ADHkmQxwoGb5fSnrFkpxmt Contract reviewServed-tier: Face: narrowing ( ① Derived judgments
② Semver level
③ Boundary flags
④ Design judgment
Implemented-by: VERDICT: PASS |
…istry rule Seat ruling: one judge, one name. The gate-local flow-region-malformed rule is dropped and validateFlowRegionShape (flow-region-shape-invalid) now runs on the runtime publish gate for flow writes (CLI_AND_RUNTIME, runtimeTypes flow). The door pins and the dogfood file are re-pointed to the registry rule id; two lint test files get schema-valid flow fixtures, since the rule refuses a flow the schema refuses. Claude-Session: https://claude.ai/code/session_01ADHkmSfgX3fNF6rAXudbtz Co-authored-by: Claude <noreply@anthropic.com>
|
domain:server#2 · os-steve · session_01ADHkmQxwoGb5fSnrFkpxmt Contract reviewDelta review on the earlier record 6584290147 (PASS on head Served-tier: Face: narrowing ( ① Derived judgments
② Semver level
③ Boundary flags
④ Design judgment
VERDICT: PASS Generated by Claude Code |
|
domain:server#2 · os-steve · session_01ADHkmQxwoGb5fSnrFkpxmt 席位核对ACCEPT ·
Generated by Claude Code |
domain:server#2 · os-steve · session_01ADHkmQxwoGb5fSnrFkpxmt
Requested by Steve · project thread
Fixes #29233
Clause-②: no (narrowing)
Metadata-protocol: narrowed
Skills-docs: none — no published skill teaches that a malformed region publishes; the automation skill's region rules (one entry, one exit, acyclic) stay true, and the publish door now refuses a region that breaks them earlier, in the engine's words.
维护者速读
validateFlowRegionShape(flow-region-shape-invalid)现在也在运行时发布门上跑:循环体、并行分支或 try/catch 区域写坏了的流程,草稿读取显示valid: false,所有发布入口都拒绝发布。surfaces: CLI_AND_RUNTIME、runtimeTypes: ['flow']),判官就是引擎注册时调用的FlowSchema+validateControlFlow,没有第二份判断;回滚即撤销本 PR。packages/lint/src/authoring-rules.ts里validateFlowRegionShape那一行,再看packages/metadata-protocol/src/runtime-authoring-gate.flow-region.test.ts。pnpm --filter @objectstack/metadata-protocol exec vitest run src/runtime-authoring-gate.flow-region.test.ts,预期 8 个用例全绿(需先pnpm --filter @objectstack/lint build)。What changes
@objectstack/lint: the registry row forvalidateFlowRegionShape(feat(lint)!: os lint refuses a flow region the engine refuses to register (flow-region-shape-invalid) #29495, rule idflow-region-shape-invalid) moves fromCLI_ONLYtosurfaces: CLI_AND_RUNTIME,runtimeTypes: ['flow'], and itssurfaceReasonis retired. The runtime authoring gate (evaluateRuntimeAuthoringGate), which the metadata draft read and every publish door run, picks it up throughrunRuntimeAuthoringRulesand discloses it inrulesRun. No gate-local rule and no second call:runtime-authoring-gate.tsis untouched.canonicalizeFlow):FlowSchema, thenvalidateControlFlowfrom@objectstack/spec/automation. The message is "Flow 'NAME' would be refused at registration: " followed by the engine's sentence, word for word..changeset/29233-flow-region-shape-publish.md:@objectstack/lintminor, BREAKING banner with before/now,adr-0087: not-required (no-migration-prescription).Accepted differences from a gate-local rule (seat ruling)
flows[i], whereflow 'NAME'), so the draft read's path is the item root'', not the container (nodes.1). The sentence names the region. Finer location is lint: flow-region-shape-invalid anchors the whole flow; point the finding at the region container at fault #29558.applyConversionsToFlow). Landed as declared under seat ruling A; judging the converted body is server(metadata): the publish doors gate the raw stored flow draft, while the live save gates its D2-converted body (#21982), so a legacy spelling the engine registers is refused at publish #29557. Measurement below.Tests
packages/metadata-protocol/src/runtime-authoring-gate.flow-region.test.ts(new, pure gate, 8 cases): the card's loop body a → b → a (pins the engine's sentence once; whereflow 'rgnprobe_loop', pathflows[0],rulesRuncontainsvalidateFlowRegionShape), a one-branch parallel, a try region that closes on itself, a region its schema refuses ("invalid region"), a malformed region nested in another (sentence names both levels), two malformed containers giving one finding; controls: well-formed loop / parallel / try_catch (nested too) raise nothing, a draft is not gated. Every expected message isvalidateControlFlow's own answer for the case.packages/metadata-protocol/src/protocol.draft-publish-verdict.test.ts(real protocol doors): a CASES row for the card's loop (draft saves, draft readvalid:falsewith the publish's own rule and message, per-item publish 422, package publish rolls back); a control that a well-formed loop, parallel and try_catch each readvalid:trueand publish (publishedCount: 3); a pin that the draft read carries the registry rule's message at the item root ('').packages/qa/dogfood/test/flow-region-malformed-publish.dogfood.test.ts(showcase boot, REST): the card's table (draft PUT 200, draft GETvalid:falsewithcode flow-region-shape-invalid,path ""and the engine's sentence, publish-draftsoutcome: refused, no active row); the per-itemPOST /meta/flow/:name/publishand a livePUTeach 422 with no active row; control: well-formed loop, parallel and try_catch each read valid, publish, and reachregistration.state: registered.packages/lint/src/runtime-gate.test.ts(brokenApprovalFlow/cleanApprovalFlow) andpackages/lint/src/validate-flow-trigger-readiness.test.ts(apiFlow,startSecond). Their runtime-gate fixtures were flows the flow schema refuses (no flow label, unlabelled nodes, a top-leveltriggerkey, no edges), which the engine never registers and which this rule now reports on the gate. The fixtures are made schema-valid; no assertion changed.Registration / publish measurement (difference 4)
triggerkey, an unknown flow type,edgesmissing) is refused by the engine'sregisterFlowtoo: the rule refuses nothing the engine would register.get_recordconfig.filters) failsFlowSchemaraw, passes once converted, andregisterFlowREGISTERS it. Seeded as a stored legacy draft, it is refused atpublishMetaItemandpublishPackageDraftswith[expression-invalid, flow-region-shape-invalid]:expression-invalidalready refuses it on main, so this PR adds no new refusal on that spelling.Acceptance notes
os validate,os buildandos lintalready ran this rule (feat(lint)!: os lint refuses a flow region the engine refuses to register (flow-region-shape-invalid) #29495); the CLI is unchanged.Verification
Head
2cb755ef(origin/main merged at f99a058, which carries #29495's merge 7f368f5).Red first (tests on the main code)
On the earlier round the same door pins went red on main with the gate answering nothing (8 failed | 38 passed). On this head the ablation below puts main's registry row back (
surfaces: CLI_ONLY) and the same 8 metadata-protocol cases fail, which is the red-first reading for the registry route.Ablation (registry row back to CLI_ONLY, through lint dist)
scripts/ablation-replace.mjsonpackages/lint/src/authoring-rules.ts(row back tosurfaces: CLI_ONLYwith a markedsurfaceReason),pnpm --filter @objectstack/lint build,ablation-dist-preflight.mjs @objectstack/lint ABLATION_29233: marker present in 4 built files.runtime-authoring-gate.flow-region.test.ts+protocol.draft-publish-verdict.test.ts:Tests 8 failed | 37 passed (45); the 8 are the six refusal cases of the pure-gate file and the CASES row plus the location pin; the controls and the draft case pass.Tests 2 failed | 1 passed (3); the card case and the per-item/live case fail, the control passes.--absentclean on all 20 built files,git status --porcelainempty.Suites, gates, dogfood
All on head 2cb755e after
pnpm install --frozen-lockfileand a full packages build (78/78 tasks).pnpm --filter @objectstack/lint typecheck && test: Test Files 192 passed (192), Tests 7885 passed (7885).pnpm --filter @objectstack/metadata-protocol typecheck && test: Test Files 308 passed | 4 skipped (312), Tests 22240 passed | 26 skipped (22266).pnpm exec eslint --no-inline-config(the rootpnpm lintcommand) on the six touched TS files, exit 0.node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstacklisted 63 families; all 63 run, exit 0.--ranreconciliation: "63 derived famil(ies) accounted for — 63 run, 0 NOT-MEASURED (a DERIVED zero)".os-verify-lock.sh -c "pnpm --filter @objectstack/dogfood test"): VERDICT command-exit 0; Test Files 398 passed | 3 skipped (401), Tests 3280 passed | 28 skipped (3308).Generated by Claude Code
🤖 Generated with Claude Code
https://claude.ai/code/session_01ADHkmSfgX3fNF6rAXudbtz