ai-foreman 1.7.29 → 2.0.0

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 (136) hide show
  1. package/README.md +1 -1
  2. package/dist/adapters/claude.d.ts +23 -12
  3. package/dist/adapters/claude.js +227 -23
  4. package/dist/adapters/codex.d.ts +17 -7
  5. package/dist/adapters/codex.js +195 -17
  6. package/dist/adapters/recovering.d.ts +2 -0
  7. package/dist/adapters/recovering.js +5 -1
  8. package/dist/adapters/structured.d.ts +71 -0
  9. package/dist/adapters/structured.js +213 -0
  10. package/dist/adapters/types.d.ts +85 -9
  11. package/dist/agentRun.d.ts +2 -0
  12. package/dist/agentRun.js +2 -1
  13. package/dist/boundedTurnEvidence.d.ts +18 -0
  14. package/dist/boundedTurnEvidence.js +76 -0
  15. package/dist/branch/currentGuard.d.ts +2 -0
  16. package/dist/branch/currentGuard.js +2 -0
  17. package/dist/branch/planner.js +1 -4
  18. package/dist/branch/runner.d.ts +2 -1
  19. package/dist/branch/runner.js +10 -7
  20. package/dist/buildAdmission.js +4 -0
  21. package/dist/buildApproval.d.ts +15 -0
  22. package/dist/buildApproval.js +25 -0
  23. package/dist/buildAssignment.d.ts +1 -0
  24. package/dist/buildAssignment.js +82 -23
  25. package/dist/buildInterventions.d.ts +2 -2
  26. package/dist/buildInterventions.js +11 -4
  27. package/dist/buildLaunchGate.js +3 -2
  28. package/dist/builderGuidanceFollowup.js +17 -6
  29. package/dist/cli/manager.js +22 -20
  30. package/dist/cli/start.js +22 -26
  31. package/dist/cli/tickets.d.ts +0 -7
  32. package/dist/cli/tickets.js +41 -40
  33. package/dist/continuity.d.ts +10 -14
  34. package/dist/continuity.js +198 -179
  35. package/dist/continuityOrigin.d.ts +13 -0
  36. package/dist/continuityOrigin.js +91 -0
  37. package/dist/foreman.d.ts +10 -4
  38. package/dist/foreman.js +51 -193
  39. package/dist/graph/boundary.d.ts +1 -1
  40. package/dist/graph/boundary.js +1 -1
  41. package/dist/graph/host.js +8 -19
  42. package/dist/graph/manager.d.ts +1 -1
  43. package/dist/graph/manager.js +1 -1
  44. package/dist/graph/preparation.d.ts +38 -0
  45. package/dist/graph/preparation.js +7 -6
  46. package/dist/graph/semantic.d.ts +1 -0
  47. package/dist/graph/semantic.js +4 -1
  48. package/dist/graph/session.js +7 -2
  49. package/dist/graph/turn.d.ts +2 -3
  50. package/dist/graph/turn.js +28 -40
  51. package/dist/handoffProtocol.js +2 -2
  52. package/dist/handoffs.d.ts +3 -1
  53. package/dist/handoffs.js +90 -59
  54. package/dist/hostGraphApplicability.d.ts +2 -1
  55. package/dist/hostGraphApplicability.js +17 -19
  56. package/dist/hostQaAugmentation.js +21 -4
  57. package/dist/hostQaComparison.d.ts +21 -2
  58. package/dist/hostQaComparison.js +43 -29
  59. package/dist/hostSourcePreservation.d.ts +38 -0
  60. package/dist/hostSourcePreservation.js +72 -0
  61. package/dist/interviews.d.ts +7 -0
  62. package/dist/interviews.js +18 -1
  63. package/dist/legacyCommunicationDisplay.d.ts +2 -0
  64. package/dist/legacyCommunicationDisplay.js +2 -0
  65. package/dist/managerPacket.js +3 -3
  66. package/dist/markers.d.ts +4 -3
  67. package/dist/markers.js +4 -20
  68. package/dist/probeIpc.d.ts +14 -0
  69. package/dist/probeIpc.js +29 -0
  70. package/dist/qaBuildGate.d.ts +21 -0
  71. package/dist/qaBuildGate.js +47 -12
  72. package/dist/qaBuilderCoverage.js +22 -13
  73. package/dist/qaBuilderEvidence.d.ts +16 -1
  74. package/dist/qaBuilderEvidence.js +48 -8
  75. package/dist/qaContractCoverage.d.ts +2 -2
  76. package/dist/qaContractCoverage.js +4 -9
  77. package/dist/qaContractDelivery.js +34 -20
  78. package/dist/qaCoverageAssembly.d.ts +22 -0
  79. package/dist/qaCoverageAssembly.js +16 -0
  80. package/dist/qaDeliveryJournal.d.ts +2 -0
  81. package/dist/qaDeliveryJournal.js +19 -0
  82. package/dist/qaFailureDelivery.js +97 -85
  83. package/dist/qaHostTransformation.d.ts +26 -0
  84. package/dist/qaHostTransformation.js +100 -0
  85. package/dist/qaPreparation.d.ts +8 -20
  86. package/dist/qaPreparation.js +133 -248
  87. package/dist/qaPreparationInputs.js +19 -0
  88. package/dist/qaPrerequisites.d.ts +2 -1
  89. package/dist/qaPrerequisites.js +21 -1
  90. package/dist/qaProtocolV2.d.ts +15 -0
  91. package/dist/qaRecovery.js +21 -4
  92. package/dist/qaReview.js +220 -395
  93. package/dist/qaReviewEvidence.d.ts +7 -3
  94. package/dist/qaReviewEvidence.js +15 -4
  95. package/dist/qaRuntime.js +1 -1
  96. package/dist/qaSnapshot.js +71 -21
  97. package/dist/qaStructuredResponse.d.ts +10 -0
  98. package/dist/qaStructuredResponse.js +57 -0
  99. package/dist/qaTimeline.js +5 -3
  100. package/dist/qaVerificationContract.d.ts +1 -2
  101. package/dist/qaVerificationContract.js +24 -35
  102. package/dist/readinessGate.js +5 -4
  103. package/dist/runtimeReadiness.js +23 -15
  104. package/dist/statusReporter.d.ts +2 -0
  105. package/dist/statusReporter.js +2 -0
  106. package/dist/structuredCutover.d.ts +31 -0
  107. package/dist/structuredCutover.js +510 -0
  108. package/dist/structuredCutoverFiles.d.ts +10 -0
  109. package/dist/structuredCutoverFiles.js +181 -0
  110. package/dist/structuredDispatchAuthority.d.ts +22 -0
  111. package/dist/structuredDispatchAuthority.js +110 -0
  112. package/dist/structuredDispatchRecord.d.ts +29 -0
  113. package/dist/structuredDispatchRecord.js +36 -0
  114. package/dist/structuredLiveBudget.d.ts +2 -0
  115. package/dist/structuredLiveBudget.js +23 -0
  116. package/dist/structuredOperation.d.ts +18 -0
  117. package/dist/structuredOperation.js +257 -0
  118. package/dist/supervisedStart.js +35 -21
  119. package/dist/ticketPlanning.d.ts +1 -373
  120. package/dist/ticketPlanning.js +2 -26
  121. package/dist/ticketPopulation.d.ts +0 -3
  122. package/dist/ticketPopulation.js +4 -21
  123. package/dist/ticketPopulationPageAuthority.d.ts +13 -0
  124. package/dist/ticketPopulationPageAuthority.js +30 -0
  125. package/dist/ticketPopulationPaging.d.ts +27 -0
  126. package/dist/ticketPopulationPaging.js +105 -0
  127. package/dist/tickets/renderMarkdown.d.ts +14 -0
  128. package/dist/tickets/renderMarkdown.js +39 -5
  129. package/dist/tickets/ticketLoader.js +10 -0
  130. package/dist/tickets/ticketSchema.d.ts +107 -0
  131. package/dist/tickets/ticketSchema.js +4 -1
  132. package/dist/workflowDb.d.ts +100 -2
  133. package/dist/workflowDb.js +763 -39
  134. package/dist/workflowReader.d.ts +1 -0
  135. package/dist/workflowReader.js +9 -0
  136. package/package.json +15 -7
