Skip to content

fix(engine): re-arm an unreported post-merge gate instead of deferring forever - #34

Open
timoteo7 wants to merge 1 commit into
mainfrom
fix/post-merge-evidence-fence-unreachable
Open

timoteo7 wants to merge 1 commit into
mainfrom
fix/post-merge-evidence-fence-unreachable

Conversation

@timoteo7

Copy link
Copy Markdown
Owner

fix(engine): re-arm an unreported post-merge gate instead of deferring forever

FN-9369 made the post-merge-verification optional group a REQUIRED gate, and
upgradeLegacyCodingPostMergeVerificationStepIds() auto-migrates older built-in
coding tasks onto it, so a task can acquire a gate it never enabled by hand.

That gate is a GRAPH post-merge hop. workflow-graph-executor only walks it from
inside runLegacyMergeSeam (postMergeEntryNodeIds -> walk(entryId)), i.e. while
the merge node is still executing. Once the merge seam has returned, every later
finalizeProvenAutoMergeTask call finds the gate unreported and returns blocked,
and nothing re-entered the graph at the post-merge node.

Measured on a local checkout with no upstream CI: a task with real commits landed
(mergeDetails.mergeConfirmed, mergedAt recorded) stayed in in-review forever
while the engine logged Auto-merge finalization deferred ... required post-merge evidence gate 'post-merge-verification' has not reported every 15s. The gate's own
prompt demands post-landing Full Suite / shard / timing evidence that such a
repository can never produce, so the block was terminal by construction.

The guard is right and is kept: no evidence, no completion. What was missing is
who owns RUNNING the gate. Finalization now schedules the unreported gate as a
runnable continuation before deferring, so the deadlock resolves instead of
silently parking the card.

The write goes through replaceActiveTaskWorkflowContinuation because a bare
upsert targets a different constraint than the one-active-continuation index and
raises when a row already exists — the failure mode that previously deadlocked the
board. The advisory pre-check skips a gate that already has a live continuation,
and a write failure is logged rather than thrown, so a refused finalization never
becomes a crash.

No existing open PR in the fork touches confirmed-merge-reconciliation.ts,
workflow-optional-steps.ts, builtin-coding-workflow-ir.ts or
builtin-post-merge-group.ts; PR #26 (fm/fusion-noop-merge) addresses a different
defect (in-review cards that committed nothing) and does not apply to cards with
real commits.

Verified: 2198 engine tests pass, 17 files fail identically on unmodified
origin/main (pre-existing, not a regression), typecheck and the full static gate
clean.

Por que nenhuma opcao de contorno resolveria

Desativar graphNativePostMerge ou remover o step por task deixa o card passar, mas o defeito
volta na proxima task que o upgradeLegacyCodingPostMergeVerificationStepIds() migrar. Esta
correction ataca a lacuna que faz o gate ser inalcancavel, nao o sintoma.

Teste que prova o defeito

post-merge-gate-scheduling.test.ts falha na base (upsertWorkflowWorkItem chamado 0 vezes) e
passa com a correcao. Ele fixa os tres contratos:

  • gate nao reportado vira runnable (o bloqueio continua, mas deixa de ser terminal)
  • gate com continuacao ativa nao e re-semeado (nao corre contra o engine)
  • gate desabilitado finaliza normalmente (nao muda semantica existente)

O teste existente confirmed-merge-must-finalize.test.ts, que fixa "blocks until the enabled
post-merge gate approves", continua passando sem alteracao.

Verificacao

  • pnpm --filter @fusion/engine typecheck limpo
  • pnpm test:gate:static completo limpo (inclui check-changeset-format,
    check-no-test-timeout-appeasement, check-no-comment-assertions-in-tests)
  • suite de merge/review/finalizacao (217 arquivos): 192 passaram, 17 falharam
  • os mesmos 17 arquivos falham em origin/main sem esta mudanca (baseline rodada em worktree
    limpo): sao falhas pre-existentes do repo, nao regressao

…g forever

FN-9369 made the `post-merge-verification` optional group a REQUIRED gate, and
`upgradeLegacyCodingPostMergeVerificationStepIds()` auto-migrates older built-in
coding tasks onto it, so a task can acquire a gate it never enabled by hand.

That gate is a GRAPH post-merge hop. `workflow-graph-executor` only walks it from
inside `runLegacyMergeSeam` (`postMergeEntryNodeIds` -> `walk(entryId)`), i.e. while
the `merge` node is still executing. Once the merge seam has returned, every later
`finalizeProvenAutoMergeTask` call finds the gate unreported and returns `blocked`,
and nothing re-entered the graph at the post-merge node.

Measured on a local checkout with no upstream CI: a task with real commits landed
(`mergeDetails.mergeConfirmed`, mergedAt recorded) stayed in `in-review` forever
while the engine logged `Auto-merge finalization deferred ... required post-merge
evidence gate 'post-merge-verification' has not reported` every 15s. The gate's own
prompt demands post-landing Full Suite / shard / timing evidence that such a
repository can never produce, so the block was terminal by construction.

The guard is right and is kept: no evidence, no completion. What was missing is
who owns RUNNING the gate. Finalization now schedules the unreported gate as a
runnable continuation before deferring, so the deadlock resolves instead of
silently parking the card.

