@awak-app/simy-cli 0.5.15 → 0.5.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -2
- package/package.json +2 -2
- package/src/agent.js +33 -1
- package/src/cli-contract.js +1 -1
- package/src/codex-plugins/simy/hooks/README.md +17 -0
- package/src/codex-plugins/simy/hooks/connection-reminder.mjs +184 -0
- package/src/codex-plugins/simy/hooks/hooks.json +27 -0
- package/src/codex-plugins/simy/skills/agentic-delivery-loop/SKILL.md +56 -0
- package/src/connection-discovery.js +49 -0
- package/src/orchestrator/audit.js +47 -5
- package/src/orchestrator/contract.js +2 -0
- package/src/orchestrator/evidence.js +64 -5
- package/src/orchestrator/independent-audit.js +10 -0
- package/src/orchestrator/index.js +7 -0
- package/src/orchestrator/instruction.js +21 -1
- package/src/orchestrator/loop.js +3 -0
- package/src/orchestrator/pr-preparation.js +420 -0
- package/src/orchestrator/result.js +6 -0
- package/src/runner.js +92 -1
- package/src/sqm/agent-skill.js +39 -7
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { collectMinimumCompletion } from "./minimum-completion.js";
|
|
2
2
|
import { execFile } from "node:child_process";
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
4
|
+
import { lstat, readFile, realpath } from "node:fs/promises";
|
|
5
|
+
import path from "node:path";
|
|
3
6
|
import { promisify } from "node:util";
|
|
4
7
|
|
|
5
8
|
import { cleanString, stringArray } from "./shared.js";
|
|
6
9
|
|
|
7
10
|
const execFileAsync = promisify(execFile);
|
|
8
|
-
const SUCCESS_CONCLUSIONS = new Set(["SUCCESS", "NEUTRAL"
|
|
11
|
+
const SUCCESS_CONCLUSIONS = new Set(["SUCCESS", "NEUTRAL"]);
|
|
9
12
|
const FAILURE_CONCLUSIONS = new Set([
|
|
10
13
|
"ACTION_REQUIRED",
|
|
11
14
|
"CANCELLED",
|
|
@@ -20,14 +23,25 @@ export async function collectPrEvidence({
|
|
|
20
23
|
repositoryPath,
|
|
21
24
|
runCommand = execFileAsync,
|
|
22
25
|
}) {
|
|
26
|
+
const baseRefresh = await command(
|
|
27
|
+
runCommand,
|
|
28
|
+
"git",
|
|
29
|
+
["fetch", "--no-tags", "origin", charter.base_branch],
|
|
30
|
+
repositoryPath,
|
|
31
|
+
);
|
|
23
32
|
const local = await collectLocalGitEvidence({
|
|
24
33
|
baseBranch: charter.base_branch,
|
|
25
34
|
repositoryPath,
|
|
26
35
|
runCommand,
|
|
27
36
|
});
|
|
37
|
+
local.base_ref_refreshed = baseRefresh.ok;
|
|
38
|
+
local.base_ref_error = baseRefresh.ok ? null : baseRefresh.error;
|
|
28
39
|
const github = await collectGitHubEvidence({ charter, attempt, repositoryPath, runCommand });
|
|
29
40
|
const minimumCompletion = await collectMinimumCompletion({ charter, attempt, repositoryPath, local });
|
|
41
|
+
const prPreparationRefs = await verifyPrPreparationRefs({ attempt, repositoryPath, local, github });
|
|
30
42
|
const latest = await collectLocalGitEvidence({ baseBranch: charter.base_branch, repositoryPath, runCommand });
|
|
43
|
+
latest.base_ref_refreshed = baseRefresh.ok;
|
|
44
|
+
latest.base_ref_error = baseRefresh.ok ? null : baseRefresh.error;
|
|
31
45
|
if (!latest.available || latest.head_sha !== local.head_sha || !latest.working_tree_clean) {
|
|
32
46
|
minimumCompletion.status = "evidence_incomplete";
|
|
33
47
|
minimumCompletion.errors = ["Checkout changed while collecting minimum evidence; verify again."];
|
|
@@ -37,27 +51,69 @@ export async function collectPrEvidence({
|
|
|
37
51
|
collected_at: new Date().toISOString(),
|
|
38
52
|
local: latest,
|
|
39
53
|
github,
|
|
54
|
+
pr_preparation_refs: prPreparationRefs,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async function verifyPrPreparationRefs({ attempt, repositoryPath, local, github }) {
|
|
59
|
+
const refs = [...new Set((attempt.pr_preparation || []).flatMap((row) => row.evidence_refs || []))];
|
|
60
|
+
return Promise.all(refs.map(async (ref) => {
|
|
61
|
+
const observed = observedEvidenceRef(ref, local, github);
|
|
62
|
+
if (observed !== null) return { ref, verified: observed, kind: "observed" };
|
|
63
|
+
try {
|
|
64
|
+
const root = await realpath(repositoryPath);
|
|
65
|
+
const target = await realpath(path.resolve(root, ref));
|
|
66
|
+
const relative = path.relative(root, target);
|
|
67
|
+
const details = await lstat(target);
|
|
68
|
+
const verified = !relative.startsWith("..") && !path.isAbsolute(relative) &&
|
|
69
|
+
details.isFile() && details.size <= 50 * 1024 * 1024;
|
|
70
|
+
const sha256 = verified
|
|
71
|
+
? createHash("sha256").update(await readFile(target)).digest("hex")
|
|
72
|
+
: null;
|
|
73
|
+
return { ref, verified, kind: "local_file", sha256 };
|
|
74
|
+
} catch {
|
|
75
|
+
return { ref, verified: false, kind: "unverified" };
|
|
76
|
+
}
|
|
77
|
+
}));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function observedEvidenceRef(ref, local, github) {
|
|
81
|
+
const sources = {
|
|
82
|
+
"observed:local.head_sha": local.head_sha,
|
|
83
|
+
"observed:local.base_sha": local.base_sha,
|
|
84
|
+
"observed:local.base_is_ancestor": local.base_is_ancestor === true,
|
|
85
|
+
"observed:github.head_sha": github.head_sha,
|
|
86
|
+
"observed:github.base_sha": github.base_sha,
|
|
87
|
+
"observed:github.checks": Array.isArray(github.checks) && github.checks.length > 0,
|
|
40
88
|
};
|
|
89
|
+
return Object.hasOwn(sources, ref) ? Boolean(sources[ref]) : null;
|
|
41
90
|
}
|
|
42
91
|
|
|
43
92
|
async function collectLocalGitEvidence({ baseBranch, repositoryPath, runCommand }) {
|
|
44
|
-
const
|
|
93
|
+
const baseRef = `origin/${baseBranch}`;
|
|
94
|
+
const [head, branch, headline, status, changed, base, mergeBase, baseAncestor] = await Promise.all([
|
|
45
95
|
command(runCommand, "git", ["rev-parse", "HEAD"], repositoryPath),
|
|
46
96
|
command(runCommand, "git", ["branch", "--show-current"], repositoryPath),
|
|
47
97
|
command(runCommand, "git", ["log", "-1", "--pretty=%s"], repositoryPath),
|
|
48
98
|
command(runCommand, "git", ["status", "--porcelain"], repositoryPath),
|
|
49
99
|
changedFiles(runCommand, baseBranch, repositoryPath),
|
|
100
|
+
command(runCommand, "git", ["rev-parse", baseRef], repositoryPath),
|
|
101
|
+
command(runCommand, "git", ["merge-base", baseRef, "HEAD"], repositoryPath),
|
|
102
|
+
command(runCommand, "git", ["merge-base", "--is-ancestor", baseRef, "HEAD"], repositoryPath),
|
|
50
103
|
]);
|
|
51
104
|
const statusLines = lines(status.stdout);
|
|
52
105
|
return {
|
|
53
|
-
available: head.ok && branch.ok && headline.ok && status.ok && changed.ok,
|
|
106
|
+
available: head.ok && branch.ok && headline.ok && status.ok && changed.ok && base.ok && mergeBase.ok,
|
|
54
107
|
head_sha: cleanString(head.stdout) || null,
|
|
55
108
|
branch_name: cleanString(branch.stdout) || null,
|
|
56
109
|
commit_headline: cleanString(headline.stdout) || null,
|
|
57
110
|
working_tree_clean: status.ok && statusLines.length === 0,
|
|
58
111
|
status_lines: statusLines,
|
|
59
112
|
changed_files: lines(changed.stdout),
|
|
60
|
-
|
|
113
|
+
base_sha: cleanString(base.stdout) || null,
|
|
114
|
+
merge_base_sha: cleanString(mergeBase.stdout) || null,
|
|
115
|
+
base_is_ancestor: baseAncestor.ok,
|
|
116
|
+
errors: [head, branch, headline, status, changed, base, mergeBase]
|
|
61
117
|
.filter((item) => !item.ok)
|
|
62
118
|
.map((item) => item.error),
|
|
63
119
|
};
|
|
@@ -93,6 +149,7 @@ async function collectGitHubEvidence({ charter, attempt, repositoryPath, runComm
|
|
|
93
149
|
"headRepository",
|
|
94
150
|
"headRepositoryOwner",
|
|
95
151
|
"baseRefName",
|
|
152
|
+
"baseRefOid",
|
|
96
153
|
"reviews",
|
|
97
154
|
"statusCheckRollup",
|
|
98
155
|
].join(",");
|
|
@@ -137,10 +194,11 @@ async function collectGitHubEvidence({ charter, attempt, repositoryPath, runComm
|
|
|
137
194
|
? `${cleanString(payload.headRepositoryOwner.login)}/${cleanString(payload.headRepository.name)}`
|
|
138
195
|
: null,
|
|
139
196
|
base_branch: cleanString(payload.baseRefName) || null,
|
|
197
|
+
base_sha: cleanString(payload.baseRefOid) || null,
|
|
140
198
|
checks,
|
|
141
199
|
checks_present: checks.length > 0,
|
|
142
200
|
checks_passing:
|
|
143
|
-
checks.length > 0 && checks.every((item) => item.state
|
|
201
|
+
checks.length > 0 && checks.every((item) => ["success", "skipped"].includes(item.state)),
|
|
144
202
|
required_checks_present: requiredChecks.every((name) => checkNames.has(name)),
|
|
145
203
|
error: null,
|
|
146
204
|
};
|
|
@@ -154,6 +212,7 @@ function normalizeChecks(value) {
|
|
|
154
212
|
const conclusion = cleanString(item?.conclusion || item?.state).toUpperCase();
|
|
155
213
|
let state = "pending";
|
|
156
214
|
if (SUCCESS_CONCLUSIONS.has(conclusion)) state = "success";
|
|
215
|
+
if (conclusion === "SKIPPED") state = "skipped";
|
|
157
216
|
if (FAILURE_CONCLUSIONS.has(conclusion)) state = "failure";
|
|
158
217
|
if (status === "COMPLETED" && !conclusion) state = "failure";
|
|
159
218
|
return { name, status: status || null, conclusion: conclusion || null, state };
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
normalizeCriterionEvidence,
|
|
6
6
|
normalizeVisualReview,
|
|
7
7
|
} from "./completion-contract.js";
|
|
8
|
+
import { independentAuditEvidenceIdentity } from "./pr-preparation.js";
|
|
8
9
|
|
|
9
10
|
export function buildIndependentAuditInstruction(charter, attempt) {
|
|
10
11
|
const localEvidence = attempt.observed_evidence?.local || {};
|
|
@@ -47,9 +48,14 @@ export function buildIndependentAuditInstruction(charter, attempt) {
|
|
|
47
48
|
`Executor criterion evidence: ${JSON.stringify(attempt.criterion_evidence || [])}`,
|
|
48
49
|
`Minimum evidence matrix: ${attempt.observed_evidence?.minimum_completion?.evidence_path || "missing"}`,
|
|
49
50
|
`Minimum evidence digest: ${attempt.observed_evidence?.minimum_completion?.matrix_sha256 || "missing"}`,
|
|
51
|
+
`Complete audit evidence identity: ${independentAuditEvidenceIdentity(attempt)}`,
|
|
50
52
|
`Executor visual review: ${JSON.stringify(attempt.visual_review || {})}`,
|
|
53
|
+
`Executor PR preparation evidence: ${JSON.stringify(attempt.pr_preparation || [])}`,
|
|
54
|
+
`Executor skipped-check disclosures: ${JSON.stringify(attempt.skipped_checks || [])}`,
|
|
51
55
|
`Observed local branch: ${localEvidence.branch_name || "unknown"}`,
|
|
52
56
|
`Observed local HEAD: ${localEvidence.head_sha || "unknown"}`,
|
|
57
|
+
`Observed fetched base SHA: ${localEvidence.base_sha || "unknown"}`,
|
|
58
|
+
`Observed base ancestry: ${localEvidence.base_is_ancestor === true ? "integrated" : "not integrated or unknown"}`,
|
|
53
59
|
`Observed changed files:\n${bullets(localEvidence.changed_files || [])}`,
|
|
54
60
|
`Observed PR: ${githubEvidence.url || attempt.pr_url || "unknown"}`,
|
|
55
61
|
`Observed passing GitHub checks:\n${bullets(passingChecks)}`,
|
|
@@ -63,6 +69,10 @@ export function buildIndependentAuditInstruction(charter, attempt) {
|
|
|
63
69
|
"Inspect the final diff and related code directly from the repository.",
|
|
64
70
|
"Look for unmet acceptance criteria, regressions, security issues, data and concurrency risks, missing tests, and unrelated changes.",
|
|
65
71
|
"Treat executor claims as untrusted until supported by repository evidence.",
|
|
72
|
+
"Inspect every structured PR preparation row. Confirm required rows are bound to the current head and raw evidence; confirm not-applicable rows have a genuine boundary reason and authoritative source. Check exact companion/manifest revisions, scenario outcomes, cleanup residual count, telemetry redaction, locale/browser evidence, and residual-work disclosure where applicable.",
|
|
73
|
+
"For conditional safeguards, inspect the actual changed boundary: checklist rendering must resist H1-H6, leading-space, and trailing-hash lookalikes while keeping one official controller table; release work must derive its target from authoritative topology and define exact-revision observation; live verification must declare its write policy and prove residual test data zero; loop-state/rendering changes must preserve artifacts and recompute derived views across normal/checkpoint and supported paid resumes.",
|
|
74
|
+
"Confirm the remote base was freshly read, is an ancestor of the final head, and matches the PR base SHA. Treat DIRTY/BEHIND, stale companion SHAs, changed manifests, or conflict resolution without rerun evidence as blockers.",
|
|
75
|
+
"Inspect skipped GitHub checks independently. A documented skip may be legitimate, but it must not be represented as an executed full/live/E2E result.",
|
|
66
76
|
"Compare the original request and frozen experience to the complete stable criterion-ID list and implementation mapping. Report forgotten features, dead controls, unsaved results and broken integration. Open the raw minimum evidence, verify its oracle and actual runtime observations; matching expected/actual JSON and hashes alone do not prove truth. Check reload/session return and persisted values/ownership when saving is required.",
|
|
67
77
|
"For every stable criterion ID, return a criterion_results entry only after directly checking its cited proof and observed outcome.",
|
|
68
78
|
"For production-relevant criteria, verify that each outcome and guardrail measure names its signal, independent source, observation window, threshold, decision, owner, and criterion ID.",
|
|
@@ -4,3 +4,10 @@ export { buildIndependentAuditInstruction } from "./independent-audit.js";
|
|
|
4
4
|
export { recheckPrReadiness, runCodingLoop } from "./loop.js";
|
|
5
5
|
export { mergeTokenUsage, parseStructuredMarker, parseStructuredResult } from "./result.js";
|
|
6
6
|
export { buildRetryFingerprint, evaluateRetryCircuit } from "./retry.js";
|
|
7
|
+
export {
|
|
8
|
+
PR_PREPARATION_CHECKS,
|
|
9
|
+
defaultPrPreparationContract,
|
|
10
|
+
evaluatePrPreparation,
|
|
11
|
+
independentAuditEvidenceIdentity,
|
|
12
|
+
skippedChecksDisclosure,
|
|
13
|
+
} from "./pr-preparation.js";
|
|
@@ -2,6 +2,7 @@ import { minimumCharter } from "../codex-plugins/simy/skills/agentic-delivery-lo
|
|
|
2
2
|
import { bullets, looksLikeUiTask, section } from "./shared.js";
|
|
3
3
|
import { problemSolvingResultContract } from "./problem-solving.js";
|
|
4
4
|
import { acceptanceCriterionRows } from "./completion-contract.js";
|
|
5
|
+
import { PR_PREPARATION_CHECKS } from "./pr-preparation.js";
|
|
5
6
|
|
|
6
7
|
export function buildCodingInstruction(
|
|
7
8
|
charter,
|
|
@@ -72,6 +73,24 @@ export function buildCodingInstruction(
|
|
|
72
73
|
`Context: run_id=${charter.id}, stage=locally_verified, environment=local, fixture_id=${charter.id}; input_sha256 is contextDigest(spec.source); source_revisions maps ${charter.repository} to current full HEAD.`,
|
|
73
74
|
].join("\n")),
|
|
74
75
|
section("expected_tests", bullets(charter.expected_tests)),
|
|
76
|
+
section("pr_preparation_contract", [
|
|
77
|
+
"Before reporting the PR ready, complete every preparation row below against the final committed HEAD. Do not reuse evidence after the head, fetched base, companion revision, manifest, policy, or relevant environment changes.",
|
|
78
|
+
...PR_PREPARATION_CHECKS.map((row) => `- ${row.id}: ${row.label}`),
|
|
79
|
+
"Return one pr_preparation entry per ID. A required row needs passed=true, a concrete observation, evidence_refs that resolve to a checked repository file or an observed:* controller fact, and the current full commit SHA in bound_revisions.",
|
|
80
|
+
"Use applicability=not_applicable only when the changed boundary genuinely excludes the check; include reason, authoritative_source, verified evidence_refs, and the current full commit SHA. Missing tools, failed checks, time, or lack of evidence are pending/failed, not not-applicable.",
|
|
81
|
+
"exact_revision_integration requires companion_revisions entries with repository/full 40-character sha/evidence_ref and/or manifest_revisions entries with manifest/SHA-256/evidence_ref, or a verified N/A boundary when neither exists. e2e_outcome_and_cleanup requires cleanup_residual_count=0.",
|
|
82
|
+
"security_and_privacy requires security_privacy_checks. ui_and_i18n requires browser_artifacts, locales_checked, console_error_count=0, and network_failure_count=0, or a verified N/A boundary for non-UI work.",
|
|
83
|
+
"The final four rows are conditional safeguards. Mark them not_applicable only with current-head-bound verified boundary evidence, never merely because verification is inconvenient.",
|
|
84
|
+
"official_checklist_ownership applies when checklist/chat rendering changes: verify heading levels 1-6, leading spaces, trailing hashes, exactly one controller-owned official checklist, and an explicit unverified label for model-generated lookalikes.",
|
|
85
|
+
"deployment_target_mapping applies when release or live verification is in scope: report deployment_target, a verified deployment_target_source evidence ref, and revision_observation_method. Derive the environment from authoritative topology instead of assuming app-dev or production.",
|
|
86
|
+
"live_data_safety applies to shared/live verification: report live_write_policy as no_persistence, scoped_fixture, or authorized_production_write and cleanup_residual_count=0. Do not infer production-write authority from verification scope.",
|
|
87
|
+
"resume_compatibility applies when Delivery Loop state, derived views, or rendering changes: exercise normal and checkpoint resume; report paid_resume_support as supported or not_supported with a verified paid_resume_support_source evidence ref, exercise paid resume when supported, and prove derived_views_recomputed and artifact_content_preserved.",
|
|
88
|
+
"For current_base_and_head, fetch the remote base immediately before final verification, integrate it, and rerun invalidated checks after conflict resolution.",
|
|
89
|
+
"For current_ci_and_skips, verify the published current HEAD. Return every skipped GitHub check in skipped_checks with name, reason, and impact; a skipped full/live job must not be described as executed E2E.",
|
|
90
|
+
"For exact_revision_integration, pin every companion repository/manifest SHA and exercise the combined path. For e2e_outcome_and_cleanup, inspect expected scenario states and prove residual fixture count is zero; a green job with failed cleanup is not passing evidence.",
|
|
91
|
+
"For security_and_privacy, verify changed trust boundaries and that arbitrary messages, email addresses, URLs, identifiers, credentials, or user data are not retained in logs, breadcrumbs, or telemetry. For UI changes, exercise the actual locale/browser, layout overflow, console/network, and screenshot evidence.",
|
|
92
|
+
"Disclose remaining work, intentional skips, uncertainty, and post-merge migration/deploy/monitoring steps without calling them complete.",
|
|
93
|
+
].join("\n")),
|
|
75
94
|
section("must_not", bullets(charter.must_not)),
|
|
76
95
|
section(
|
|
77
96
|
"active_request_reconciliation",
|
|
@@ -161,11 +180,12 @@ export function buildCodingInstruction(
|
|
|
161
180
|
"result_contract",
|
|
162
181
|
[
|
|
163
182
|
"Finish with exactly one line beginning SIMY_RESULT_JSON: followed by one JSON object.",
|
|
164
|
-
"Required keys: outcome_kind, summary, branch_name, commit_sha, commit_message_headline, pr_url, pr_number, pr_title, pr_base_branch, tests_run, tests_passed, ui_evidence_path, unrelated_changes_detected, secret_scan_passed, changed_files, residual_risks, problem_solving, uncertainty_verification, criterion_evidence, visual_review, delivery_evidence_path.",
|
|
183
|
+
"Required keys: outcome_kind, summary, branch_name, commit_sha, commit_message_headline, pr_url, pr_number, pr_title, pr_base_branch, tests_run, tests_passed, ui_evidence_path, unrelated_changes_detected, secret_scan_passed, changed_files, residual_risks, problem_solving, uncertainty_verification, criterion_evidence, visual_review, delivery_evidence_path, pr_preparation, skipped_checks.",
|
|
165
184
|
problemSolvingResultContract(),
|
|
166
185
|
"uncertainty_verification must contain top_uncertainty, first_verification, evidence_checked, observed_result, resolved, and fallback. Only set resolved=true when concrete observed evidence resolves it.",
|
|
167
186
|
"criterion_evidence must contain one entry for every stable criterion ID with criterion_id, passed, verification_method, evidence_refs, and observed_result. Only set passed=true when the evidence directly proves that criterion.",
|
|
168
187
|
"For UI or browser-extension work, visual_review must contain inspected=true, the local screenshot or video paths in artifacts, and concrete acceptance_observations from viewing the actual content. Capturing a file without viewing it does not count.",
|
|
188
|
+
"pr_preparation must contain the eight stable preparation IDs and four conditional safeguard IDs. skipped_checks must contain name, reason, and impact for every GitHub check that intentionally skipped.",
|
|
169
189
|
"Use null for unavailable scalar values and [] for unavailable arrays. Do not claim evidence that was not observed.",
|
|
170
190
|
].join("\n"),
|
|
171
191
|
),
|
package/src/orchestrator/loop.js
CHANGED
|
@@ -9,6 +9,7 @@ import { canStartProvider, refreshBudgetState } from "./budget.js";
|
|
|
9
9
|
import { buildAttempt, mergeTokenUsage } from "./result.js";
|
|
10
10
|
import { evaluateRetryCircuit } from "./retry.js";
|
|
11
11
|
import { appendEvent, publish } from "./shared.js";
|
|
12
|
+
import { independentAuditEvidenceIdentity } from "./pr-preparation.js";
|
|
12
13
|
|
|
13
14
|
async function runCodingLoopOwned({
|
|
14
15
|
snapshot,
|
|
@@ -212,6 +213,7 @@ async function runCodingLoopOwned({
|
|
|
212
213
|
if (shouldStop()) return stopCodingLoop(snapshot, onUpdate);
|
|
213
214
|
const auditInstruction = buildIndependentAuditInstruction(snapshot.charter, attempt);
|
|
214
215
|
const reviewedMatrix = attempt.observed_evidence?.minimum_completion?.matrix_sha256;
|
|
216
|
+
const reviewedEvidenceIdentity = independentAuditEvidenceIdentity(attempt);
|
|
215
217
|
const auditExecution = await safeExecution(() =>
|
|
216
218
|
executeIndependentAudit({
|
|
217
219
|
attemptNumber,
|
|
@@ -231,6 +233,7 @@ async function runCodingLoopOwned({
|
|
|
231
233
|
attempt.budget = refreshBudgetState(snapshot);
|
|
232
234
|
|
|
233
235
|
attempt.independent_audit.minimum_matrix_sha256 = reviewedMatrix;
|
|
236
|
+
attempt.independent_audit.evidence_identity_sha256 = reviewedEvidenceIdentity;
|
|
234
237
|
|
|
235
238
|
// Re-collect after the read-only auditor to catch any mutated HEAD or working tree.
|
|
236
239
|
attempt.observed_evidence = await safeEvidence(collectEvidence, attempt);
|