@williambeto/ai-workflow 2.4.9 → 2.6.4

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 (103) hide show
  1. package/CHANGELOG.md +1138 -168
  2. package/README.md +203 -77
  3. package/bin/ai-workflow.d.ts +3 -0
  4. package/bin/ai-workflow.js +2669 -1
  5. package/bin/ai-workflow.js.map +1 -0
  6. package/chunk-2AOV2ATY.js +749 -0
  7. package/chunk-5WRI5ZAA.js +31 -0
  8. package/chunk-6EMG6FAO.js +734 -0
  9. package/chunk-UHLZX2EG.js +1827 -0
  10. package/core/index.d.ts +440 -0
  11. package/core/index.js +32 -0
  12. package/core/index.js.map +1 -0
  13. package/dist-assets/AGENTS.md +23 -6
  14. package/dist-assets/agents/atlas.md +8 -8
  15. package/dist-assets/agents/nexus.md +4 -4
  16. package/dist-assets/agents/orion.md +1 -1
  17. package/dist-assets/agents/phoenix.md +1 -1
  18. package/dist-assets/agents/sage.md +1 -1
  19. package/dist-assets/commands/implement.md +1 -1
  20. package/dist-assets/docs/QUICKSTART.md +8 -8
  21. package/dist-assets/docs/cli-reference.md +7 -7
  22. package/dist-assets/docs/compatibility/provider-usage.md +1 -9
  23. package/dist-assets/docs/compatibility/runtime-matrix.md +5 -6
  24. package/dist-assets/docs/consumer-onboarding.md +2 -2
  25. package/dist-assets/docs/design-patterns-policy.md +1 -1
  26. package/dist-assets/docs/npm-consumer-quickstart.md +70 -109
  27. package/dist-assets/docs/policies/01-BRANCH_GATE.md +1 -1
  28. package/dist-assets/docs/policies/06-FINAL_EVIDENCE_CONTRACT.md +2 -2
  29. package/dist-assets/docs/policies/08-PRODUCT_TRUTHFULNESS_AND_PROJECT_DOCS.md +1 -1
  30. package/dist-assets/docs/policies/10-BEHAVIORAL_CONTRACT_HARDENING.md +1 -1
  31. package/dist-assets/docs/policies/ORCHESTRATION_PROTOCOL.md +8 -9
  32. package/dist-assets/docs/policies/PROCEDURE_DELIVERY_ARTIFACTS.md +4 -10
  33. package/dist-assets/docs/policies/SKILLS_COMMON_GOVERNANCE.md +1 -1
  34. package/dist-assets/docs/profiles/README.md +2 -2
  35. package/dist-assets/docs/specs/runtime-operational-contract.md +12 -12
  36. package/dist-assets/docs/troubleshooting-guide.md +2 -2
  37. package/dist-assets/docs/visual-validation-guide.md +3 -3
  38. package/dist-assets/schemas/evidence.schema.json +109 -0
  39. package/dist-assets/templates/ANTIGRAVITY.md.template +7 -5
  40. package/dist-assets/templates/CLAUDE.md.template +7 -5
  41. package/dist-assets/templates/CODEX.md.template +7 -5
  42. package/evidence-validator-76ZQQYDU.js +6661 -0
  43. package/package.json +7 -64
  44. package/{src/core/workspace/read-only-workspace.js → read-only-workspace-PZBE7KAX.js} +14 -31
  45. package/{src/commands/validate.js → validate-Q2NLLQ5G.js} +30 -22
  46. package/AGENTS.md +0 -34
  47. package/PUBLISH_MANIFEST.json +0 -34
  48. package/dist-assets/templates/.geminiignore.template +0 -8
  49. package/dist-assets/templates/GEMINI.md.template +0 -20
  50. package/docs/compatibility/provider-usage.md +0 -46
  51. package/docs/compatibility/runtime-matrix.md +0 -31
  52. package/docs/getting-started/DESKTOP_PROMPT.md +0 -52
  53. package/docs/getting-started/authoring-custom-skills.md +0 -80
  54. package/docs/getting-started/quickstart.md +0 -117
  55. package/docs/getting-started/troubleshooting.md +0 -123
  56. package/docs/getting-started/upgrading-to-v2.md +0 -62
  57. package/src/adapters/index.js +0 -4
  58. package/src/adapters/platforms/antigravity.js +0 -382
  59. package/src/adapters/platforms/claude.js +0 -126
  60. package/src/adapters/platforms/codex.js +0 -113
  61. package/src/adapters/platforms/gemini.js +0 -245
  62. package/src/cli.js +0 -155
  63. package/src/commands/clean.js +0 -106
  64. package/src/commands/collect-evidence.js +0 -25
  65. package/src/commands/doctor.js +0 -202
  66. package/src/commands/execute.js +0 -382
  67. package/src/commands/init.js +0 -359
  68. package/src/commands/run.js +0 -112
  69. package/src/core/backup.js +0 -65
  70. package/src/core/completion-contract.js +0 -35
  71. package/src/core/delegation-controller.js +0 -193
  72. package/src/core/evidence/evidence-ledger.js +0 -98
  73. package/src/core/execution-planner.js +0 -62
  74. package/src/core/filesystem.js +0 -141
  75. package/src/core/finalization/finalizer.js +0 -165
  76. package/src/core/finalization/workspace-snapshot.js +0 -110
  77. package/src/core/gates/branch-gate.js +0 -134
  78. package/src/core/gates/merge-gate.js +0 -74
  79. package/src/core/handoff/handoff-engine.js +0 -104
  80. package/src/core/healing/cli-remediation-executor.js +0 -153
  81. package/src/core/healing/healer-engine.js +0 -212
  82. package/src/core/healing/runtime-remediation-executor.js +0 -136
  83. package/src/core/identity.js +0 -43
  84. package/src/core/install-plan.js +0 -34
  85. package/src/core/opencode-merge.js +0 -150
  86. package/src/core/package-assets.js +0 -141
  87. package/src/core/request-classifier.js +0 -73
  88. package/src/core/request-router.js +0 -289
  89. package/src/core/runtime/opencode-adapter.js +0 -202
  90. package/src/core/sdd/validator.js +0 -67
  91. package/src/core/statuses.js +0 -29
  92. package/src/core/symlink-layout.js +0 -93
  93. package/src/core/templates.js +0 -229
  94. package/src/core/validation/artifact-fidelity-gate.js +0 -445
  95. package/src/core/validation/canonical-finalization.js +0 -43
  96. package/src/core/validation/delivery-decision-engine.js +0 -344
  97. package/src/core/validation/evidence-collector.js +0 -245
  98. package/src/core/validation/quality-guard.js +0 -251
  99. package/src/core/validation/stack-detector.js +0 -65
  100. package/src/core/validation/validation-planner.js +0 -134
  101. package/src/core/validation/visual-verifier.js +0 -175
  102. package/src/core/workflow-profiles.js +0 -107
  103. package/src/core/workflow-state-machine.js +0 -46