@@ -45,6 +45,25 @@ export function admittedPlanInput(projectDir, ticket) {
45
45
  requirements.push({ id: `req-plan-${fingerprint.slice(0, 24)}-${occurrence}`, inputRef: input.id, locator: `${location}${field}[${index}]`, statement, digest: contractDigest("statement", statement), obligation: "mandatory", origin: "explicit", authority: "approved" });
46
46
  });
47
47
  }
48
+ if (slice.verification_version === 2) {
49
+ for (const requirement of slice.verification_requirements ?? []) {
50
+ const statement = requirement.summary;
51
+ requirements.push({
52
+ id: `req-plan-v2-${slice.slice_ref}-${requirement.id}`,
53
+ inputRef: input.id,
54
+ locator: `slices.${slice.slice_ref}.verification_requirements.${requirement.id}`,
55
+ statement,
56
+ // A RequirementRef's historical digest domain is its statement.
57
+ // Keep that invariant and bind the executable V2 intent separately.
58
+ digest: contractDigest("statement", statement),
59
+ verificationIntentDigest: contractDigest("verification-intent-v2", requirement),
60
+ obligation: "mandatory",
61
+ origin: "explicit",
62
+ authority: "approved",
63
+ verificationIntent: structuredClone(requirement),
64
+ });
65
+ }
66
+ }
48
67
  return { input, plan, requirements };
