Uh oh!
There was an error while loading. Please reload this page.
Delete the weak bare-reference walk from the fan-out test - #77
Conversation
The scoped walk in test/assignment-fanout.test.ts found a bare occurrence correctly and then asked whether the source CONTAINED `record.<field>` anywhere. Those are different questions, so a compound predicate satisfied it with a genuine bare reference still in place. Measured: with the start condition written `status == "dispatched" && record.status != "cancelled"`, that assertion passes. test/flow-predicates.test.ts reports the same tree as `duly_assignment_fanout · node 'start' config.condition: reads 'status' bare — write record.status`. That file already owns the rule — per identifier, over every flow in dulyFlows, recursing into region bodies through the platform's own collectFlowGraphs, with self-tests pinning that it can fail. Two guards on one rule, one of them unable to fail, is a maintenance hazard rather than depth: a reader finding the weak one first would believe the coverage is thinner than it is, or "fix" a future finding by qualifying one clause. The file header claimed this file was the only gate for flows. That stopped being true when the repo-level walk landed; it now points at the owner. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SqkTcrxUFci7nqXdbBSe2p
os-warren
commented
Sep 1, 2026
Reviewed and verified independently — mergingDeleting a guard needs a higher bar than adding one, so I re-measured rather than reading the report. Both claims hold. Gates, re-run by me on The dominance claim, re-measured. I mutated the fan-out flow's start condition to the exact shape the card names and ran the retained guard against it, mutating and measuring inside a single shell call so no restore could sit between the two:
The deleted assertion's flaw is worth restating because it is a reusable shape: On the changeset — you were right and my dispatch was wrongLand as is, no changeset. I carried that requirement over from the If release notes are ever wanted here, that is your option C as its own card, not a directory quietly appearing in a test-only PR. Generated by Claude Code |
Uh oh!
There was an error while loading. Please reload this page.
Fixes#37
Deletes the scoped bare-reference walk from
test/assignment-fanout.test.tsand handsthe rule to
test/flow-predicates.test.ts, which already owns it. The file header, whichclaimed this file was the only gate for flows, now points at the owner.
The reproduction — the current test passes on a real bare reference
A test never seen failing is not a test, so the defect was reproduced before the walk was
touched. All measurements below mutate and measure inside a single shell invocation, with
the mutation confirmed on disk (
grep -F -cboth directions) and the file restored from abyte-copy afterwards, verified by
sha256sum.Mutation — the issue's exact shape, in
src/flows/assignment.flow.ts, the start node:A. The walk under test, in isolation. A genuine bare
statusis in the predicate:Green.
bare.test(source)matches on the leadingstatus, andtoContain('record.status')is then satisfied by the second clause — a different question from the one it just asked.
B. The realistic form: the whole file goes green. All four predicates this flow
authors are also pinned by exact-equality assertions elsewhere in the same file, so growing
one clause trips a literal pin — which an author then updates, because updating it is the
obvious thing to do. Doing both, exactly as an author would:
28/28 green with a real bare reference shipped in the flow.
C. The same tree, judged by the repo-level walk:
Located, corrective, and naming the occurrence rather than the predicate.
D. The platform gate, for completeness.
pnpm validateexits 0 on the mutatedtree. That is correct and deliberate — AGENTS.md rule 4: flow node and edge conditions run
in flattened scope, so
collectBoundRecordReadsnever judges a bare identifier there. Therepo-local gate is genuinely load-bearing on this surface; only this copy of it was not.
Why deletion rather than strengthening the walk
test/flow-predicates.test.tsstrictly dominates it: per identifier instead of per source,over every flow in
dulyFlowsinstead of this one, exempting declared flow variables(the platform's own exemption, so it cannot disagree with objectstack-ai/objectstack#14089
about what a defect is), and recursing into
loop/parallel/try_catchbodies throughthe platform's
collectFlowGraphsrather than a hand-rolledconfig.body.edgeswalk. Itcarries self-tests pinning that it can fail, including the compound case this card is about.
So the scoped walk contributed no coverage and could not fail on its own subject. Two guards
on one rule — one of them unable to fail — is a maintenance hazard rather than depth, and
both are slated to go away when objectstack-ai/objectstack#14089 lands and
pnpm validatecovers bare identifiers itself. Keeping two in step is what that file's own header warns
against.
Coverage of this flow stays anchored at both ends:
is registered in dulyFlowsin this filepins that the flow is in the collection, and the repo-level walk walks that collection.
No coverage was lost — measured after the deletion
The same mutation as B, on the fixed tree, through the full suite:
What was 28/28 green before now takes the suite red through exactly one guard, the right
one. Test count moves 536 → 535: one assertion removed, none broken.
Gates
All four green at
0cacc78, the commit this PR points at, on a clean tree:The one
validatewarning naming@objectstack/security-enterpriseis the documentedexpected state of this checkout (AGENTS.md rule 7), not a regression.
No changeset
The dispatch asked for
.changeset/*.md. This repo has no changeset mechanism: no.changeset/directory now or at any point ingit log --all, no@changesets/*dependency, no
changesetscript, no mention anywhere outsidenode_modules, and CI runsonly the four gates. AGENTS.md's "Landing your work" lists those four and never mentions a
changeset. Creating the directory here would mint a mechanism nothing reads, which the next
agent would take for a real one. Flagged for the maintainer rather than decided quietly —
say the word and it gets added. The change is test-only in any case, with no user-visible
surface.
Generated by Claude Code
Generated by Claude Code