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
10 changes: 5 additions & 5 deletions scripts/feed-production-assessment-checkpoint.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { createHash } from 'node:crypto';
import { readFileSync, writeFileSync } from 'node:fs';
import { isAbsolute, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { ACCOUNT, CORE, FEED, SQL, REPO, sourceIdentity, projectionIdentity } from './feed-production-assessment.mjs';
import { ACCOUNT, CORE, FEED, SQL, sourceIdentity, projectionIdentity } from './feed-production-assessment.mjs';
import { validateCarryoverReceipt, validateContext } from './feed-production-assessment-recovery.mjs';

const check = (ok, message) => { if (!ok) throw new Error(message); };
Expand Down Expand Up @@ -41,15 +41,15 @@ export async function verifyCheckpoint(releaseSha, manifest, carryover, { query,
check(Array.isArray(rows) && rows.length === 1, `checkpoint_${kind}_not_unique`);
return rows[0];
};
const run = await one('runs', [REPO, receipt.sourceSha]);
const run = await one('runs', [receipt.repository, receipt.sourceSha]);
check(run.id === receipt.analysisId && run.status === 'completed' && run.idempotency_key === receipt.idempotencyKey,
'checkpoint_live_run_identity_mismatch');
const row = await one('source', [receipt.analysisId, REPO]);
const row = await one('source', [receipt.analysisId, receipt.repository]);
check(Object.entries(run).every(([key, value]) => row[key] === value), 'checkpoint_live_run_changed');
const source = sourceIdentity(row, receipt.sourceSha, receipt.analysisId, receipt.web.agentId);
const source = sourceIdentity(row, receipt.sourceSha, receipt.analysisId, receipt.web.agentId, receipt.repository);
check(Object.entries(manifest.source).every(([key, value]) => Object.hasOwn(source, key) && source[key] === value),
'checkpoint_live_source_changed');
const projection = stage === 'projection' ? projectionIdentity(await one('projection', [REPO]), source, receipt.sourceSha) : null;
const projection = stage === 'projection' ? projectionIdentity(await one('projection', [receipt.repository]), source, receipt.sourceSha, receipt.repository) : null;
check(stage === 'source' || projection, 'checkpoint_projection_not_ready');
return { format: 'ghfind-production-assessment-checkpoint-result-v1', status: stage === 'source' ? 'source_verified' : 'passed', releaseSha,
sourceSha: receipt.sourceSha, intentId: receipt.intentId, receiptSHA256: manifest.receiptSHA256,
Expand Down
12 changes: 12 additions & 0 deletions scripts/feed-production-assessment-checkpoint.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,18 @@ test('completed checkpoint is freshly verified without mutating receipt or provi
assert.equal(result.status,'passed'); assert.equal(result.providerRequests,0); assert.equal(result.observedAt,123);
assert.deepEqual(calls.map(([k])=>k),['runs','source','projection']); assert.equal(JSON.stringify(m),before);
});
test('historical checkpoint retains its original repository identity', async()=>{
const legacy='hikariming/ghfind', m=checkpoint(), calls=[];
m.receipt.repository=legacy; m.receiptSHA256=checkpointHash(m.receipt);
const historical={
runs:[run({repo_key:legacy,status:'completed'})],
source:[source({repo_key:legacy,requested_repo_key:legacy,aggregate_key:legacy})],
projection:[projection({job_repo:legacy})],
};
const result=await verifyCheckpoint('b'.repeat(40),m,carryover,{query:async(k,p)=>{calls.push([k,p]);return historical[k];}});
assert.equal(result.status,'passed');
assert.deepEqual(calls,[['runs',[legacy,SHA]],['source',[ID,legacy]],['projection',[legacy]]]);
});
test('tampering, mismatched carryover, and nonterminal checkpoints fail before reads',async()=>{
for(const mutate of [m=>m.receipt.polls=1,m=>m.receipt.analysisId='other',m=>{m.receipt.phase='waiting';m.receiptSHA256=checkpointHash(m.receipt);},m=>delete m.source.providerThreadId]){
const m=checkpoint(); mutate(m); let reads=0;
Expand Down
18 changes: 9 additions & 9 deletions scripts/feed-production-assessment.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -381,12 +381,12 @@ function candidate(row) {
sourceHash: row.source_hash,
};
}
function runIdentity(row, sha, wantedId) {
function runIdentity(row, sha, wantedId, repository = REPO) {
check(
row &&
id(row.id) &&
(!wantedId || row.id === wantedId) &&
row.repo_key === REPO &&
row.repo_key === repository &&
row.requested_ref === sha &&
Object.entries(versions).every(([k, v]) =>
k === "skill_version" ? acceptedSkillVersions.has(row[k]) : row[k] === v,
Expand Down Expand Up @@ -608,8 +608,8 @@ export async function startAssessment(sha, path, options = {}) {
c.close();
}
}
export function sourceIdentity(row, sha, analysisId, agentId) {
const run = runIdentity(row, sha, analysisId);
export function sourceIdentity(row, sha, analysisId, agentId, repository = REPO) {
const run = runIdentity(row, sha, analysisId, repository);
check(
row.status === "completed" &&
row.create_attempts >= 1 &&
Expand All @@ -630,9 +630,9 @@ export function sourceIdentity(row, sha, analysisId, agentId) {
].every(hash) &&
row.source_hash === row.analysis_sha256 &&
id(row.receipt_id) &&
row.requested_repo_key === REPO &&
row.requested_repo_key === repository &&
row.source_kind === "app_submission" &&
row.aggregate_key === REPO &&
row.aggregate_key === repository &&
row.contract_version === 1 &&
Number.isSafeInteger(row.sequence) &&
row.sequence > 0 &&
Expand Down Expand Up @@ -680,7 +680,7 @@ export function executionIdentity(row, source) {
"projection_terminal_failure",
);
}
export function projectionIdentity(row, source, sha) {
export function projectionIdentity(row, source, sha, repository = REPO) {
if (!row) return null;
if (
Number.isSafeInteger(row.source_version) &&
Expand All @@ -701,7 +701,7 @@ export function projectionIdentity(row, source, sha) {
row.event_source_version === row.source_version &&
row.event_receipt_id === row.receipt_id &&
row.event_contract_version === 1 &&
row.job_repo === REPO &&
row.job_repo === repository &&
typeof row.resolved_commit_sha === "string" &&
/^[a-f0-9]{40}$/.test(row.resolved_commit_sha) &&
["app_submission", "agent_submission", "verified_backfill"].includes(
Expand Down Expand Up @@ -736,7 +736,7 @@ export function projectionIdentity(row, source, sha) {
row.event_receipt_id === source.receiptId &&
row.event_source_hash === source.sourceHash &&
row.event_contract_version === 1 &&
row.job_repo === REPO &&
row.job_repo === repository &&
row.resolved_commit_sha === sha &&
row.source_kind === "app_submission" &&
hash(row.source_hash) &&
Expand Down
Loading