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:
gsxdsm
2026-08-16 13:47:12 -07:00
parent 1c9e4dfccd
commit 3c235ce275
2 changed files with 109 additions and 25 deletions

View File

@@ -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`

View File

@@ -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);
});
});