The write goes through `replaceActiveTaskWorkflowContinuation` because a bare
upsert targets a different constraint than the one-active-continuation index and
raises when a row already exists — the failure mode that previously deadlocked the
board. The advisory pre-check skips a gate that already has a live continuation,
and a write failure is logged rather than thrown, so a refused finalization never
becomes a crash.

No existing open PR in the fork touches `confirmed-merge-reconciliation.ts`,
`workflow-optional-steps.ts`, `builtin-coding-workflow-ir.ts` or
`builtin-post-merge-group.ts`; PR #26 (fm/fusion-noop-merge) addresses a different
defect (in-review cards that committed nothing) and does not apply to cards with
real commits.

Verified: 2198 engine tests pass, 17 files fail identically on unmodified
origin/main (pre-existing, not a regression), typecheck and the full static gate
clean.
@github-actions

Copy link
Copy Markdown

ThreatCrush Security Scan

4589 finding(s)

HIGH/CRITICAL: 43 | MEDIUM: 4035 | LOW: 511

Severity Rule Location
HIGH secret-database-url .github/workflows/full-suite.yml:55
HIGH secret-generic-credential .github/workflows/full-suite.yml:56
HIGH secret-database-url .github/workflows/full-suite.yml:284
HIGH secret-generic-credential .github/workflows/full-suite.yml:285
HIGH secret-database-url .github/workflows/full-suite.yml:324
HIGH secret-generic-credential .github/workflows/full-suite.yml:325
HIGH secret-database-url .github/workflows/pr-checks.yml:221
HIGH secret-generic-credential .github/workflows/pr-checks.yml:222
HIGH secret-generic-credential .github/workflows/release.yml:522
HIGH secret-generic-credential .github/workflows/release.yml:524
HIGH secret-generic-credential .github/workflows/test-release.yml:445
HIGH secret-generic-credential .github/workflows/test-release.yml:447
HIGH secret-generic-credential docs/cli-reference.md:80
HIGH secret-generic-credential docs/signals-connectors.md:34
HIGH secret-generic-credential docs/signals-connectors.md:77
HIGH secret-generic-credential docs/signals-connectors.md:94
HIGH secret-generic-credential docs/signals-connectors.md:117
HIGH secret-generic-credential docs/signals-connectors.md:159
HIGH secret-generic-credential packages/cli/STANDALONE.md:71
HIGH secret-database-url packages/core/src/postgres/credential-redact.ts:12
HIGH secret-database-url packages/core/src/postgres/credential-redact.ts:30
HIGH secret-database-url packages/core/src/postgres/credential-redact.ts:31
HIGH secret-database-url packages/core/src/postgres/credential-redact.ts:102
HIGH secret-database-url packages/core/src/postgres/credential-redact.ts:103
HIGH secret-generic-credential packages/core/src/postgres/embedded-lifecycle.ts:843
HIGH secret-database-url packages/core/src/postgres/embedded-lifecycle.ts:1574
HIGH secret-database-url packages/core/src/postgres/pg-backup.ts:756
HIGH js-ssrf-outbound-request packages/dashboard/app/public/sw.js:651
HIGH js-ssrf-outbound-request packages/dashboard/app/public/sw.js:727
HIGH js-host-header-trust packages/dashboard/src/cli-session-ws.ts:81
HIGH js-host-header-trust packages/dashboard/src/cli-session-ws.ts:115
HIGH js-ssrf-outbound-request packages/dashboard/src/routes.ts:1858
HIGH js-host-header-trust packages/dashboard/src/server.ts:2689
HIGH js-host-header-trust packages/dashboard/src/server.ts:2714
HIGH js-host-header-trust packages/dashboard/src/server.ts:3025
HIGH js-host-header-trust packages/dashboard/src/server.ts:3193
HIGH secret-slack-webhook plugins/examples/fusion-plugin-notification/README.md:46
HIGH secret-database-url scripts/pg-test-server.mjs:200
HIGH secret-database-url scripts/pg-test-server.mjs:231
HIGH secret-database-url scripts/pg-test-server.mjs:241
HIGH secret-generic-credential scripts/sync-fusion-skill-tools.mjs:550
HIGH secret-generic-credential scripts/verify-windows-elevated-restricted.mjs:81
HIGH secret-generic-credential scripts/verify-windows-encoding-recovery.mjs:41
MEDIUM redos-nested-quantifier docs/agents.md:1710
MEDIUM insecure-temp-file docs/solutions/test-failures/main-full-suite-census-2026-09-25.md:376
MEDIUM insecure-temp-file docs/solutions/test-failures/main-full-suite-census-2026-09-25.md:377
MEDIUM insecure-temp-file docs/solutions/test-failures/main-full-suite-census-2026-09-25.md:541
MEDIUM insecure-temp-file packages/cli/src/__tests__/bin.test.ts:136
MEDIUM insecure-temp-file packages/cli/src/__tests__/dev-with-memory-lib.test.ts:33
MEDIUM insecure-temp-file packages/cli/src/__tests__/dev-with-memory-lib.test.ts:34

…and 4539 more. Full results in the Security tab.

Snippets are redacted; ThreatCrush never prints matched credential material.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant