@williambeto/ai-workflow 2.9.4 → 2.9.6
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/CHANGELOG.md +44 -0
- package/bin/ai-workflow.js +113 -15
- package/bin/ai-workflow.js.map +1 -1
- package/{chunk-PP42PB7M.js → chunk-D2W2ZW3Y.js} +1 -1
- package/{chunk-JDSEOEYW.js → chunk-E7DHZFTN.js} +2 -2
- package/{chunk-FNT7DN3N.js → chunk-XRYGVFHF.js} +3 -2
- package/core/index.d.ts +3 -1
- package/core/index.js +1 -1
- package/dist-assets/commands/atlas.md +7 -0
- package/dist-assets/commands/deploy.md +6 -0
- package/dist-assets/commands/implement.md +7 -0
- package/dist-assets/commands/release.md +6 -0
- package/dist-assets/commands/validate.md +6 -0
- package/dist-assets/docs/cli-reference.md +3 -1
- package/{evidence-validator-HS3NTWAB.js → evidence-validator-6GD442JE.js} +2 -2
- package/package.json +2 -2
- package/{validate-7G3HBE2K.js → validate-YMBSICJL.js} +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,49 @@
|
|
|
1
1
|
## [unreleased]
|
|
2
2
|
|
|
3
|
+
## [2.9.6] - 2026-07-19
|
|
4
|
+
|
|
5
|
+
### 🐛 Bug Fixes
|
|
6
|
+
|
|
7
|
+
- Include committed release changes in evidence through an explicit Git base
|
|
8
|
+
reference while preserving current working-tree behavior by default
|
|
9
|
+
- Replace duplicate aggregate validation inside the E2E suite with an exact
|
|
10
|
+
registry contract, keeping the observed aggregate gate in its dedicated CI
|
|
11
|
+
step
|
|
12
|
+
|
|
13
|
+
### 🛡️ Hardening
|
|
14
|
+
|
|
15
|
+
- Enforce TypeScript source checking in local validation and GitHub Actions
|
|
16
|
+
- Point internal architecture documentation to existing canonical release,
|
|
17
|
+
template inventory, and status sources, with release-readiness regression
|
|
18
|
+
coverage
|
|
19
|
+
|
|
20
|
+
### 📦 Dependencies
|
|
21
|
+
|
|
22
|
+
- Update `@opencode-ai/sdk` from 1.17.18 to 1.18.3 after package-diff,
|
|
23
|
+
adapter, consumer, and packaging compatibility validation
|
|
24
|
+
|
|
25
|
+
### 🧪 Testing
|
|
26
|
+
|
|
27
|
+
- Cover committed, uncommitted, added, deleted, and invalid-ref release
|
|
28
|
+
evidence scopes
|
|
29
|
+
- Preserve the complete 23-check validation registry while removing a
|
|
30
|
+
redundant network and browser execution path from E2E
|
|
31
|
+
|
|
32
|
+
## [2.9.5] - 2026-07-17
|
|
33
|
+
|
|
34
|
+
### 🛡️ Hardening
|
|
35
|
+
|
|
36
|
+
- Declare the 17 required command-to-policy relationships through a
|
|
37
|
+
runtime-neutral canonical policy section
|
|
38
|
+
- Fail validation when required command policy references are missing,
|
|
39
|
+
duplicated, unknown, or unexpected
|
|
40
|
+
|
|
41
|
+
### 🧪 Testing
|
|
42
|
+
|
|
43
|
+
- Add data-driven contract coverage for the complete policy reference map
|
|
44
|
+
- Verify referenced policies remain available in OpenCode installations and
|
|
45
|
+
Codex command skills
|
|
46
|
+
|
|
3
47
|
## [2.9.4] - 2026-07-16
|
|
4
48
|
|
|
5
49
|
### 🛡️ Hardening
|
package/bin/ai-workflow.js
CHANGED
|
@@ -23,18 +23,18 @@ import {
|
|
|
23
23
|
isTerminalFailure,
|
|
24
24
|
readPackageFile,
|
|
25
25
|
resolveWorkflowProfile
|
|
26
|
-
} from "../chunk-
|
|
26
|
+
} from "../chunk-E7DHZFTN.js";
|
|
27
27
|
import {
|
|
28
28
|
EvidenceValidator,
|
|
29
29
|
require_ajv,
|
|
30
30
|
require_codegen
|
|
31
|
-
} from "../chunk-
|
|
31
|
+
} from "../chunk-XRYGVFHF.js";
|
|
32
32
|
import {
|
|
33
33
|
EvidenceCollector,
|
|
34
34
|
QualityGuard,
|
|
35
35
|
ValidationPlanner,
|
|
36
36
|
parseDeliveryOutcomeClaim
|
|
37
|
-
} from "../chunk-
|
|
37
|
+
} from "../chunk-D2W2ZW3Y.js";
|
|
38
38
|
import {
|
|
39
39
|
DeliveryDecisionEngine
|
|
40
40
|
} from "../chunk-H7GIKXFO.js";
|
|
@@ -2194,6 +2194,9 @@ async function runDoctor({ cwd }) {
|
|
|
2194
2194
|
}
|
|
2195
2195
|
}
|
|
2196
2196
|
|
|
2197
|
+
// src/cli/commands/collect-evidence.ts
|
|
2198
|
+
import { spawnSync } from "child_process";
|
|
2199
|
+
|
|
2197
2200
|
// src/core/validation/canonical-finalization.ts
|
|
2198
2201
|
var DELIVERY_SUMMARY_FIELDS = Object.freeze([
|
|
2199
2202
|
"Status",
|
|
@@ -2217,6 +2220,93 @@ function formatDeliverySummary(summary) {
|
|
|
2217
2220
|
}
|
|
2218
2221
|
|
|
2219
2222
|
// src/cli/commands/collect-evidence.ts
|
|
2223
|
+
function runGit(cwd, args, errorMessage) {
|
|
2224
|
+
const result = spawnSync("git", args, { cwd, encoding: "utf8" });
|
|
2225
|
+
if (result.status !== 0) {
|
|
2226
|
+
const detail = String(result.stderr || result.error?.message || "unknown Git error").trim();
|
|
2227
|
+
throw new Error(`${errorMessage}${detail ? `: ${detail}` : ""}`);
|
|
2228
|
+
}
|
|
2229
|
+
return String(result.stdout || "");
|
|
2230
|
+
}
|
|
2231
|
+
function parseNameStatus(output) {
|
|
2232
|
+
const changedFiles = /* @__PURE__ */ new Set();
|
|
2233
|
+
const addedFiles = /* @__PURE__ */ new Set();
|
|
2234
|
+
const deletedFiles = /* @__PURE__ */ new Set();
|
|
2235
|
+
const tokens = output.split("\0").filter(Boolean);
|
|
2236
|
+
for (let index = 0; index < tokens.length; ) {
|
|
2237
|
+
const entry = tokens[index++];
|
|
2238
|
+
const tabIndex = entry.indexOf(" ");
|
|
2239
|
+
const status = tabIndex >= 0 ? entry.slice(0, tabIndex) : entry;
|
|
2240
|
+
const firstPath = tabIndex >= 0 ? entry.slice(tabIndex + 1) : tokens[index++];
|
|
2241
|
+
if (!firstPath) continue;
|
|
2242
|
+
if (status.startsWith("R")) {
|
|
2243
|
+
const nextPath = tokens[index++];
|
|
2244
|
+
changedFiles.add(firstPath);
|
|
2245
|
+
deletedFiles.add(firstPath);
|
|
2246
|
+
if (nextPath) {
|
|
2247
|
+
changedFiles.add(nextPath);
|
|
2248
|
+
addedFiles.add(nextPath);
|
|
2249
|
+
}
|
|
2250
|
+
continue;
|
|
2251
|
+
}
|
|
2252
|
+
if (status.startsWith("C")) {
|
|
2253
|
+
const nextPath = tokens[index++];
|
|
2254
|
+
if (nextPath) {
|
|
2255
|
+
changedFiles.add(nextPath);
|
|
2256
|
+
addedFiles.add(nextPath);
|
|
2257
|
+
}
|
|
2258
|
+
continue;
|
|
2259
|
+
}
|
|
2260
|
+
changedFiles.add(firstPath);
|
|
2261
|
+
if (status.startsWith("A")) addedFiles.add(firstPath);
|
|
2262
|
+
if (status.startsWith("D")) deletedFiles.add(firstPath);
|
|
2263
|
+
}
|
|
2264
|
+
return {
|
|
2265
|
+
changedFiles: [...changedFiles],
|
|
2266
|
+
addedFiles: [...addedFiles],
|
|
2267
|
+
deletedFiles: [...deletedFiles]
|
|
2268
|
+
};
|
|
2269
|
+
}
|
|
2270
|
+
function resolveGitDiffScope(cwd, baseRef) {
|
|
2271
|
+
const normalizedRef = baseRef.trim();
|
|
2272
|
+
if (!normalizedRef) throw new Error("--base-ref must not be empty");
|
|
2273
|
+
const baseSha = runGit(
|
|
2274
|
+
cwd,
|
|
2275
|
+
["rev-parse", "--verify", "--end-of-options", `${normalizedRef}^{commit}`],
|
|
2276
|
+
`Invalid --base-ref '${normalizedRef}'`
|
|
2277
|
+
).trim();
|
|
2278
|
+
const mergeBase = runGit(
|
|
2279
|
+
cwd,
|
|
2280
|
+
["merge-base", baseSha, "HEAD"],
|
|
2281
|
+
`Cannot determine a merge base for --base-ref '${normalizedRef}'`
|
|
2282
|
+
).trim();
|
|
2283
|
+
const committed = parseNameStatus(runGit(
|
|
2284
|
+
cwd,
|
|
2285
|
+
["diff", "--name-status", "-z", "--find-renames", `${mergeBase}..HEAD`],
|
|
2286
|
+
`Cannot inspect committed changes from --base-ref '${normalizedRef}'`
|
|
2287
|
+
));
|
|
2288
|
+
const workingTree = parseNameStatus(runGit(
|
|
2289
|
+
cwd,
|
|
2290
|
+
["diff", "--name-status", "-z", "--find-renames", "HEAD"],
|
|
2291
|
+
"Cannot inspect uncommitted changes"
|
|
2292
|
+
));
|
|
2293
|
+
const untracked = runGit(
|
|
2294
|
+
cwd,
|
|
2295
|
+
["ls-files", "--others", "--exclude-standard", "-z"],
|
|
2296
|
+
"Cannot inspect untracked files"
|
|
2297
|
+
).split("\0").filter(Boolean);
|
|
2298
|
+
const changedFiles = /* @__PURE__ */ new Set([...committed.changedFiles, ...workingTree.changedFiles, ...untracked]);
|
|
2299
|
+
const addedFiles = /* @__PURE__ */ new Set([...committed.addedFiles, ...workingTree.addedFiles, ...untracked]);
|
|
2300
|
+
const deletedFiles = /* @__PURE__ */ new Set([...committed.deletedFiles, ...workingTree.deletedFiles]);
|
|
2301
|
+
for (const file of addedFiles) {
|
|
2302
|
+
if (deletedFiles.delete(file)) addedFiles.delete(file);
|
|
2303
|
+
}
|
|
2304
|
+
return {
|
|
2305
|
+
changedFiles: [...changedFiles].sort(),
|
|
2306
|
+
addedFiles: [...addedFiles].sort(),
|
|
2307
|
+
deletedFiles: [...deletedFiles].sort()
|
|
2308
|
+
};
|
|
2309
|
+
}
|
|
2220
2310
|
function resolvePoliciesFromLegacyMode(mode, evidencePolicy) {
|
|
2221
2311
|
const activePolicy = evidencePolicy || (mode === "full" ? "required" : "optional");
|
|
2222
2312
|
let risk = "medium";
|
|
@@ -2246,7 +2336,8 @@ async function runCollectEvidence({
|
|
|
2246
2336
|
claimedOutcome = null,
|
|
2247
2337
|
deliveryChangedFiles = null,
|
|
2248
2338
|
deliveryAddedFiles = null,
|
|
2249
|
-
deliveryDeletedFiles = null
|
|
2339
|
+
deliveryDeletedFiles = null,
|
|
2340
|
+
baseRef = null
|
|
2250
2341
|
}) {
|
|
2251
2342
|
if (process.env.AI_WORKFLOW_FINALIZATION_OWNER === "execute") {
|
|
2252
2343
|
console.log("[FINALIZATION DEFERRED] Parent ai-workflow execute process owns canonical finalization.");
|
|
@@ -2254,7 +2345,8 @@ async function runCollectEvidence({
|
|
|
2254
2345
|
}
|
|
2255
2346
|
const policies = resolvePoliciesFromLegacyMode(mode, evidencePolicy);
|
|
2256
2347
|
const workspaceGuard = new QualityGuard({ cwd, taskSlug, evidencePolicy: policies.evidencePolicy, riskLevel: policies.riskLevel });
|
|
2257
|
-
const
|
|
2348
|
+
const gitDiffScope = deliveryChangedFiles === null && baseRef ? resolveGitDiffScope(cwd, baseRef) : null;
|
|
2349
|
+
const observedDeliveryFiles = deliveryChangedFiles ?? gitDiffScope?.changedFiles ?? workspaceGuard.getChangedFiles();
|
|
2258
2350
|
const qualityGuard = new QualityGuard({
|
|
2259
2351
|
cwd,
|
|
2260
2352
|
taskSlug,
|
|
@@ -2291,8 +2383,8 @@ async function runCollectEvidence({
|
|
|
2291
2383
|
workflowEvidence,
|
|
2292
2384
|
claimedOutcome,
|
|
2293
2385
|
deliveryChangedFiles: observedDeliveryFiles,
|
|
2294
|
-
deliveryAddedFiles,
|
|
2295
|
-
deliveryDeletedFiles,
|
|
2386
|
+
deliveryAddedFiles: deliveryAddedFiles ?? gitDiffScope?.addedFiles,
|
|
2387
|
+
deliveryDeletedFiles: deliveryDeletedFiles ?? gitDiffScope?.deletedFiles,
|
|
2296
2388
|
riskLevel: policies.riskLevel
|
|
2297
2389
|
});
|
|
2298
2390
|
const evidence = await collector.collect(tasks, { writeArtifact: persist });
|
|
@@ -2307,7 +2399,7 @@ async function runCollectEvidence({
|
|
|
2307
2399
|
}
|
|
2308
2400
|
|
|
2309
2401
|
// src/core/healing/cli-remediation-executor.ts
|
|
2310
|
-
import { spawnSync, execSync as execSync2 } from "child_process";
|
|
2402
|
+
import { spawnSync as spawnSync2, execSync as execSync2 } from "child_process";
|
|
2311
2403
|
import fs8 from "fs/promises";
|
|
2312
2404
|
import path10 from "path";
|
|
2313
2405
|
async function readScripts(cwd) {
|
|
@@ -2371,7 +2463,7 @@ function createCliRemediationExecutor(cwd) {
|
|
|
2371
2463
|
process.stdout.write(` \u2192 npm run ${scriptName} ... `);
|
|
2372
2464
|
const isWin32 = process.platform === "win32";
|
|
2373
2465
|
const cmd = isWin32 ? "npm.cmd" : "npm";
|
|
2374
|
-
const result =
|
|
2466
|
+
const result = spawnSync2(cmd, ["run", scriptName], {
|
|
2375
2467
|
cwd,
|
|
2376
2468
|
shell: false,
|
|
2377
2469
|
encoding: "utf8",
|
|
@@ -2440,6 +2532,8 @@ function stripMarkdownFence(content) {
|
|
|
2440
2532
|
}
|
|
2441
2533
|
|
|
2442
2534
|
// src/core/sdd/approval-receipt.ts
|
|
2535
|
+
var Ajv = import_ajv.default;
|
|
2536
|
+
var addFormats = import_ajv_formats.default;
|
|
2443
2537
|
var ApprovalReceiptValidator = class {
|
|
2444
2538
|
cwd;
|
|
2445
2539
|
constructor({ cwd = process.cwd() } = {}) {
|
|
@@ -2463,8 +2557,8 @@ var ApprovalReceiptValidator = class {
|
|
|
2463
2557
|
receipt = JSON.parse(await fs10.readFile(absoluteReceiptPath, "utf8"));
|
|
2464
2558
|
const root = getPackageRoot();
|
|
2465
2559
|
const schema = JSON.parse(await fs10.readFile(path12.join(root, "dist-assets/schemas/approval-receipt.schema.json"), "utf8"));
|
|
2466
|
-
const ajv = new
|
|
2467
|
-
(
|
|
2560
|
+
const ajv = new Ajv({ allErrors: true });
|
|
2561
|
+
addFormats(ajv);
|
|
2468
2562
|
const validateSchema = ajv.compile(schema);
|
|
2469
2563
|
if (!validateSchema(receipt)) {
|
|
2470
2564
|
const errors = (validateSchema.errors || []).map((error) => `${error.instancePath} ${error.message}`).join(", ");
|
|
@@ -2488,6 +2582,7 @@ var ApprovalReceiptValidator = class {
|
|
|
2488
2582
|
var import_ajv2 = __toESM(require_ajv(), 1);
|
|
2489
2583
|
import fs11 from "fs/promises";
|
|
2490
2584
|
import path13 from "path";
|
|
2585
|
+
var Ajv2 = import_ajv2.default;
|
|
2491
2586
|
function isContained(root, candidate) {
|
|
2492
2587
|
return candidate === root || candidate.startsWith(`${root}${path13.sep}`);
|
|
2493
2588
|
}
|
|
@@ -2542,7 +2637,7 @@ var TechnicalPlanValidator = class {
|
|
|
2542
2637
|
}
|
|
2543
2638
|
try {
|
|
2544
2639
|
const schema = JSON.parse(await fs11.readFile(path13.join(getPackageRoot(), "dist-assets/schemas/technical-plan.schema.json"), "utf8"));
|
|
2545
|
-
const validateSchema = new
|
|
2640
|
+
const validateSchema = new Ajv2({ allErrors: true }).compile(schema);
|
|
2546
2641
|
if (!validateSchema(plan)) {
|
|
2547
2642
|
const errors = (validateSchema.errors || []).map((error) => `${error.instancePath} ${error.message}`).join(", ");
|
|
2548
2643
|
return { valid: false, reason: `Technical plan schema violation: ${errors}` };
|
|
@@ -3351,7 +3446,7 @@ async function runFastTrackValidation(cwd, result, checkBranchSafety) {
|
|
|
3351
3446
|
const evidencePath = path16.join(cwd, "EVIDENCE.json");
|
|
3352
3447
|
const hasEvidence = await fs14.access(evidencePath).then(() => true).catch(() => false);
|
|
3353
3448
|
if (hasEvidence) {
|
|
3354
|
-
const { EvidenceValidator: EvidenceValidator2 } = await import("../evidence-validator-
|
|
3449
|
+
const { EvidenceValidator: EvidenceValidator2 } = await import("../evidence-validator-6GD442JE.js");
|
|
3355
3450
|
const isTest = process.env.NODE_ENV === "test" || process.env.VITEST === "true";
|
|
3356
3451
|
const evidenceValidator = new EvidenceValidator2({ cwd, skipFileCheck: isTest, skipGitCheck: isTest });
|
|
3357
3452
|
const valResult = await evidenceValidator.validate();
|
|
@@ -3662,7 +3757,7 @@ Usage:
|
|
|
3662
3757
|
ai-workflow run --spec-path=<path> [--approval-path=<path>] [--runtime=<opencode|codex>]
|
|
3663
3758
|
ai-workflow init [--yes] [--force] [--dry-run] [--no-install] [--no-overwrite] [--claude] [--codex] [--antigravity] [--profile=<profile>]
|
|
3664
3759
|
ai-workflow validate [--a11y] [--visual-dist=<path>] [--port=<port>]
|
|
3665
|
-
ai-workflow collect-evidence [--task=<slug>] [--evidence-policy=<optional|required>] [--mode=<quick|standard|full>] [--dry-run] [--visual-dist=<path>] [--port=<port>]
|
|
3760
|
+
ai-workflow collect-evidence [--task=<slug>] [--evidence-policy=<optional|required>] [--mode=<quick|standard|full>] [--base-ref=<git-ref>] [--dry-run] [--visual-dist=<path>] [--port=<port>]
|
|
3666
3761
|
ai-workflow doctor
|
|
3667
3762
|
ai-workflow clean [--yes] [--dry-run] [--purge-agents]
|
|
3668
3763
|
ai-workflow lint skills
|
|
@@ -3686,6 +3781,7 @@ function parseFlags(args) {
|
|
|
3686
3781
|
const taskArg = args.find((arg) => arg.startsWith("--task="));
|
|
3687
3782
|
const modeArg = args.find((arg) => arg.startsWith("--mode="));
|
|
3688
3783
|
const evidencePolicyArg = args.find((arg) => arg.startsWith("--evidence-policy="));
|
|
3784
|
+
const baseRefArg = args.find((arg) => arg.startsWith("--base-ref="));
|
|
3689
3785
|
const requestArg = args.find((arg) => arg.startsWith("--request="));
|
|
3690
3786
|
const visualDistArg = args.find((arg) => arg.startsWith("--visual-dist="));
|
|
3691
3787
|
const portArg = args.find((arg) => arg.startsWith("--port="));
|
|
@@ -3708,6 +3804,7 @@ function parseFlags(args) {
|
|
|
3708
3804
|
taskSlug: taskArg ? taskArg.replace("--task=", "") : void 0,
|
|
3709
3805
|
mode: modeArg ? modeArg.replace("--mode=", "") : void 0,
|
|
3710
3806
|
evidencePolicy: evidencePolicyArg ? evidencePolicyArg.replace("--evidence-policy=", "") : void 0,
|
|
3807
|
+
baseRef: baseRefArg ? baseRefArg.replace("--base-ref=", "") : void 0,
|
|
3711
3808
|
request: requestArg ? requestArg.replace("--request=", "") : void 0,
|
|
3712
3809
|
visualDist: visualDistArg ? visualDistArg.replace("--visual-dist=", "") : void 0,
|
|
3713
3810
|
port: portArg ? portArg.replace("--port=", "") : void 0,
|
|
@@ -3751,13 +3848,14 @@ var commandMap = {
|
|
|
3751
3848
|
taskSlug: flags.taskSlug,
|
|
3752
3849
|
mode: flags.mode,
|
|
3753
3850
|
evidencePolicy: flags.evidencePolicy,
|
|
3851
|
+
baseRef: flags.baseRef,
|
|
3754
3852
|
dryRun: flags.dryRun,
|
|
3755
3853
|
visualDist: flags.visualDist,
|
|
3756
3854
|
port: flags.port
|
|
3757
3855
|
});
|
|
3758
3856
|
},
|
|
3759
3857
|
validate: async (_, flags) => {
|
|
3760
|
-
const { runValidate } = await import("../validate-
|
|
3858
|
+
const { runValidate } = await import("../validate-YMBSICJL.js");
|
|
3761
3859
|
await runValidate({
|
|
3762
3860
|
cwd: process.cwd(),
|
|
3763
3861
|
taskSlug: flags.taskSlug,
|