From 773b9a2af6917d31cd64adfffe3081784d9829bc Mon Sep 17 00:00:00 2001 From: namastex888 Date: Thu, 23 Jul 2026 02:14:48 -0300 Subject: [PATCH] fix(delivery): republish a stale record when a verified delivery lands on a current generation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Live-QA finding (v5.260723.6): genie install converged the codex plugin itself (normal non-deferred path), leaving installed N current with T — but publication was deferral-only, so the prior generation's delivery record survived the verified install and setup's Decision-9 gate refused with 'mismatch' while its recovery pointed back at the very commands that skip publishing in that state. - codex-delivery seam: CodexDeliveryFacts gains an optional existingRecord read-state; a 'current' classification now publishes ONLY when the caller supplies that state and it fails the core binding (absent/invalid/stale) — a matching record is never republished, and callers that omit the state keep the conservative no-publish contract - install: publication is no longer deferral-only; the converged path derives N from the snapshot registration and routes through the same shared gate - update: the delivery-flow publisher passes the record state for the same converged-host truth Both call sites sit immediately after a signature/attestation-verified tarball delivery and bind the physically scanned tree, so this is delivery-fact recording under Decision 10, not self-attestation; the same-version REPAIR path (no fresh artifact) is untouched. --- src/genie-commands/codex-delivery.test.ts | 48 ++++++++++++++++++++++- src/genie-commands/codex-delivery.ts | 27 ++++++++++++- src/genie-commands/install.ts | 32 ++++++++------- src/genie-commands/update.ts | 4 ++ 4 files changed, 95 insertions(+), 16 deletions(-) diff --git a/src/genie-commands/codex-delivery.test.ts b/src/genie-commands/codex-delivery.test.ts index dbbbc3103..e58df4b03 100644 --- a/src/genie-commands/codex-delivery.test.ts +++ b/src/genie-commands/codex-delivery.test.ts @@ -68,7 +68,7 @@ describe('buildDeliveryPublication — publish facts only for pending', () => { ).toEqual({ targetVersion: T, canonicalPayloadSha256: DIGEST, channel: 'dev' }); }); - test('current publishes nothing', () => { + test('current without a record read-state publishes nothing (conservative pre-E contract)', () => { expect( buildDeliveryPublication({ installedVersion: T, @@ -78,6 +78,52 @@ describe('buildDeliveryPublication — publish facts only for pending', () => { }), ).toBeNull(); }); + + test('current with a MATCHING record never republishes (idempotent)', () => { + expect( + buildDeliveryPublication({ + installedVersion: T, + targetVersion: T, + canonicalPayloadSha256: DIGEST, + channel: 'dev', + existingRecord: { + status: 'present', + record: { + targetVersion: T, + canonicalPayloadSha256: DIGEST, + channel: 'dev', + deliveryId: 'c'.repeat(32), + }, + }, + }), + ).toBeNull(); + }); + + test('current with a STALE or absent record publishes the delivered facts (2026-07-23 live-QA regression)', () => { + // Install converged the plugin itself (N current with T) but the on-disk + // record still bound the prior generation — setup then refused `mismatch` + // while its recovery pointed at the very command that skipped publishing. + const stale = { + status: 'present' as const, + record: { + targetVersion: '5.260711.9', + canonicalPayloadSha256: 'b'.repeat(64), + channel: 'dev', + deliveryId: 'c'.repeat(32), + }, + }; + for (const existingRecord of [stale, { status: 'absent' as const }]) { + expect( + buildDeliveryPublication({ + installedVersion: T, + targetVersion: T, + canonicalPayloadSha256: DIGEST, + channel: 'dev', + existingRecord, + }), + ).toEqual({ targetVersion: T, canonicalPayloadSha256: DIGEST, channel: 'dev' }); + } + }); }); /** A store that flags any activation-time call — C must only ever call publishDelivery. */ diff --git a/src/genie-commands/codex-delivery.ts b/src/genie-commands/codex-delivery.ts index f29603f1d..ed90d9b47 100644 --- a/src/genie-commands/codex-delivery.ts +++ b/src/genie-commands/codex-delivery.ts @@ -31,6 +31,7 @@ import { compareReleaseVersions, parseReleaseVersion, } from '../lib/codex-activation.js'; +import { type DeliveryRecordReadState, assessAuthenticatedDelivery } from '../lib/codex-host-observation.js'; import type { HeldLifecycleLease } from '../lib/codex-lifecycle-lease.js'; /** The exact operator recovery every delivered-but-action-required Codex path names. */ @@ -118,6 +119,15 @@ export interface CodexDeliveryFacts { canonicalPayloadSha256: string; /** Delivery channel recorded in the published facts. */ channel: string; + /** + * The on-disk record read-state (Group E). Lets a verified delivery whose + * plugin generation is already `current` (e.g. install converged the plugin + * itself) republish a STALE record: without it, a prior-generation record + * survives the delivery and setup's Decision-9 gate refuses with `mismatch` + * while its recovery points back at the very command that skipped publishing + * (2026-07-23 live-QA finding). A matching record is never republished. + */ + existingRecord?: DeliveryRecordReadState; } /** @@ -135,7 +145,8 @@ export interface CodexDeliveryFacts { */ export function buildDeliveryPublication(facts: CodexDeliveryFacts): PublishDeliveryInput | null { const state = classifyCodexDelivery(facts.installedVersion, facts.targetVersion); - if (state.kind !== 'pending' && state.kind !== 'absent') return null; + if (state.kind === 'indeterminate') return null; + if (state.kind === 'current' && !currentNeedsRepublication(facts)) return null; const input: PublishDeliveryInput = { targetVersion: facts.targetVersion, canonicalPayloadSha256: facts.canonicalPayloadSha256, @@ -147,6 +158,20 @@ export function buildDeliveryPublication(facts: CodexDeliveryFacts): PublishDeli return input; } +/** + * A `current` generation republishes ONLY when the caller supplied the on-disk + * record state and it fails the core binding (absent/invalid/mismatched). No + * record state supplied → conservative no-publish (the pre-Group-E contract). + */ +function currentNeedsRepublication(facts: CodexDeliveryFacts): boolean { + if (facts.existingRecord === undefined) return false; + const assessment = assessAuthenticatedDelivery(facts.existingRecord, { + targetVersion: facts.targetVersion, + canonicalPayloadSha256: facts.canonicalPayloadSha256, + }); + return assessment !== 'matching'; +} + export interface PublishCodexDeliveryInput extends CodexDeliveryFacts { /** The caller-held `update-delivery` / `install-converge` lease (parent only). */ lease: HeldLifecycleLease; diff --git a/src/genie-commands/install.ts b/src/genie-commands/install.ts index 5bc1546c2..5cd21ae01 100644 --- a/src/genie-commands/install.ts +++ b/src/genie-commands/install.ts @@ -22,6 +22,7 @@ import { type LifecycleLeaseResult, acquireLifecycleLease as acquireCodexLifecycleLease, } from '../lib/codex-lifecycle-lease.js'; +import { snapshotDeliveryReadState } from '../lib/codex-lifecycle-truth.js'; import { genieConfigExists, getGenieConfigPath } from '../lib/genie-config.js'; import { retireInstallVersionMarker } from '../lib/install-version-marker.js'; import { @@ -190,9 +191,15 @@ function finalizeInstallDeliveryLifecycle( codexFailed: boolean, ): void { if (codexFailed) return; - if (codexDeferral !== null && lease !== null) { + // Group E: publication is NOT deferral-only. The normal converged path + // (install refreshed the plugin itself, so N is now current with T) also just + // performed a verified delivery — skipping publication there left a stale + // prior-generation record in place and setup refused with `mismatch` + // (2026-07-23 live-QA finding). The shared seam stays idempotent: a matching + // record is never republished. + if (lease !== null) { try { - publishDeferredInstallDeliveryFacts(codexDeferral.installedVersion, lease); + publishInstallDeliveryFacts(codexDeferral?.installedVersion ?? null, lease); } catch { // Recording the delivered fact is best-effort; never fail a completed install over it. } @@ -204,7 +211,7 @@ function finalizeInstallDeliveryLifecycle( } } -function publishDeferredInstallDeliveryFacts(installedVersion: string, lease: HeldLifecycleLease): void { +function publishInstallDeliveryFacts(deferredInstalledVersion: string | null, lease: HeldLifecycleLease): void { let command: string | null = null; try { command = resolveRuntimeExecutable('codex', process.cwd()); @@ -213,22 +220,19 @@ function publishDeferredInstallDeliveryFacts(installedVersion: string, lease: He } const snapshot = observeCodexActivation({ genieHome: GENIE_HOME, command }); if (snapshot.canonical.status !== 'ok') return; - const targetVersion = snapshot.canonical.version.canonical; - const canonicalPayloadSha256 = snapshot.canonical.digest; - if ( - snapshot.delivery.status === 'present' && - snapshot.delivery.record.targetVersion === targetVersion && - snapshot.delivery.record.canonicalPayloadSha256 === canonicalPayloadSha256 - ) { - return; // matching record already published — never republish. - } + // Deferred path: N from the deferral's live query. Converged path: N from + // this snapshot's own registration (current with T after install's refresh). + const registration = snapshot.query.status === 'ok' ? snapshot.query.registration : { present: false as const }; + const installedVersion = + deferredInstalledVersion ?? (registration.present && registration.version ? registration.version.canonical : null); publishCodexDelivery({ lease, store: openCodexActivationStore({ genieHome: GENIE_HOME }), installedVersion, - targetVersion, - canonicalPayloadSha256, + targetVersion: snapshot.canonical.version.canonical, + canonicalPayloadSha256: snapshot.canonical.digest, channel: resolveDeliveryChannelForInstall(), + existingRecord: snapshotDeliveryReadState(snapshot), }); } diff --git a/src/genie-commands/update.ts b/src/genie-commands/update.ts index 977c482f4..38c1b467a 100644 --- a/src/genie-commands/update.ts +++ b/src/genie-commands/update.ts @@ -38,6 +38,7 @@ import { type HeldLifecycleLease, acquireLifecycleLease as acquireCodexLifecycleLease, } from '../lib/codex-lifecycle-lease.js'; +import { snapshotDeliveryReadState } from '../lib/codex-lifecycle-truth.js'; import { contractPath, genieConfigExists, getGenieConfigPath, saveGenieConfig } from '../lib/genie-config.js'; import { type InstallStagingDirectoryGuard, @@ -2541,6 +2542,9 @@ function publishCodexDeliveryFacts(channel: string, previousBackup: string | nul targetVersion: snapshot.canonical.version.canonical, canonicalPayloadSha256: snapshot.canonical.digest, channel, + // Group E: a current-N delivery with a STALE record republishes (a matching + // record never does) — same fresh-host/converged-host truth as install. + existingRecord: snapshotDeliveryReadState(snapshot), }); if (published.published && published.record !== null && previousBackup !== null) { publishBackupSidecarIfProtocolCapable(previousBackup, published.record.deliveryId);