@@ -1,193 +0,0 @@
1
- import { OpenCodeAdapter } from "./runtime/opencode-adapter.js";
2
-
3
- /**
4
- * DelegationController - Manages and logs coordination between Atlas, Astra, Sage, and Phoenix.
5
- */
6
- export class DelegationController {
7
- constructor({ cwd = process.cwd(), ledger, adapter } = {}) {
8
- this.cwd = cwd;
9
- this.ledger = ledger;
10
- this.adapter = adapter || new OpenCodeAdapter({ cwd });
11
- }
12
-
13
- /**
14
- * Logs routing decision by Atlas.
15
- */
16
- async route(naturalRequest, classification) {
17
- const decision = classification.routingDecision || {};
18
- this.lastRoutingDecision = decision;
19
-
20
- if (this.ledger) {
21
- this.ledger.logEvent({
22
- actor: "Atlas",
23
- actorType: "control-plane",
24
- observed: true,
25
- eventType: "routing",
26
- provenance: "request-classifier",
27
- data: {
28
- request: naturalRequest,
29
- requestedActor: decision.requestedActor || null,
30
- selectedActor: decision.selectedActor || null,
31
- operationType: decision.operationType || null,
32
- mutationIntent: decision.mutationIntent || null,
33
- permissionDecision: decision.permissionDecision || null,
34
- reason: decision.reason || null,
35
- workflowPath: decision.workflowPath || [],
36
- classification,
37
- event: "routing.completed",
38
- runtimeAgentRequested: null,
39
- runtimeAgentApplied: null,
40
- runtimeAgentSupported: false,
41
- status: decision.permissionDecision === "blocked" ? "BLOCKED" : "COMPLETED"
42
- }
43
- });
44
- }
45
- }
46
-
47
- /**
48
- * Logs and executes the implementation process by Astra.
49
- */
50
- async implement(promptMsg, owner, options = {}) {
51
- const decision = this.lastRoutingDecision || {};
52
- const inspection = await this.adapter.inspect();
53
- const isSpecialized = ["Nexus", "Orion", "Astra", "Sage", "Phoenix"].includes(owner);
54
- const supportsAgent = inspection.supports?.agent;
55
- const canApply = !isSpecialized || supportsAgent;
56
-
57
- if (this.ledger) {
58
- this.ledger.logEvent({
59
- actor: canApply ? owner : "Atlas",
60
- actorType: canApply ? "runtime-agent" : "control-plane",
61
- observed: true,
62
- runtime: "opencode",
63
- eventType: "implementation_start",
64
- provenance: "opencode-adapter",
65
- data: {
66
- agent: owner,
67
- prompt: promptMsg,
68
- event: "implementation.started",
69
- requestedActor: decision.requestedActor || null,
70
- selectedActor: decision.selectedActor || null,
71
- runtimeAgentRequested: owner,
72
- runtimeAgentApplied: null,
73
- runtimeAgentSupported: supportsAgent || false,
74
- permissionDecision: decision.permissionDecision || null,
75
- reason: decision.reason || null,
76
- status: "started"
77
- }
78
- });
79
- }
80
-
81
- const runResult = await this.adapter.execute(promptMsg, { agent: owner, ...options });
82
-
83
- if (this.ledger) {
84
- const isApplied = runResult.runtimeAgentApplied === owner;
85
- this.ledger.logEvent({
86
- actor: isApplied ? owner : "Atlas",
87
- actorType: isApplied ? "runtime-agent" : "control-plane",
88
- observed: true,
89
- runtime: "opencode",
90
- eventType: "implementation_complete",
91
- provenance: "opencode-adapter",
92
- data: {
93
- success: runResult.success,
94
- commandsRun: runResult.commandsRun || [],
95
- eventCount: runResult.eventCount || 0,
96
- error: runResult.error,
97
- event: "implementation.completed",
98
- requestedActor: decision.requestedActor || null,
99
- selectedActor: decision.selectedActor || null,
100
- runtimeAgentRequested: runResult.runtimeAgentRequested || owner || null,
101
- runtimeAgentApplied: runResult.runtimeAgentApplied || null,
102
- runtimeAgentSupported: runResult.runtimeAgentSupported !== undefined ? runResult.runtimeAgentSupported : (supportsAgent || false),
103
- permissionDecision: decision.permissionDecision || null,
104
- reason: runResult.error || decision.reason || null,
105
- status: runResult.status || (runResult.success ? "COMPLETED" : "FAILED")
106
- }
107
- });
108
- }
109
-
110
- return runResult;
111
- }
112
-
113
- /**
114
- * Runs validation locally without logging Sage since no validation agent runtime was executed.
115
- */
116
- async validate(taskSlug, mode, profile, verifyFn) {
117
- const result = await verifyFn();
118
-
119
- if (mode === "full") {
120
- const decision = this.lastRoutingDecision || {};
121
- const inspection = await this.adapter.inspect();
122
- const supportsAgent = inspection.supports?.agent;
123
- const canApply = supportsAgent;
124
-
125
- if (this.ledger) {
126
- this.ledger.logEvent({
127
- actor: canApply ? "Sage" : "Atlas",
128
- actorType: canApply ? "auditor" : "control-plane",
129
- observed: true,
130
- runtime: "opencode",
131
- eventType: "validation_start",
132
- provenance: "opencode-adapter",
133
- data: {
134
- agent: "Sage",
135
- event: "validation.started",
136
- requestedActor: decision.requestedActor || null,
137
- selectedActor: decision.selectedActor || null,
138
- runtimeAgentRequested: "Sage",
139
- runtimeAgentApplied: null,
140
- runtimeAgentSupported: supportsAgent || false,
141
- permissionDecision: decision.permissionDecision || null,
142
- reason: decision.reason || null,
143
- status: "started"
144
- }
145
- });
146
- }
147
-
148
- let gitDiff = "";
149
- try {
150
- const { execSync } = await import("node:child_process");
151
- gitDiff = execSync("git diff HEAD", { cwd: this.cwd, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
152
- } catch {
153
- // ignore
154
- }
155
-
156
- const prompt = `Please validate the implementation. Diff:\n${gitDiff}\nLocal Evidence:\n${JSON.stringify(result, null, 2)}`;
157
- const runResult = await this.adapter.execute(prompt, { agent: "Sage" });
158
-
159
- if (this.ledger) {
160
- const isApplied = runResult.runtimeAgentApplied === "Sage";
161
- this.ledger.logEvent({
162
- actor: isApplied ? "Sage" : "Atlas",
163
- actorType: isApplied ? "auditor" : "control-plane",
164
- observed: true,
165
- runtime: "opencode",
166
- eventType: "validation_complete",
167
- provenance: "opencode-adapter",
168
- data: {
169
- success: runResult.success,
170
- commandsRun: runResult.commandsRun || [],
171
- eventCount: runResult.eventCount || 0,
172
- error: runResult.error,
173
- event: "validation.completed",
174
- requestedActor: decision.requestedActor || null,
175
- selectedActor: decision.selectedActor || null,
176
- runtimeAgentRequested: runResult.runtimeAgentRequested || "Sage",
177
- runtimeAgentApplied: runResult.runtimeAgentApplied || null,
178
- runtimeAgentSupported: runResult.runtimeAgentSupported !== undefined ? runResult.runtimeAgentSupported : (supportsAgent || false),
179
- permissionDecision: decision.permissionDecision || null,
180
- reason: runResult.error || decision.reason || null,
181
- status: runResult.status || (runResult.success ? "COMPLETED" : "FAILED")
182
- }
183
- });
184
- }
185
-
186
- if (!runResult.success) {
187
- result.overallStatus = "FAIL_QUALITY_GATE";
188
- }
189
- }
190
-
191
- return result;
192
- }
193
- }
@@ -1,98 +0,0 @@
1
- import fs from "node:fs/promises";
2
- import path from "node:path";
3
-
4
- /**
5
- * EvidenceLedger - Traces structural workflow events to make delegation observable.
6
- */
7
- export class EvidenceLedger {
8
- constructor({ cwd = process.cwd(), workflowId = "unknown" } = {}) {
9
- this.cwd = cwd;
10
- this.workflowId = workflowId;
11
- this.events = [];
12
- }
13
-
14
- /**
15
- * Logs a workflow event to the ledger.
16
- * @param {Object} eventParam
17
- * @param {string} eventParam.actor - The agent name (Atlas, Astra, Sage, Phoenix).
18
- * @param {string} eventParam.eventType - Type of event.
19
- * @param {string} eventParam.provenance - Originating component.
20
- * @param {Object} [eventParam.data] - Additional metadata.
21
- * @returns {Object} The logged event.
22
- */
23
- logEvent({ actor, eventType, provenance, data = {}, actorType, observed, runtime }) {
24
- const event = {
25
- workflowId: this.workflowId,
26
- timestamp: new Date().toISOString(),
27
- actor,
28
- actorType: actorType !== undefined ? actorType : undefined,
29
- observed: observed !== undefined ? observed : undefined,
30
- runtime: runtime !== undefined ? runtime : undefined,
31
- eventType,
32
- provenance,
33
- data
34
- };
35
- this.events.push(event);
36
- console.log(`[LEDGER] [${actor}] ${eventType}: ${JSON.stringify(data)}`);
37
- return event;
38
- }
39
-
40
- getEvents() {
41
- return this.events;
42
- }
43
-
44
- /**
45
- * Saves the ledger events to a JSON file.
46
- * @param {string} filePath - Absolute or relative path.
47
- */
48
- async save(filePath) {
49
- const targetPath = path.isAbsolute(filePath) ? filePath : path.join(this.cwd, filePath);
50
- await fs.mkdir(path.dirname(targetPath), { recursive: true });
51
- await fs.writeFile(targetPath, JSON.stringify(this.events, null, 2), "utf8");
52
- }
53
-
54
- async verifyRoleConfinement() {
55
- const violations = [];
56
- const events = this.events;
57
-
58
- const atlasImplements = events.some(e => e.actor === "Atlas" && (e.eventType === "implementation_start" || e.eventType === "implementation_complete"));
59
- if (atlasImplements) {
60
- violations.push("Atlas cannot implement directly");
61
- }
62
-
63
- const nexusMutates = events.some(e => e.actor === "Nexus" && e.eventType === "file_mutate");
64
- if (nexusMutates) {
65
- violations.push("Nexus cannot mutate files during discovery");
66
- }
67
-
68
- const sageValidates = events.some(e => e.actor === "Sage" && (e.eventType === "validation_start" || e.eventType === "validation_complete"));
69
- const hasFidelity = events.some(e => e.eventType === "fidelity_check" || e.eventType === "fidelity_verification");
70
- if (sageValidates && !hasFidelity) {
71
- violations.push("Sage cannot validate without fidelity evidence");
72
- }
73
-
74
- const phoenixRemediates = events.find(e => e.actor === "Phoenix" && e.eventType === "remediation_start");
75
- if (phoenixRemediates) {
76
- const findings = phoenixRemediates.data?.findings;
77
- if (!findings || findings.length === 0) {
78
- violations.push("Phoenix cannot perform remediation without findings");
79
- }
80
- }
81
-
82
- const orionReleases = events.find(e => e.actor === "Orion" && e.eventType === "release_start");
83
- if (orionReleases) {
84
- const approvals = orionReleases.data?.approvals;
85
- if (!approvals || approvals.length === 0) {
86
- violations.push("Orion cannot release without explicit approval");
87
- }
88
- }
89
-
90
- const passed = violations.length === 0;
91
- return {
92
- passed,
93
- status: passed ? "PASS" : "BLOCKED",
94
- reason: violations.length > 0 ? `Role violations: ${violations.join("; ")}` : "Verification passed",
95
- violations
96
- };
97
- }
98
- }
@@ -1,62 +0,0 @@
1
- import path from "node:path";
2
-
3
- /**
4
- * ExecutionPlanner - Formulates a minimum operational plan based on classification.
5
- */
6
- export class ExecutionPlanner {
7
- constructor({ cwd = process.cwd() } = {}) {
8
- this.cwd = cwd;
9
- }
10
-
11
- /**
12
- * Generates an execution plan.
13
- * @param {Object} classification - The output of RequestClassifier.
14
- * @param {string} taskSlug - Descriptive slug for the task.
15
- * @returns {Object} Operational plan contract.
16
- */
17
- plan(classification, taskSlug = "task") {
18
- const remediationLimit = classification.mode === "full" ? 3 : classification.mode === "quick" ? 1 : 2;
19
- const branchNeeded = classification.intent === "write";
20
- const specPath = classification.specNeeded
21
- ? path.join("docs/workflows", taskSlug, "spec.md")
22
- : null;
23
-
24
- const owner = classification.routingDecision?.selectedActor ||
25
- (classification.intent === "write" ? "Astra" : classification.owner);
26
-
27
- // Build default expected validations based on the resolved profile
28
- const validationsExpected = [];
29
- if (classification.validationNeeded) {
30
- validationsExpected.push("test");
31
- if (classification.profile.startsWith("frontend")) {
32
- validationsExpected.push("lint", "build");
33
- } else if (classification.profile === "backend-api") {
34
- validationsExpected.push("lint");
35
- }
36
- }
37
-
38
- const restrictions = [
39
- "Never commit or push directly to main/master.",
40
- "Always execute behavior tests for new or modified behavior."
41
- ];
42
-
43
- if (classification.risk === "high") {
44
- restrictions.push("Require independent validation review.");
45
- }
46
-
47
- return {
48
- objective: classification.request,
49
- scope: classification.intent === "read-only" ? "Analysis and verification" : "Implementation of requested behavior",
50
- restrictions,
51
- owner,
52
- skills: [...classification.skills],
53
- branchNeeded,
54
- branchName: branchNeeded ? `feat/${taskSlug}` : null,
55
- validationsExpected,
56
- remediationLimit,
57
- specPath,
58
- mode: classification.mode,
59
- profile: classification.profile
60
- };
61
- }
62
- }
@@ -1,141 +0,0 @@
1
- import fs from "node:fs/promises";
2
- import path from "node:path";
3
-
4
- export async function exists(filePath) {
5
- try {
6
- await fs.access(filePath);
7
- return true;
8
- } catch {
9
- return false;
10
- }
11
- }
12
-
13
- export async function writeFileSafe(filePath, content) {
14
- await fs.mkdir(path.dirname(filePath), { recursive: true });
15
- await fs.writeFile(filePath, content, "utf8");
16
- }
17
-
18
- export async function readJson(filePath) {
19
- const content = await fs.readFile(filePath, "utf8");
20
- return JSON.parse(content);
21
- }
22
-
23
- function stripJsonComments(content) {
24
- let output = "";
25
- let inString = false;
26
- let quote = "";
27
- let escaped = false;
28
- let inLineComment = false;
29
- let inBlockComment = false;
30
-
31
- for (let index = 0; index < content.length; index += 1) {
32
- const char = content[index];
33
- const next = content[index + 1];
34
-
35
- if (inLineComment) {
36
- if (char === "\n" || char === "\r") {
37
- inLineComment = false;
38
- output += char;
39
- }
40
- continue;
41
- }
42
-
43
- if (inBlockComment) {
44
- if (char === "*" && next === "/") {
45
- inBlockComment = false;
46
- index += 1;
47
- continue;
48
- }
49
- if (char === "\n" || char === "\r") {
50
- output += char;
51
- }
52
- continue;
53
- }
54
-
55
- if (inString) {
56
- output += char;
57
- if (escaped) {
58
- escaped = false;
59
- } else if (char === "\\") {
60
- escaped = true;
61
- } else if (char === quote) {
62
- inString = false;
63
- quote = "";
64
- }
65
- continue;
66
- }
67
-
68
- if (char === '"' || char === "'") {
69
- inString = true;
70
- quote = char;
71
- output += char;
72
- continue;
73
- }
74
-
75
- if (char === "/" && next === "/") {
76
- inLineComment = true;
77
- index += 1;
78
- continue;
79
- }
80
-
81
- if (char === "/" && next === "*") {
82
- inBlockComment = true;
83
- index += 1;
84
- continue;
85
- }
86
-
87
- output += char;
88
- }
89
-
90
- return output;
91
- }
92
-
93
- function stripTrailingCommas(content) {
94
- let output = "";
95
- let inString = false;
96
- let quote = "";
97
- let escaped = false;
98
-
99
- for (let index = 0; index < content.length; index += 1) {
100
- const char = content[index];
101
-
102
- if (inString) {
103
- output += char;
104
- if (escaped) {
105
- escaped = false;
106
- } else if (char === "\\") {
107
- escaped = true;
108
- } else if (char === quote) {
109
- inString = false;
110
- quote = "";
111
- }
112
- continue;
113
- }
114
-
115
- if (char === '"' || char === "'") {
116
- inString = true;
117
- quote = char;
118
- output += char;
119
- continue;
120
- }
121
-
122
- if (char === ",") {
123
- let nextIndex = index + 1;
124
- while (/\s/.test(content[nextIndex] ?? "")) {
125
- nextIndex += 1;
126
- }
127
- if (content[nextIndex] === "}" || content[nextIndex] === "]") {
128
- continue;
129
- }
130
- }
131
-
132
- output += char;
133
- }
134
-
135
- return output;
136
- }
137
-
138
- export async function readJsonc(filePath) {
139
- const content = await fs.readFile(filePath, "utf8");
140
- return JSON.parse(stripTrailingCommas(stripJsonComments(content)));
141
- }
@@ -1,165 +0,0 @@
1
- import { WorkspaceSnapshot } from "./workspace-snapshot.js";
2
- import { DeliveryDecisionEngine } from "../validation/delivery-decision-engine.js";
3
- import { ArtifactFidelityGate } from "../validation/artifact-fidelity-gate.js";
4
- import { execSync } from "node:child_process";
5
-
6
- export class Finalizer {
7
- constructor({ cwd = process.cwd() } = {}) {
8
- this.cwd = cwd;
9
- this.snapshotManager = new WorkspaceSnapshot({ cwd });
10
- }
11
-
12
- getChangedFilesFromGit() {
13
- try {
14
- const files = new Set();
15
- // Unstaged changes
16
- const diff = execSync("git diff --name-only HEAD", { cwd: this.cwd, encoding: "utf8" });
17
- diff.split("\n").map(f => f.trim()).filter(Boolean).forEach(f => files.add(f));
18
-
19
- // Untracked/staged status
20
- const status = execSync("git status --short --untracked-files=all", { cwd: this.cwd, encoding: "utf8" });
21
- status.split("\n").forEach(line => {
22
- const match = line.match(/^(?:[ MADRCU?!]{2}\s+|[MADRCU?!]\s+)(.+)$/);
23
- if (match) {
24
- const candidate = match[1].trim();
25
- const renamed = candidate.includes(" -> ") ? candidate.split(" -> ").at(-1) : candidate;
26
- if (renamed) files.add(renamed);
27
- }
28
- });
29
- return [...files].sort();
30
- } catch {
31
- return [];
32
- }
33
- }
34
-
35
- async verifyFidelity(arg) {
36
- let userRequest = null;
37
- let projectContext = null;
38
- let deliveryDecision = null;
39
- let changedFiles = null;
40
- let evidence = null;
41
- let explicitApprovals = [];
42
-
43
- if (Array.isArray(arg)) {
44
- changedFiles = arg;
45
- } else if (arg && typeof arg === "object") {
46
- userRequest = arg.userRequest;
47
- projectContext = arg.projectContext;
48
- deliveryDecision = arg.deliveryDecision;
49
- changedFiles = arg.changedFiles;
50
- evidence = arg.evidence;
51
- explicitApprovals = arg.explicitApprovals || [];
52
- }
53
-
54
- const filesToCheck = changedFiles || this.getChangedFilesFromGit();
55
- const decisionEngine = new DeliveryDecisionEngine({ cwd: this.cwd });
56
- const actualProjectContext = projectContext || await decisionEngine.detectContext();
57
- const actualUserRequest = userRequest || "";
58
-
59
- const actualDeliveryDecision = deliveryDecision || await decisionEngine.determineDecision({
60
- userRequest: actualUserRequest,
61
- projectContext: actualProjectContext,
62
- explicitApprovals
63
- });
64
-
65
- const actualDeliveryClass = await decisionEngine.determineClass(filesToCheck);
66
-
67
- const fidelityGate = new ArtifactFidelityGate({
68
- cwd: this.cwd,
69
- deliveryClass: actualDeliveryClass,
70
- userRequest: actualUserRequest,
71
- explicitApprovals
72
- });
73
-
74
- const requestFidelity = await fidelityGate.verifyRequestFidelity({
75
- userRequest: actualUserRequest,
76
- deliveryDecision: actualDeliveryDecision,
77
- projectContext: actualProjectContext,
78
- changedFiles: filesToCheck,
79
- evidence,
80
- explicitApprovals
81
- });
82
-
83
- const verifyResult = await fidelityGate.verify(filesToCheck);
84
-
85
- const passed = requestFidelity.passed && verifyResult.passed;
86
- const reason = !requestFidelity.passed ? requestFidelity.reason : verifyResult.reason;
87
-
88
- return {
89
- passed,
90
- status: requestFidelity.status || (passed ? "PASS" : "BLOCKED"),
91
- reason,
92
- deliveryClass: actualDeliveryClass,
93
- requestFidelity
94
- };
95
- }
96
-
97
- /**
98
- * Compares the current workspace state with a previously captured snapshot.
99
- * @param {Object} previousSnapshot - The snapshot object captured earlier.
100
- * @returns {Object} { valid: boolean, changes: { added: string[], modified: string[], deleted: string[] } }
101
- */
102
- async verifyIntegrity(previousSnapshot) {
103
- const currentSnapshot = await this.snapshotManager.capture();
104
- const added = [];
105
- const modified = [];
106
- const deleted = [];
107
-
108
- let valid = true;
109
-
110
- // Compare metadata and git trees
111
- if (previousSnapshot.branch !== currentSnapshot.branch) {
112
- modified.push(`branch:${previousSnapshot.branch}->${currentSnapshot.branch}`);
113
- valid = false;
114
- }
115
- if (previousSnapshot.head !== currentSnapshot.head) {
116
- modified.push(`head:${previousSnapshot.head}->${currentSnapshot.head}`);
117
- valid = false;
118
- }
119
- if (previousSnapshot.indexTree !== currentSnapshot.indexTree) {
120
- modified.push(`indexTree:${previousSnapshot.indexTree}->${currentSnapshot.indexTree}`);
121
- valid = false;
122
- }
123
- if (previousSnapshot.stagedDiffHash !== currentSnapshot.stagedDiffHash) {
124
- modified.push("stagedDiff");
125
- valid = false;
126
- }
127
- if (previousSnapshot.unstagedDiffHash !== currentSnapshot.unstagedDiffHash) {
128
- modified.push("unstagedDiff");
129
- valid = false;
130
- }
131
- if (previousSnapshot.untrackedFilesHash !== currentSnapshot.untrackedFilesHash) {
132
- modified.push("untrackedFiles");
133
- valid = false;
134
- }
135
-
136
- // Check for additions and modifications in files
137
- const prevFiles = previousSnapshot.files || {};
138
- const currFiles = currentSnapshot.files || {};
139
-
140
- for (const [file, info] of Object.entries(currFiles)) {
141
- const prevInfo = prevFiles[file];
142
- if (!prevInfo) {
143
- added.push(file);
144
- } else if (prevInfo.sha256 !== info.sha256 || prevInfo.mode !== info.mode) {
145
- modified.push(file);
146
- }
147
- }
148
-
149
- // Check for deletions in files
150
- for (const file of Object.keys(prevFiles)) {
151
- if (!currFiles[file]) {
152
- deleted.push(file);
153
- }
154
- }
155
-
156
- if (added.length > 0 || modified.length > 0 || deleted.length > 0) {
157
- valid = false;
158
- }
159
-
160
- return {
161
- valid,
162
- changes: { added, modified, deleted }
163
- };
164
- }
165
- }