Skip to content

Commit 4e7c8ff

Browse files
committed
test(dogfood): the sweep's SELECTION scope on the real driver stack
The differential control the branch never had: `sched_org_sweep`, a `time_relative` flow declared for org A, with matching rows in BOTH A (one) and B (two). The discriminating number is the count of LAUNCHED RUNS — 1 with the fix, 3 on the defect — because a pin asserting only "A's row was touched" passes on the defect too. Three properties, three producers: - selection: exactly one run, and it names A's record; - the data plane: `update_record` lands on A's row and on no other (the half the branch left unpinned — the earlier fixture flow was start -> notify -> end, so nothing would have gone red if the run's narrowing were wrong); - disclosure: no `sys_notification` names a B record. An unscoped sweep emits that notification BEFORE its write silently matches nothing, so a fix that narrowed only the writes would leave the leak open. The memory limb answers the other question the card is about: a store with no tenant isolation REFUSES the scoped sweep (#16589) and the failure is reported at `error` naming the flow, with a paired control proving the refusal is about the scope and not about the fixture (the same query unscoped still sees all three rows). The fixture object gains `due_date` as `datetime`, not `date`: the window is a pair of ISO instants, and a column the driver truncates to YYYY-MM-DD would put a per-driver truncation rule between the fixture and the property under test. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012zTkyNHJ7TkuN2oXtP5x37
1 parent 5cb5f96 commit 4e7c8ff

3 files changed

Lines changed: 399 additions & 5 deletions

File tree

packages/qa/dogfood/test/fixtures/schedule-organization-fixture.ts

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,23 @@
99
// `sys_organization` ids and the recipient's `sys_user` id. A fixture that
1010
// baked either one in would assert against a row that does not exist.
1111

12-
/** Object the tick touches, so a run has a data write of its own to land. */
12+
/**
13+
* Object the tick touches, so a run has a data write of its own to land.
14+
*
15+
* `due_date` is what the `time_relative` sweep selects on (#16659 F2). It is a
16+
* `datetime` rather than a `date` deliberately: the window the trigger computes
17+
* is a pair of ISO-8601 instants, and comparing them against a column the
18+
* driver truncates to `YYYY-MM-DD` puts a per-driver truncation rule between
19+
* the fixture and the property under test, which is WHICH ORGANIZATION's rows
20+
* came back.
21+
*/
1322
const SweepTargetObject = {
1423
name: 'sched_org_target',
1524
label: 'Sweep Target',
1625
fields: {
1726
name: { type: 'text', label: 'Name', required: true },
1827
touched: { type: 'checkbox', label: 'Touched' },
28+
due_date: { type: 'datetime', label: 'Due' },
1929
},
2030
};
2131

@@ -94,3 +104,75 @@ export function organizationLessScheduleFlow(recipientId: string): unknown {
94104
});
95105
return { ...declared, name: 'sched_org_undeclared', nodes };
96106
}
107+
108+
/**
109+
* [#16659 F2] The `time_relative` twin: a sweep that declares its acting
110+
* organization, selects `sched_org_target` rows whose `due_date` falls in the
111+
* next week, and — once per matched record — notifies and writes.
112+
*
113+
* Both trailing nodes are load-bearing and they measure DIFFERENT halves:
114+
*
115+
* - `notify` produces one tenant-scoped inbox row per LAUNCHED run, so the
116+
* count of those rows is the count of records the sweep SELECTED. That is
117+
* the F2 property: an unscoped sweep selects the other organization's rows
118+
* too and posts about them into the declared organization's inbox.
119+
* - `update_record` is the data-plane half the branch previously left unpinned
120+
* (the fixture flow was `start → notify → end`). The run is scoped to the
121+
* declared organization, so a write aimed at another organization's row
122+
* matches nothing — silently. Asserting WHICH rows got `touched` is what
123+
* makes that narrowing observable instead of assumed.
124+
*/
125+
export function declaringTimeRelativeFlow(organizationId: string, recipientId: string): unknown {
126+
return {
127+
name: 'sched_org_sweep',
128+
label: 'Time-relative sweep (organization declared)',
129+
type: 'schedule',
130+
status: 'active',
131+
runAs: 'system',
132+
nodes: [
133+
{
134+
id: 'start',
135+
type: 'start',
136+
label: 'Daily sweep',
137+
config: {
138+
timeRelative: {
139+
object: 'sched_org_target',
140+
dateField: 'due_date',
141+
withinDays: 7,
142+
},
143+
organization: organizationId,
144+
},
145+
},
146+
{
147+
id: 'notify',
148+
type: 'notify',
149+
label: 'Due soon',
150+
config: {
151+
topic: 'sched.due',
152+
recipients: [recipientId],
153+
title: 'Due soon: {record.name}',
154+
message: '{record.name} is due.',
155+
channels: ['inbox'],
156+
sourceObject: 'sched_org_target',
157+
sourceId: '{record.id}',
158+
},
159+
},
160+
{
161+
id: 'touch',
162+
type: 'update_record',
163+
label: 'Mark touched',
164+
config: {
165+
objectName: 'sched_org_target',
166+
filter: { id: '{record.id}' },
167+
fields: { touched: true },
168+
},
169+
},
170+
{ id: 'end', type: 'end', label: 'End' },
171+
],
172+
edges: [
173+
{ id: 'e1', source: 'start', target: 'notify' },
174+
{ id: 'e2', source: 'notify', target: 'touch' },
175+
{ id: 'e3', source: 'touch', target: 'end' },
176+
],
177+
};
178+
}

0 commit comments

Comments
 (0)