Found while implementing #29. Filed unassigned. No shipped metadata is affected — duly_assignment_fanout is correct today, and #29's test/flow-predicates.test.ts now covers this ground soundly. This is about the older, weaker assertion left behind.
The assertion
test/assignment-fanout.test.ts, in "every predicate qualifies its record reads with record.":
constbare=newRegExp(`(^|[^.\\w])${field}\\b`);if(bare.test(source)){expect(source,`${where} reads '${field}' bare — write record.${field}`).toContain(`record.${field}`);}It detects the bare occurrence correctly, then asks whether the source string containsrecord.<field>anywhere. Those are different questions. A predicate that reads a field both ways satisfies the toContain and passes:
condition: P`status == "dispatched" && record.status != "cancelled"`
bare.test matches on the leading status, toContain('record.status') is true because of the second clause, and the assertion passes with a genuine bare reference in the predicate. Same for any compound predicate where one clause is qualified and another is not — which is the realistic shape, since a predicate grows a clause at a time and only the new clause gets written wrong.
Two smaller consequences of the same toContain shape:
- It reports at most one field per predicate, so
status == "x" && due_date != null is one finding, not two. - It cannot say which occurrence was bare, so the message points at the predicate rather than the read.
Why it is not urgent
test/flow-predicates.test.ts (#29) checks per-identifier rather than per-source, over dulyFlows rather than one flow, and recurses into region bodies via collectFlowGraphs. It flags the case above:
duly_assignment_fanout · node 'start' config.condition: reads 'status' bare — write record.status
So the repo is covered. What remains is a weaker duplicate assertion sitting next to the strong one, which is a maintenance hazard rather than a live hole: a reader who finds the scoped version first may believe the coverage is thinner than it is, or "fix" a future finding by qualifying one clause.
Suggested fix
Delete the scoped assertion from test/assignment-fanout.test.ts and let test/flow-predicates.test.ts own the rule. It was written before the repo-level walk existed and its own comment says so ("this is the only gate on the house rule"), which is no longer true.
Left alone in #29 deliberately: that card's declared file surface was AGENTS.md and test/flow-predicates.test.ts, and test/assignment-fanout.test.ts belongs to another card's PR.
Note for whoever picks this up: both files go away or shrink when objectstack-ai/objectstack#14089 lands and pnpm validate covers bare identifiers itself.
Found while implementing #29. Filed unassigned. No shipped metadata is affected —
duly_assignment_fanoutis correct today, and #29'stest/flow-predicates.test.tsnow covers this ground soundly. This is about the older, weaker assertion left behind.The assertion
test/assignment-fanout.test.ts, in "every predicate qualifies its record reads withrecord.":It detects the bare occurrence correctly, then asks whether the source string contains
record.<field>anywhere. Those are different questions. A predicate that reads a field both ways satisfies thetoContainand passes:bare.testmatches on the leadingstatus,toContain('record.status')is true because of the second clause, and the assertion passes with a genuine bare reference in the predicate. Same for any compound predicate where one clause is qualified and another is not — which is the realistic shape, since a predicate grows a clause at a time and only the new clause gets written wrong.Two smaller consequences of the same
toContainshape:status == "x" && due_date != nullis one finding, not two.Why it is not urgent
test/flow-predicates.test.ts(#29) checks per-identifier rather than per-source, overdulyFlowsrather than one flow, and recurses into region bodies viacollectFlowGraphs. It flags the case above:So the repo is covered. What remains is a weaker duplicate assertion sitting next to the strong one, which is a maintenance hazard rather than a live hole: a reader who finds the scoped version first may believe the coverage is thinner than it is, or "fix" a future finding by qualifying one clause.
Suggested fix
Delete the scoped assertion from
test/assignment-fanout.test.tsand lettest/flow-predicates.test.tsown the rule. It was written before the repo-level walk existed and its own comment says so ("this is the only gate on the house rule"), which is no longer true.Left alone in #29 deliberately: that card's declared file surface was
AGENTS.mdandtest/flow-predicates.test.ts, andtest/assignment-fanout.test.tsbelongs to another card's PR.Note for whoever picks this up: both files go away or shrink when objectstack-ai/objectstack#14089 lands and
pnpm validatecovers bare identifiers itself.