Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 47 additions & 1 deletion src/genie-commands/codex-delivery.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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. */
Expand Down
27 changes: 26 additions & 1 deletion src/genie-commands/codex-delivery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand Down Expand Up @@ -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;
}

/**
Expand All @@ -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,
Expand All @@ -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;
Expand Down
32 changes: 18 additions & 14 deletions src/genie-commands/install.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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);
Comment on lines +200 to +202

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Require verified handoff before republishing installs

This widens delivery-record publication from deferred installs to every successful in-scope genie install. When an operator runs the exposed manual path (genie install --integrations codex) rather than the install.sh handoff, this process has not downloaded or attestation-verified a release; if the local $GENIE_HOME/plugins/genie tree is stale or tampered and the existing record is absent/mismatched, the new call writes a fresh delivery record and makes setup/doctor treat those local bytes as a verified delivery. Please gate this publication to a verified installer context, or route standalone current/absent repairs through the existing re-fetch/verify flow.

Useful? React with 👍 / 👎.

} catch {
// Recording the delivered fact is best-effort; never fail a completed install over it.
}
Expand All @@ -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());
Expand All @@ -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),
});
}

Expand Down
4 changes: 4 additions & 0 deletions src/genie-commands/update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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);
Expand Down
Loading