@awak-app/simy-cli 0.5.8 → 0.5.11

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.
Files changed (32) hide show
  1. package/README.md +64 -0
  2. package/package.json +7 -3
  3. package/src/agent.js +70 -9
  4. package/src/auto-update.js +312 -35
  5. package/src/browser.js +24 -7
  6. package/src/cli-contract.js +1 -1
  7. package/src/codex-plugins/simy/skills/agentic-delivery-loop/SKILL.md +227 -18
  8. package/src/codex-plugins/simy/skills/agentic-delivery-loop/references/acceptance-evidence.md +131 -0
  9. package/src/codex-plugins/simy/skills/agentic-delivery-loop/references/change-boundary-matrix.md +20 -1
  10. package/src/codex-plugins/simy/skills/agentic-delivery-loop/references/cross-repository-control.md +76 -0
  11. package/src/codex-plugins/simy/skills/agentic-delivery-loop/references/delivery-levels.md +80 -0
  12. package/src/codex-plugins/simy/skills/agentic-delivery-loop/references/minimum-completion.md +137 -0
  13. package/src/codex-plugins/simy/skills/agentic-delivery-loop/references/shared-system-specifications.md +138 -0
  14. package/src/codex-plugins/simy/skills/agentic-delivery-loop/references/state-and-receipts.md +18 -0
  15. package/src/codex-plugins/simy/skills/agentic-delivery-loop/scripts/check-acceptance.mjs +193 -0
  16. package/src/codex-plugins/simy/skills/agentic-delivery-loop/scripts/check-minimum-completion.mjs +221 -0
  17. package/src/console/app.js +21 -2
  18. package/src/console/commands.js +3 -0
  19. package/src/desktop-host.js +220 -0
  20. package/src/desktop-process-lock.js +231 -0
  21. package/src/desktop-status.js +263 -0
  22. package/src/direct-executor.js +6 -0
  23. package/src/index.js +37 -11
  24. package/src/install-update.js +494 -0
  25. package/src/orchestrator/audit.js +17 -2
  26. package/src/orchestrator/completion-contract.js +1 -1
  27. package/src/orchestrator/evidence.js +9 -1
  28. package/src/orchestrator/independent-audit.js +13 -1
  29. package/src/orchestrator/instruction.js +13 -1
  30. package/src/orchestrator/loop.js +34 -4
  31. package/src/orchestrator/minimum-completion.js +108 -0
  32. package/src/orchestrator/result.js +7 -0
@@ -1,3 +1,4 @@
1
+ import { minimumCharter } from "../codex-plugins/simy/skills/agentic-delivery-loop/scripts/check-minimum-completion.mjs";
1
2
  import { auditAttempt, evaluatePrReadiness } from "./audit.js";