49
68
  }
50
69
  throw new Error("Exact admitted approved-plan revision unavailable; restore its immutable artifact before preparation");
@@ -6,10 +6,11 @@ export interface QaPrerequisiteEvidence {
6
6
  checkedAt: string;
7
7
  /** Host absence is actionable; host availability never proves provider access. */
8
8
  authority: "host-preflight";
9
+ /** not_applicable: a typed applicability condition is false, so the requirement does not apply (never a missing prerequisite). */
9
10
  checks: Array<{
10
11
  requirement: string;
11
12
  capability: string;
12
- outcome: "available" | "not_run";
13
+ outcome: "available" | "not_run" | "not_applicable";
13
14
  evidence: string;
14
15
  }>;
15
16
  sourceDefects: string[];
@@ -17,12 +17,32 @@ export async function checkQaPrerequisites(input) {
17
17
  catch { /* malformed manifests remain source findings */ }
18
18
  const evidence = {
19
19
  version: 3, snapshotPath: realpathSync(input.snapshotPath), checkedAt: new Date().toISOString(), authority: "host-preflight",
20
- scopeDigest: qaDigest("qa-prerequisites-v3", { sourceDigest: input.sourceDigest, snapshotPath: realpathSync(input.snapshotPath), manifests, node: process.version, path: env.PATH ?? "", runtimeContext: input.runtimeContext ?? null, requirements: input.ticket.required_tests }),
20
+ scopeDigest: qaDigest("qa-prerequisites-v4", { sourceDigest: input.sourceDigest, snapshotPath: realpathSync(input.snapshotPath), manifests, node: process.version, path: env.PATH ?? "", runtimeContext: input.runtimeContext ?? null, requirements: input.ticket.verification_version === 2 ? input.ticket.verification_requirements : input.ticket.required_tests }),
21
21
  checks: [], sourceDefects: [],
22
22
  };
23
23
  const probe = input.probe ?? ((executable, args) => new Promise(resolve => {
24
24
  execFile(executable, args, { cwd: input.snapshotPath, env, timeout: timeoutMs, maxBuffer: 4096, windowsHide: true }, error => resolve(!error));
25
25
  }));
26
+ // V2 never extracts or executes a command from provider prose. Its closed
27
+ // probe vocabulary is resolved by the host and records availability only.
28
+ if (input.ticket.verification_version === 2) {
29
+ for (const requirement of input.ticket.verification_requirements ?? []) {
30
+ if (requirement.applicability.kind !== "host-executable-available")
31
+ continue;
32
+ const executable = resolveExecutablePath(requirement.applicability.executable, env);
33
+ const available = Boolean(executable);
34
+ const expected = requirement.applicability.expected;
35
+ evidence.checks.push({
36
+ requirement: requirement.id,
37
+ capability: `host-probe:executable-available:${requirement.applicability.executable}`,
38
+ outcome: available === expected ? "available" : "not_applicable",
39
+ evidence: `Host registry resolved executable availability=${available}; expected=${expected}; ${available === expected ? "requirement applies" : "requirement does not apply"}; snapshot=${evidence.snapshotPath}`,
40
+ });
41
+ }
42
+ return evidence;
43
+ }
44
+ // V1 compatibility only: historical prose is read conservatively and never
45
+ // promoted to a typed applicability condition.
26
46
  for (const requirement of input.ticket.required_tests) {
27
47
  const command = /^\s*`?(pnpm|npm|yarn|bun|node|docker|python3|pytest|cargo|go)\b/.exec(requirement)?.[1];
28
48
  if (command) {
@@ -73,6 +73,13 @@ export interface QaSessionHandleV2 {
73
73
  acceptedHandoffReceiptDigest?: string;
74
74
  }
75
75
  export interface QaTurnIntentV2 {
76
+ sealedDispatch?: import("./structuredDispatchRecord.js").SealedDispatchRecordV2;
77
+ outputContract?: {
78
+ protocolVersion: 2;
79
+ operation: string;
80
+ schemaVersion: number;
81
+ schemaDigest: string;
82
+ };
76
83
  version: 2;
77
84
  operationId: string;
78
85
  runId: string;
@@ -87,6 +94,14 @@ export interface QaTurnIntentV2 {
87
94
  intendedAt: string;
88
95
  }
89
96
  export interface QaTurnReceiptV2 {
97
+ retainedEvidence?: {
98
+ rawOriginalBytes?: number;
99
+ rawRetainedBytes?: number;
100
+ cleanedOriginalBytes: number;
101
+ cleanedRetainedBytes: number;
102
+ };
103
+ sealedDispatch?: import("./structuredDispatchRecord.js").SealedDispatchRecordV2;
104
+ structuredResponseDigest?: string;
90
105
  version: 2;
91
106
  operationId: string;
92
107
  dispatch: "not-dispatched" | "completed" | "uncertain";
@@ -2,7 +2,7 @@ import { createHash, randomUUID } from "node:crypto";
2
2
  import { execFileSync } from "node:child_process";
3
3
  import { chmodSync, closeSync, cpSync, existsSync, fsyncSync, lstatSync, mkdirSync, openSync, readFileSync, readlinkSync, readdirSync, realpathSync, renameSync, writeFileSync, } from "node:fs";
4
4
  import { basename, dirname, join, relative, resolve, sep } from "node:path";
5
- import { parseProviderContract, parseQaFailureReport, qaRecoveryAcknowledgementV1Schema } from "rafi-spec";
5
+ import { parseProviderContract, parseStrictJson, validateQaFailureReport, QA_FAILURE_REPORT_MAX_BYTES, qaRecoveryAcknowledgementV1Schema } from "rafi-spec";
6
6
  import { calculateFrozenQaStateDigest, captureFrozenQaSource } from "./qaSnapshot.js";
7
7
  import { WorkflowDb } from "./workflowDb.js";
8
8
  export const QA_RECOVERY_ROOT = ".foreman/qa-report-recovery";
@@ -81,6 +81,8 @@ export function canonicalizeQaRecoveryRequirements(packet) {
81
81
  return revise(loaded, { resources });
82
82
  }
83
83
  export function loadQaRecoveryPacket(directory) {
84
+ if (existsSync(join(directory, ".structured-communication-v2-cutover")))
85
+ throw new Error("structured-communication-v2-cutover: this recovery packet cannot resume; start a new native dispatch");
84
86
  const root = resolve(directory);
85
87
  const manifestPath = join(root, "manifest.json");
86
88
  if (!existsSync(manifestPath) || lstatSync(manifestPath).isSymbolicLink())
@@ -455,7 +457,22 @@ export function readManualQaReport(packetDirectory) { return JSON.parse(readFile
455
457
  export function validateManualQaReport(packet) {
456
458
  const path = join(packet.directory, "report.json");
457
459
  const raw = existsSync(path) ? readFileSync(path) : Buffer.alloc(0);
458
- const parsed = parseQaFailureReport(raw.toString());
460
+ let value;
461
+ let errors = [];
462
+ if (raw.length > QA_FAILURE_REPORT_MAX_BYTES)
463
+ errors = [`serialized report exceeds ${QA_FAILURE_REPORT_MAX_BYTES} bytes`];
464
+ else {
465
+ try {
466
+ value = parseStrictJson(raw.toString());
467
+ }
468
+ catch (error) {
469
+ errors = [`invalid JSON: ${String(error)}`];
470
+ }
471
+ if (!errors.length)
472
+ errors = validateQaFailureReport(value).errors;
473
+ }
474
+ const parsed = { report: errors.length ? undefined : value,
475
+ validation: { valid: errors.length === 0, errors } };
459
476
  let next = appendQaRecoveryResource(packet, `validation/manual-repair-r${packet.manifest.revision + 1}.json`, parsed.validation, { purpose: "Validation result for the latest operator-edited report.json" });
460
477
  const editable = writeEditableReport(packet.directory, raw);
461
478
  next = revise(next, { resources: next.manifest.resources.filter((resource) => resource.path !== "report.json").concat(editable).sort(resourceOrder) });
@@ -499,8 +516,8 @@ export function renderQaRecoveryAcknowledgementInstruction(packet, relativePath)
499
516
  `Packet digest: ${packet.manifest.packetDigest}`,
500
517
  `Reviewed-state digest: ${packet.manifest.reviewedStateDigest}`,
501
518
  "Required resources:", required,
502
- "Reply with exactly the following acknowledgement line, followed by the required RAFI_CONTINUITY_DELTA record:",
503
- `RAFI_QA_RECOVERY_ACK packet="${packet.manifest.packetDigest}" reviewed_state="${packet.manifest.reviewedStateDigest}" required_resources_read="all"`,
519
+ "Return the following payload in the native qa-recovery-acceptance response, with bounded continuity in the same object:",
520
+ JSON.stringify({ packet: packet.manifest.packetDigest, reviewed_state: packet.manifest.reviewedStateDigest, required_resources_read: "all" }),
504
521
  ].join("\n");
505
522
  }
506
523
  export function validateQaRecoveryAcknowledgement(text, packet, options = {}) {