From 1aeffb47037c5749beb144d5c9f8386c3e52c760 Mon Sep 17 00:00:00 2001 From: Michael Davis Date: Tue, 15 Sep 2026 13:04:24 -0400 Subject: [PATCH] Scope shadow receipt identities to each retry --- src/value-receipt.js | 9 ++++++++- tests/tasks-capability.test.js | 7 +++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/value-receipt.js b/src/value-receipt.js index f906203..0230fd5 100644 --- a/src/value-receipt.js +++ b/src/value-receipt.js @@ -1,3 +1,5 @@ +import { contentIdentity } from './canonical.js'; + const RECEIPT_SCHEMA = 'opsle.value-receipt.v1'; function measurement({ id, baseline, result, delta, unit, direction, operatorDisplay, limitation = [] }) { @@ -173,7 +175,12 @@ export function buildShadowValueReceipt(plan, shadow, { work_classification: 'DETERMINISTIC_VERIFICATION_SHADOW', }, operation: { - id: shadow.observation_identity, + // The observation identity deliberately stays stable when the same plan + // and results are reproduced. A receipt operation is one execution of + // that observation, so include its execution generation to keep retries + // distinct within the same Visible Value run. + id: contentIdentity({ execution_id: executionId, generation, + observation_identity: shadow.observation_identity }), name: 'verification-shadow', configuration_id: plan.provenance.verification_catalog_identity, policy_id: plan.provenance.policy_identity, diff --git a/tests/tasks-capability.test.js b/tests/tasks-capability.test.js index 98d2bdf..f77a8d2 100644 --- a/tests/tasks-capability.test.js +++ b/tests/tasks-capability.test.js @@ -139,6 +139,13 @@ test('installable artifact contains planner and schemas, plans deterministically result.value.decision.plan.plan_identity); const repeat = adapter.invoke('verification.plan', { ...request(f), generation: 2 }); assert.deepEqual(repeat.value.decision, result.value.decision); + const repeatedShadow = adapter.invoke('verification.shadow', + shadowRequest(f, repeat.value.decision.plan, { generation: 2 })); + assert.equal(repeatedShadow.value.shadow.observation_identity, + finalized.value.shadow.observation_identity, + 'the same plan and results retain their content identity'); + assert.notEqual(repeatedShadow.receipts[0].operation.id, receipt.operation.id, + 'each execution generation has a distinct receipt operation'); const capture = () => adapter.invoke('verification.capture', { schema: 'opsle.execution.change-capture-request.v1', task: f.task }).value; assert.equal(capture().target_revision, result.value.change.target_revision); writeFileSync(resolve(f.task.repo_path, 'a.js'), 'drift\n');