2
3
  import {
3
4
  buildIndependentAudit,
@@ -9,7 +10,7 @@ import { buildAttempt, mergeTokenUsage } from "./result.js";
9
10
  import { evaluateRetryCircuit } from "./retry.js";
10
11
  import { appendEvent, publish } from "./shared.js";
11
12
 
12
- export async function runCodingLoop({
13
+ async function runCodingLoopOwned({
13
14
  snapshot,
14
15
  executeAttempt,
15
16
  executeIndependentAudit,
@@ -21,6 +22,19 @@ export async function runCodingLoop({
21
22
  shouldStop = () => false,
22
23
  consumeHumanGuidance = () => "",
23
24
  }) {
25
+ if (!resume && snapshot.attempts.length > 0) {
26
+ return recheckPrReadinessOwned({ snapshot, collectEvidence, checkSqm, onUpdate });
27
+ }
28
+ snapshot.implementation_gate_passed = false;
29
+ snapshot.done_gate_passed = false;
30
+ try { minimumCharter(snapshot.charter); } catch {
31
+ snapshot.final_audit = humanGate("Completion charter requires refresh before verification.", [{
32
+ code: "MINIMUM_CHARTER_REQUIRED", severity: "blocker", target: "completion_contract",
33
+ explanation: "Missing or unsupported completion charter. Preserve history, re-charter and collect current minimum evidence.", repairability: "manual",
34
+ }]);
35
+ await publish(snapshot, "waiting_human", onUpdate);
36
+ return snapshot;
37
+ }
24
38
  if (!resume || snapshot.attempts.length === 0) {
25
39
  appendEvent(snapshot, "risk_classifying", "PR risk classification completed.", {
26
40
  risk: snapshot.charter.risk,
@@ -197,6 +211,7 @@ export async function runCodingLoop({
197
211
  await publish(snapshot, "independent_auditing", onUpdate);
198
212
  if (shouldStop()) return stopCodingLoop(snapshot, onUpdate);
199
213
  const auditInstruction = buildIndependentAuditInstruction(snapshot.charter, attempt);
214
+ const reviewedMatrix = attempt.observed_evidence?.minimum_completion?.matrix_sha256;
200
215
  const auditExecution = await safeExecution(() =>
201
216
  executeIndependentAudit({
202
217
  attemptNumber,
@@ -215,6 +230,8 @@ export async function runCodingLoop({
215
230
  attempt.token_usage = mergeTokenUsage(attempt.token_usage, auditExecution.tokenUsage);
216
231
  attempt.budget = refreshBudgetState(snapshot);
217
232
 
233
+ attempt.independent_audit.minimum_matrix_sha256 = reviewedMatrix;
234
+
218
235
  // Re-collect after the read-only auditor to catch any mutated HEAD or working tree.
219
236
  attempt.observed_evidence = await safeEvidence(collectEvidence, attempt);
220
237
  attempt.sqm = await safeSqm(checkSqm, attempt, attempt.sqm);
@@ -276,9 +293,11 @@ async function stopCodingLoop(snapshot, onUpdate) {
276
293
  return snapshot;
277
294
  }
278
295
 
279
- export async function recheckPrReadiness({ snapshot, collectEvidence, checkSqm = null, onUpdate }) {
296
+ async function recheckPrReadinessOwned({ snapshot, collectEvidence, checkSqm = null, onUpdate }) {
280
297
  const attempt = snapshot.attempts.at(-1);
281
- if (!attempt || !attempt.audit?.passed || !attempt.independent_audit?.passed) return snapshot;
298
+ snapshot.implementation_gate_passed = false;
299
+ snapshot.done_gate_passed = false;
300
+ if (!attempt) return snapshot;
282
301
  appendEvent(snapshot, "checking_pr", "Refreshing GitHub review and CI evidence.", {
283
302
  attempt_number: attempt.attempt_number,
284
303
  });
@@ -296,7 +315,7 @@ export async function recheckPrReadiness({ snapshot, collectEvidence, checkSqm =
296
315
  snapshot.final_audit = aggregateAudit(
297
316
  attempt,
298
317
  attempt.pr_readiness.ready,
299
- attempt.independent_audit.findings || [],
318
+ attempt.independent_audit?.findings || [],
300
319
  );
301
320
  appendEvent(snapshot, attempt.pr_readiness.state, attempt.pr_readiness.summary, {
302
321
  pr_url: attempt.pr_url,
@@ -583,3 +602,14 @@ function humanGate(summary, findings) {
583
602
  release_gate: null,
584
603
  };
585
604
  }
605
+
606
+ const activeControllers = new Set();
607
+ async function withController(options, action) {
608
+ const owner = options.snapshot?.id;
609
+ if (typeof owner !== "string" || !owner) throw new Error("Delivery snapshot ID required");
610
+ if (activeControllers.has(owner)) throw new Error("Delivery controller is already active for this snapshot");
611
+ activeControllers.add(owner);
612
+ try { return await action(options); } finally { activeControllers.delete(owner); }
613
+ }
614
+ export const runCodingLoop = options => withController(options, runCodingLoopOwned);
615
+ export const recheckPrReadiness = options => withController(options, recheckPrReadinessOwned);
@@ -0,0 +1,108 @@
1
+ import path from "node:path";
2
+ import { lstat, realpath } from "node:fs/promises";
3
+ import {
4
+ minimumCharter,
5
+ minimumContext,
6
+ checkMinimumCompletion,
7
+ loadMinimumJson,
8
+ } from "../codex-plugins/simy/skills/agentic-delivery-loop/scripts/check-minimum-completion.mjs";
9
+ import {
10
+ contextDigest,
11
+ sha256,
12
+ } from "../codex-plugins/simy/skills/agentic-delivery-loop/scripts/check-acceptance.mjs";
13
+
14
+ export function minimumCharterDigest(charter) {
15
+ return sha256(Buffer.from(JSON.stringify(minimumCharter(charter))));
16
+ }
17
+
18
+ export async function collectMinimumCompletion({
19
+ charter,
20
+ attempt,
21
+ repositoryPath,
22
+ local,
23
+ }) {
24
+ try {
25
+ if (!local.available || !local.working_tree_clean)
26
+ throw new Error("Clean current checkout required");
27
+ const root = await realpath(
28
+ charter.ui_evidence_root || path.join(repositoryPath, ".artifacts"),
29
+ );
30
+ const requested = attempt.raw_output?.delivery_evidence_path;
31
+ if (typeof requested !== "string") throw new Error("Matrix path missing");
32
+ const file = path.resolve(root, requested);
33
+ const relative = path.relative(root, file);
34
+ if (!relative || relative.startsWith("..") || path.isAbsolute(relative))
35
+ throw new Error("Outside evidence root");
36
+ let current = root;
37
+ for (const segment of relative.split(path.sep)) {
38
+ current = path.join(current, segment);
39
+ if ((await lstat(current)).isSymbolicLink())
40
+ throw new Error("Symlink evidence path");
41
+ }
42
+ const matrix = await loadMinimumJson(file);
43
+ const report = await checkMinimumCompletion({
44
+ charter,
45
+ matrix,
46
+ context: minimumContext(charter, local.head_sha),
47
+ evidenceRoot: path.dirname(file),
48
+ });
49
+ if (report.status !== "evidence_complete") return report;
50
+ // Check mapping paths without reading source contents or following links outside the checkout.
51
+ const repo = await realpath(repositoryPath);
52
+ for (const row of matrix.results) {
53
+ for (const ref of row.implementation_refs || []) {
54
+ if (typeof ref !== "string") continue;
55
+ const target = await realpath(path.resolve(repo, ref));
56
+ const rel = path.relative(repo, target);
57
+ if (
58
+ rel.startsWith("..") ||
59
+ path.isAbsolute(rel) ||
60
+ !(await lstat(target)).isFile()
61
+ )
62
+ throw new Error("Invalid implementation mapping");
63
+ }
64
+ }
65
+ if (contextDigest(await loadMinimumJson(file)) !== report.matrix_sha256)
66
+ throw new Error("Matrix changed during verification");
67
+ return { ...report, evidence_path: file };
68
+ } catch {
69
+ return {
70
+ status: "evidence_incomplete",
71
+ errors: [
72
+ "Minimum completion evidence missing, invalid or stale; verify the actual operation, result, postcondition and integration for every AC-N.",
73
+ ],
74
+ };
75
+ }
76
+ }
77
+
78
+ export function minimumCompletionCheck(charter, attempt) {
79
+ const report = attempt.observed_evidence?.minimum_completion;
80
+ const head = attempt.observed_evidence?.local?.head_sha;
81
+ let passed = false;
82
+ try {
83
+ passed =
84
+ report?.minimum_completion_version === 1 &&
85
+ report.status === "evidence_complete" &&
86
+ report.errors?.length === 0 &&
87
+ report.charter_sha256 === minimumCharterDigest(charter) &&
88
+ report.context_sha256 === contextDigest(minimumContext(charter, head)) &&
89
+ /^[a-f0-9]{64}$/.test(report.matrix_sha256) &&
90
+ report.head_sha === head &&
91
+ attempt.observed_evidence.local.working_tree_clean === true;
92
+ } catch {
93
+ /* Unsupported/missing charters never waive this gate. */
94
+ }
95
+ return {
96
+ id: "minimum_completion",
97
+ label: "Every requested outcome has current operation and system evidence",
98
+ passed: Boolean(passed),
99
+ detail: passed
100
+ ? "Current minimum evidence verified; independent coverage/oracle review and remaining gates still apply."
101
+ : (
102
+ report?.errors || [
103
+ "Minimum completion evidence has not been verified.",
104
+ ]
105
+ ).join("; "),
106
+ evidence_refs: report?.evidence_path ? [report.evidence_path] : [],
107
+ };
108
+ }
@@ -1,3 +1,6 @@
1
+ import { minimumCharterDigest } from "./minimum-completion.js";
2
+ import { minimumContext } from "../codex-plugins/simy/skills/agentic-delivery-loop/scripts/check-minimum-completion.mjs";
3
+ import { contextDigest } from "../codex-plugins/simy/skills/agentic-delivery-loop/scripts/check-acceptance.mjs";
1
4
  import { cleanString, stringArray } from "./shared.js";
2
5
  import { normalizeProblemSolving } from "./problem-solving.js";
3
6
  import {
@@ -15,6 +18,8 @@ export function buildAttempt({ attemptNumber, charter, instruction, promptInterv
15
18
  const completed = execution.exitCode === 0 && !execution.error;
16
19
  const now = new Date().toISOString();
17
20
  return {
21
+ minimum_charter_sha256: safeDigest(() => minimumCharterDigest(charter)),
22
+ minimum_context_sha256: safeDigest(() => contextDigest(minimumContext(charter, cleanString(result.commit_sha)))),
18
23
  attempt_number: attemptNumber,
19
24
  backend: charter.backend,
20
25
  base_branch: charter.base_branch,
@@ -112,3 +117,5 @@ function buildArtifactPreviews(result) {
112
117
  function isUsageRecord(value) {
113
118
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
114
119
  }
120
+
121
+ function safeDigest(compute) { try { return compute(); } catch { return null; } }