@williambeto/ai-workflow 2.4.9 → 2.6.3

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 (104) hide show
  1. package/bin/ai-workflow.d.ts +3 -0
  2. package/bin/ai-workflow.js +2669 -1
  3. package/bin/ai-workflow.js.map +1 -0
  4. package/chunk-2AOV2ATY.js +749 -0
  5. package/chunk-5WRI5ZAA.js +31 -0
  6. package/chunk-6EMG6FAO.js +734 -0
  7. package/chunk-UHLZX2EG.js +1827 -0
  8. package/core/index.d.ts +440 -0
  9. package/core/index.js +32 -0
  10. package/core/index.js.map +1 -0
  11. package/dist-assets/AGENTS.md +23 -6
  12. package/dist-assets/agents/atlas.md +8 -8
  13. package/dist-assets/agents/nexus.md +4 -4
  14. package/dist-assets/agents/orion.md +1 -1
  15. package/dist-assets/agents/phoenix.md +1 -1
  16. package/dist-assets/agents/sage.md +1 -1
  17. package/dist-assets/commands/implement.md +1 -1
  18. package/dist-assets/docs/QUICKSTART.md +8 -8
  19. package/dist-assets/docs/cli-reference.md +7 -7
  20. package/dist-assets/docs/compatibility/provider-usage.md +1 -9
  21. package/dist-assets/docs/compatibility/runtime-matrix.md +5 -6
  22. package/dist-assets/docs/consumer-onboarding.md +2 -2
  23. package/dist-assets/docs/design-patterns-policy.md +1 -1
  24. package/dist-assets/docs/npm-consumer-quickstart.md +70 -109
  25. package/dist-assets/docs/policies/01-BRANCH_GATE.md +1 -1
  26. package/dist-assets/docs/policies/06-FINAL_EVIDENCE_CONTRACT.md +2 -2
  27. package/dist-assets/docs/policies/08-PRODUCT_TRUTHFULNESS_AND_PROJECT_DOCS.md +1 -1
  28. package/dist-assets/docs/policies/10-BEHAVIORAL_CONTRACT_HARDENING.md +1 -1
  29. package/dist-assets/docs/policies/ORCHESTRATION_PROTOCOL.md +8 -9
  30. package/dist-assets/docs/policies/PROCEDURE_DELIVERY_ARTIFACTS.md +4 -10
  31. package/dist-assets/docs/policies/SKILLS_COMMON_GOVERNANCE.md +1 -1
  32. package/dist-assets/docs/profiles/README.md +2 -2
  33. package/dist-assets/docs/specs/runtime-operational-contract.md +12 -12
  34. package/dist-assets/docs/troubleshooting-guide.md +2 -2
  35. package/dist-assets/docs/visual-validation-guide.md +3 -3
  36. package/dist-assets/schemas/evidence.schema.json +109 -0
  37. package/dist-assets/templates/ANTIGRAVITY.md.template +7 -5
  38. package/dist-assets/templates/CLAUDE.md.template +7 -5
  39. package/dist-assets/templates/CODEX.md.template +7 -5
  40. package/evidence-validator-76ZQQYDU.js +6661 -0
  41. package/package.json +7 -64
  42. package/{src/core/workspace/read-only-workspace.js → read-only-workspace-PZBE7KAX.js} +14 -31
  43. package/{src/commands/validate.js → validate-Q2NLLQ5G.js} +30 -22
  44. package/AGENTS.md +0 -34
  45. package/CHANGELOG.md +0 -259
  46. package/LICENSE +0 -21
  47. package/PUBLISH_MANIFEST.json +0 -34
  48. package/README.md +0 -129
  49. package/dist-assets/templates/.geminiignore.template +0 -8
  50. package/dist-assets/templates/GEMINI.md.template +0 -20
  51. package/docs/compatibility/provider-usage.md +0 -46
  52. package/docs/compatibility/runtime-matrix.md +0 -31
  53. package/docs/getting-started/DESKTOP_PROMPT.md +0 -52
  54. package/docs/getting-started/authoring-custom-skills.md +0 -80
  55. package/docs/getting-started/quickstart.md +0 -117
  56. package/docs/getting-started/troubleshooting.md +0 -123
  57. package/docs/getting-started/upgrading-to-v2.md +0 -62
  58. package/src/adapters/index.js +0 -4
  59. package/src/adapters/platforms/antigravity.js +0 -382
  60. package/src/adapters/platforms/claude.js +0 -126
  61. package/src/adapters/platforms/codex.js +0 -113
  62. package/src/adapters/platforms/gemini.js +0 -245
  63. package/src/cli.js +0 -155
  64. package/src/commands/clean.js +0 -106
  65. package/src/commands/collect-evidence.js +0 -25
  66. package/src/commands/doctor.js +0 -202
  67. package/src/commands/execute.js +0 -382
  68. package/src/commands/init.js +0 -359
  69. package/src/commands/run.js +0 -112
  70. package/src/core/backup.js +0 -65
  71. package/src/core/completion-contract.js +0 -35
  72. package/src/core/delegation-controller.js +0 -193
  73. package/src/core/evidence/evidence-ledger.js +0 -98
  74. package/src/core/execution-planner.js +0 -62
  75. package/src/core/filesystem.js +0 -141
  76. package/src/core/finalization/finalizer.js +0 -165
  77. package/src/core/finalization/workspace-snapshot.js +0 -110
  78. package/src/core/gates/branch-gate.js +0 -134
  79. package/src/core/gates/merge-gate.js +0 -74
  80. package/src/core/handoff/handoff-engine.js +0 -104
  81. package/src/core/healing/cli-remediation-executor.js +0 -153
  82. package/src/core/healing/healer-engine.js +0 -212
  83. package/src/core/healing/runtime-remediation-executor.js +0 -136
  84. package/src/core/identity.js +0 -43
  85. package/src/core/install-plan.js +0 -34
  86. package/src/core/opencode-merge.js +0 -150
  87. package/src/core/package-assets.js +0 -141
  88. package/src/core/request-classifier.js +0 -73
  89. package/src/core/request-router.js +0 -289
  90. package/src/core/runtime/opencode-adapter.js +0 -202
  91. package/src/core/sdd/validator.js +0 -67
  92. package/src/core/statuses.js +0 -29
  93. package/src/core/symlink-layout.js +0 -93
  94. package/src/core/templates.js +0 -229
  95. package/src/core/validation/artifact-fidelity-gate.js +0 -445
  96. package/src/core/validation/canonical-finalization.js +0 -43
  97. package/src/core/validation/delivery-decision-engine.js +0 -344
  98. package/src/core/validation/evidence-collector.js +0 -245
  99. package/src/core/validation/quality-guard.js +0 -251
  100. package/src/core/validation/stack-detector.js +0 -65
  101. package/src/core/validation/validation-planner.js +0 -134
  102. package/src/core/validation/visual-verifier.js +0 -175
  103. package/src/core/workflow-profiles.js +0 -107
  104. 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
- }