FN-9129: fence revision attribution assertions by identity
Make settings revision attribution tests select only immutable revisions owned by each test. - Resolve reset-ordering failures by querying revision IDs from unique targets and payloads. - Prove late background system writes cannot contaminate actor assertions. - Document the loaded-lane diagnosis, evidence, and remediation. Files changed: .../suite-only-flakes-observed-register.md | 26 ++++++ .../settings-revision-attribution.test.ts | 104 ++++++++++++++++----- 2 files changed, 107 insertions(+), 23 deletions(-) Fusion-Task-Id: FN-9129 Fusion-Task-Lineage: ae336ed2-0870-400c-9599-37adb588e283 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
@@ -222,6 +222,32 @@ FN-9116 adds deterministic ordering coverage for desktop and mobile rows across
|
||||
|
||||
The flake is structurally removed rather than stabilized: every hydration/recovery writer now has an ownership boundary before it can overwrite a newer turn. This is a published behavior fix, so FN-9116 includes a patch changeset.
|
||||
|
||||
## 11. Settings revision attribution reset-ordering assertion
|
||||
|
||||
- **File:** `packages/core/src/__tests__/settings-revision-attribution.test.ts`
|
||||
- **Exact test:** `settings revision attribution > round-trips every explicit provenance variant through committed JSONB revisions`
|
||||
- **Owner:** FN-9129
|
||||
- **Observed tree/SHA:** retained FN-9128 logs; remediation started at `5e5422de6e57ead4f0c4a253b47b59063c1f9fe3`.
|
||||
- **Observed frequency:** 5/5 retained loaded full-core runs (default, 4, 8, 12, and sampled 12 workers), not 4/5.
|
||||
|
||||
| run | result |
|
||||
|---|---|
|
||||
| FN-9128 loaded campaign | **failed 5/5**; retained `/tmp/fn-9128-core-*.log` |
|
||||
| FN-9129 isolated pre-fix | **failed**; `/tmp/fn-9129-solo-1.log` |
|
||||
| FN-9129 full core, 4 workers ×3 | subject passed after repair; first run had unrelated satellite-store failure, remaining two runs clean |
|
||||
| FN-9129 full core, 12 workers ×1 | passed after repair; co-observed command-center cases passed |
|
||||
|
||||
Verbatim observed failure:
|
||||
|
||||
```
|
||||
FAIL src/__tests__/settings-revision-attribution.test.ts > settings revision attribution > round-trips every explicit provenance variant through committed JSONB revisions
|
||||
AssertionError: expected [ { id: 'fusion-system', …(1) }, …(4) ] to deeply equal [ { kind: 'human', …(1) }, …(4) ]
|
||||
```
|
||||
|
||||
**Resolved 2026-08-16 (FN-9129):** This was not configuration-provenance loss. A direct table dump retained in the FN-9129 `evidence` task document and `/tmp/fn-9129-instrumented.log` showed all five explicit actors physically persisted among 19 rows. The shared harness intentionally restarts identities between tests; consequently `ORDER BY sequence ASC` has duplicate values across reset boundaries, and the test's `.slice(before.length)` count window selected previous system rows. The assertion now identifies each explicit write by its test-owned `taskPrefix`, retains its immutable revision UUID, and re-reads only those IDs; regression coverage adds a post-snapshot background system write to prove it cannot enter the provenance assertion. This preserves the provenance invariant without retries, waits, quarantines, timeout changes, or a broad harness mutation.
|
||||
|
||||
The two command-center durable-agent activity cases observed once at 12 workers are classified as co-observed identity-reuse risk, not this subject's cause: the repaired 12-worker campaign passed them. Core PostgreSQL quarantine remains forbidden and `quarantinedCoreTests` remains empty.
|
||||
|
||||
## 9. Create Room picker loaded-lane state ordering
|
||||
|
||||
- **File:** `packages/dashboard/app/components/__tests__/CreateRoomModal.test.tsx`
|
||||
|
||||
@@ -21,16 +21,55 @@ pgDescribe("settings revision attribution", () => {
|
||||
afterEach(h.afterEach);
|
||||
afterAll(h.afterAll);
|
||||
|
||||
async function revisions(): Promise<Array<{ id: string; configKind: string; changedBy: ConfigChangedBy }>> {
|
||||
return await h.adminDb().execute(sql`
|
||||
SELECT id, config_kind AS "configKind", changed_by AS "changedBy"
|
||||
/*
|
||||
FNXC:ConfigVersioning 2026-08-16-20:17:
|
||||
A snapshot-difference window admits late background revisions after a harness
|
||||
reset. Attribution assertions instead compose only immutable IDs whose target
|
||||
or payload is uniquely owned by this test's writes.
|
||||
*/
|
||||
async function revisionIdsForTarget(configKind: string, configTarget: Record<string, string>) {
|
||||
const rows = await h.adminDb().execute(sql`
|
||||
SELECT id
|
||||
FROM project.configuration_revisions
|
||||
ORDER BY sequence ASC
|
||||
`) as Array<{ id: string; configKind: string; changedBy: ConfigChangedBy }>;
|
||||
WHERE config_kind = ${configKind}
|
||||
AND config_target @> ${JSON.stringify(configTarget)}::jsonb
|
||||
ORDER BY created_at ASC, sequence ASC, id ASC
|
||||
`) as Array<{ id: string }>;
|
||||
return rows.map((row) => row.id);
|
||||
}
|
||||
|
||||
async function revisionActors(): Promise<ConfigChangedBy[]> {
|
||||
return (await revisions()).map((row) => row.changedBy);
|
||||
async function revisionIdForAfterValue(configKind: string, key: string, value: string) {
|
||||
const rows = await h.adminDb().execute(sql`
|
||||
SELECT id
|
||||
FROM project.configuration_revisions
|
||||
WHERE config_kind = ${configKind}
|
||||
AND "after" ->> ${key} = ${value}
|
||||
ORDER BY created_at DESC, sequence DESC, id DESC
|
||||
`) as Array<{ id: string }>;
|
||||
expect(rows).toHaveLength(1);
|
||||
return rows[0]!.id;
|
||||
}
|
||||
|
||||
async function revisionForTaskPrefix(taskPrefix: string) {
|
||||
const rows = await h.adminDb().execute(sql`
|
||||
SELECT id, config_kind AS "configKind", changed_by AS "changedBy"
|
||||
FROM project.configuration_revisions
|
||||
WHERE config_kind = 'project-settings'
|
||||
AND "after" ->> 'taskPrefix' = ${taskPrefix}
|
||||
ORDER BY created_at DESC, sequence DESC, id DESC
|
||||
`) as Array<{ id: string; configKind: string; changedBy: ConfigChangedBy }>;
|
||||
expect(rows).toHaveLength(1);
|
||||
return rows[0]!;
|
||||
}
|
||||
|
||||
async function revisionById(id: string) {
|
||||
const rows = await h.adminDb().execute(sql`
|
||||
SELECT id, config_kind AS "configKind", changed_by AS "changedBy"
|
||||
FROM project.configuration_revisions
|
||||
WHERE id = ${id}
|
||||
`) as Array<{ id: string; configKind: string; changedBy: ConfigChangedBy }>;
|
||||
expect(rows).toHaveLength(1);
|
||||
return rows[0]!;
|
||||
}
|
||||
|
||||
it("persists system for every omitted-actor configuration writer", async () => {
|
||||
@@ -38,12 +77,16 @@ pgDescribe("settings revision attribution", () => {
|
||||
const layer = { ...store.getAsyncLayer()!, projectId: store.getWorkflowSettingsProjectId() };
|
||||
const automationStore = new AutomationStore(h.rootDir, { asyncLayer: layer });
|
||||
const routineStore = new RoutineStore(h.rootDir, { asyncLayer: layer });
|
||||
const before = await revisions();
|
||||
|
||||
await store.updateSettings({ taskPrefix: "ATR" });
|
||||
const projectRevisionId = (await revisionForTaskPrefix("ATR")).id;
|
||||
await store.updateGlobalSettings({ defaultModelId: "attribution-model" });
|
||||
const globalRevisionId = await revisionIdForAfterValue("global-settings", "defaultModelId", "attribution-model");
|
||||
const directGlobal = await store.globalSettingsStore.updateSettings({ defaultModelId: "direct-attribution-model" });
|
||||
await store.updateWorkflowSettingValues("builtin:coding", store.getWorkflowSettingsProjectId(), { workflowStepTimeoutMs: 1_000 });
|
||||
const directGlobalRevisionId = await revisionIdForAfterValue("global-settings", "defaultModelId", "direct-attribution-model");
|
||||
const workflowProjectId = store.getWorkflowSettingsProjectId();
|
||||
await store.updateWorkflowSettingValues("builtin:coding", workflowProjectId, { workflowStepTimeoutMs: 1_000 });
|
||||
const [workflowRevisionId] = await revisionIdsForTarget("workflow-settings", { workflowId: "builtin:coding", projectId: workflowProjectId });
|
||||
expect(workflowRevisionId).toBeDefined();
|
||||
|
||||
const schedule = await automationStore.createSchedule({
|
||||
name: "Attribution schedule", scheduleType: "daily", command: "",
|
||||
@@ -60,22 +103,31 @@ pgDescribe("settings revision attribution", () => {
|
||||
});
|
||||
await routineStore.updateRoutine(routine.id, { name: "Updated attribution routine" });
|
||||
|
||||
const created = (await revisions()).slice(before.length);
|
||||
const globalRevision = created.find((revision) => revision.configKind === "global-settings");
|
||||
expect(globalRevision).toBeDefined();
|
||||
await store.globalSettingsStore.rollbackConfiguration(globalRevision!.id);
|
||||
|
||||
const automationRevision = created.find((revision) => revision.configKind === "automation" && revision.id !== created.find((candidate) => candidate.configKind === "automation")?.id);
|
||||
expect(automationRevision).toBeDefined();
|
||||
await automationStore.rollbackConfiguration(automationRevision!.id);
|
||||
const automationRevisionIds = await revisionIdsForTarget("automation", { automationId: schedule.id });
|
||||
expect(automationRevisionIds).toHaveLength(3);
|
||||
const globalRollback = await store.globalSettingsStore.rollbackConfiguration(globalRevisionId);
|
||||
await automationStore.rollbackConfiguration(automationRevisionIds[1]!);
|
||||
await automationStore.deleteSchedule(schedule.id);
|
||||
|
||||
const routineRevision = created.find((revision) => revision.configKind === "routine" && revision.id !== created.find((candidate) => candidate.configKind === "routine")?.id);
|
||||
expect(routineRevision).toBeDefined();
|
||||
await routineStore.rollbackConfiguration(routineRevision!.id);
|
||||
const routineRevisionIds = await revisionIdsForTarget("routine", { routineId: routine.id });
|
||||
expect(routineRevisionIds).toHaveLength(2);
|
||||
await routineStore.rollbackConfiguration(routineRevisionIds[1]!);
|
||||
await routineStore.deleteRoutine(routine.id);
|
||||
|
||||
const actors = (await revisions()).slice(before.length).map((revision) => revision.changedBy);
|
||||
/* FNXC:ConfigVersioning 2026-08-16-20:17: A post-snapshot system revision must not join this test-owned actor set. */
|
||||
await store.updateSettings({ taskPrefix: "ATR-background" });
|
||||
|
||||
const ownedRevisionIds = [
|
||||
projectRevisionId,
|
||||
globalRevisionId,
|
||||
directGlobalRevisionId,
|
||||
workflowRevisionId!,
|
||||
...(await revisionIdsForTarget("automation", { automationId: schedule.id })),
|
||||
...(await revisionIdsForTarget("routine", { routineId: routine.id })),
|
||||
globalRollback.id,
|
||||
];
|
||||
expect(new Set(ownedRevisionIds).size).toBe(14);
|
||||
const actors = (await Promise.all(ownedRevisionIds.map(revisionById))).map((revision) => revision.changedBy);
|
||||
expect(actors).toHaveLength(14);
|
||||
expect(actors).toEqual(Array.from({ length: 14 }, () => ({ kind: "system", id: "fusion-system" })));
|
||||
expect(actors).not.toContainEqual(expect.objectContaining({ kind: "human" }));
|
||||
@@ -84,7 +136,6 @@ pgDescribe("settings revision attribution", () => {
|
||||
|
||||
it("round-trips every explicit provenance variant through committed JSONB revisions", async () => {
|
||||
const store = h.store();
|
||||
const before = await revisionActors();
|
||||
const actors: ConfigChangedBy[] = [
|
||||
{ kind: "human", id: "future-auth-user" },
|
||||
{ kind: "agent", id: "agent-1" },
|
||||
@@ -92,11 +143,18 @@ pgDescribe("settings revision attribution", () => {
|
||||
{ kind: "api", id: "http:test-verified" },
|
||||
{ kind: "rollback", id: "rollback-test" },
|
||||
];
|
||||
const revisionIds: string[] = [];
|
||||
|
||||
for (const [index, actor] of actors.entries()) {
|
||||
await store.updateSettings({ taskPrefix: `ATR${index}` }, actor);
|
||||
const taskPrefix = `ATR${index}`;
|
||||
await store.updateSettings({ taskPrefix }, actor);
|
||||
revisionIds.push((await revisionForTaskPrefix(taskPrefix)).id);
|
||||
}
|
||||
|
||||
expect((await revisionActors()).slice(before.length)).toEqual(actors);
|
||||
/* FNXC:ConfigVersioning 2026-08-16-20:00: A background system write after the explicit writes must not enter their ID-addressed provenance assertion. */
|
||||
const lateBackgroundWrite = Promise.resolve().then(() => store.updateSettings({ taskPrefix: "ATR-background" }));
|
||||
await lateBackgroundWrite;
|
||||
|
||||
expect((await Promise.all(revisionIds.map(revisionById))).map((revision) => revision.changedBy)).toEqual(actors);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user