@llblab/pi-actors 0.45.0 → 0.46.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.
- package/AGENTS.md +10 -6
- package/CHANGELOG.md +14 -1
- package/README.md +28 -12
- package/dist/fixtures/protocol/recipe-summary.json +1 -1
- package/dist/index.d.ts +1 -2
- package/dist/index.js +10 -110
- package/dist/lib/async-runs.d.ts +4 -1
- package/dist/lib/async-runs.js +22 -23
- package/dist/lib/automatic-review-runtime.d.ts +2 -0
- package/dist/lib/automatic-review-runtime.js +12 -5
- package/dist/lib/draft-sleep.js +0 -1
- package/dist/lib/extension-runtime.d.ts +21 -0
- package/dist/lib/extension-runtime.js +122 -0
- package/dist/lib/inspector.js +45 -7
- package/dist/lib/paths.d.ts +0 -1
- package/dist/lib/paths.js +0 -7
- package/dist/lib/prompts.d.ts +1 -1
- package/dist/lib/prompts.js +2 -2
- package/dist/lib/recipes-context.js +2 -2
- package/dist/lib/recipes-references.d.ts +24 -9
- package/dist/lib/recipes-references.js +152 -126
- package/dist/lib/runtime.d.ts +0 -1
- package/dist/lib/runtime.js +1 -3
- package/dist/lib/tools-inspect.d.ts +0 -1
- package/dist/lib/tools-inspect.js +35 -8
- package/dist/lib/tools-local.d.ts +2 -0
- package/dist/lib/tools-local.js +1 -1
- package/dist/lib/tools-spawn.d.ts +2 -0
- package/dist/lib/tools-spawn.js +24 -52
- package/dist/scripts/async-runner.mjs +0 -16
- package/dist/scripts/build-dist.mjs +13 -4
- package/dist/skills/actors/SKILL.md +6 -6
- package/dist/skills/actors/recipes/recipe-validate.json +11 -0
- package/{recipes/utility-coordinator-lock-snapshot.json → dist/skills/actors/recipes/resource-locker-snapshot.json} +1 -3
- package/dist/{recipes → skills/actors/recipes}/resource-locker.json +1 -3
- package/dist/{recipes/utility-run-ops-snapshot.json → skills/actors/recipes/run-ops-snapshot.json} +1 -3
- package/dist/skills/actors/recipes/run-summary.json +9 -0
- package/dist/{scripts/locker.mjs → skills/actors/scripts/resource-locker.mjs} +2 -23
- package/dist/skills/actors/scripts/run-utils.mjs +164 -0
- package/{scripts → dist/skills/actors/scripts}/validate-recipe.mjs +134 -18
- package/dist/skills/artifacts/SKILL.md +16 -0
- package/{recipes/pipeline-artifact-bundle.json → dist/skills/artifacts/recipes/bundle.json} +4 -4
- package/dist/{recipes/utility-artifact-write.json → skills/artifacts/recipes/file-write.json} +1 -3
- package/dist/{recipes/utility-artifact-manifest.json → skills/artifacts/recipes/manifest.json} +1 -3
- package/{recipes/pipeline-artifact-report.json → dist/skills/artifacts/recipes/report.json} +2 -2
- package/dist/{recipes/pipeline-artifact-write.json → skills/artifacts/recipes/write.json} +3 -3
- package/dist/skills/artifacts/scripts/artifact-utils.mjs +63 -0
- package/dist/skills/media/SKILL.md +16 -0
- package/dist/{recipes/pipeline-media-library.json → skills/media/recipes/library.json} +2 -2
- package/{recipes/music-player.json → dist/skills/media/recipes/player.json} +1 -3
- package/dist/{recipes/utility-playlist-build.json → skills/media/recipes/playlist-build.json} +1 -3
- package/dist/skills/media/scripts/media-utils.mjs +47 -0
- package/dist/{scripts → skills/media/scripts}/music-player.mjs +9 -3
- package/dist/skills/project-work/SKILL.md +16 -0
- package/dist/skills/project-work/recipes/changelog-section.json +11 -0
- package/{recipes/pipeline-docs-maintenance.json → dist/skills/project-work/recipes/docs-maintenance.json} +4 -4
- package/dist/skills/project-work/recipes/package-summary.json +9 -0
- package/{recipes/pipeline-release-readiness.json → dist/skills/project-work/recipes/release-readiness.json} +6 -6
- package/dist/{recipes/pipeline-release-summary.json → skills/project-work/recipes/release-summary.json} +5 -5
- package/{recipes/pipeline-repo-health.json → dist/skills/project-work/recipes/repo-health.json} +6 -6
- package/{recipes/pipeline-async-run-ops.json → dist/skills/project-work/recipes/run-ops.json} +3 -3
- package/dist/skills/project-work/recipes/skill-summary.json +11 -0
- package/dist/skills/project-work/scripts/project-utils.mjs +89 -0
- package/dist/skills/recipe-memory/SKILL.md +16 -0
- package/dist/skills/swarm/SKILL.md +4 -4
- package/{recipes/pipeline-architect-coordinator.json → dist/skills/swarm/recipes/architect.json} +5 -5
- package/{recipes/pipeline-checkpoint-continuation.json → dist/skills/swarm/recipes/checkpoint-continuation.json} +3 -3
- package/dist/{recipes/pipeline-development-tasking.json → skills/swarm/recipes/development-tasking.json} +4 -4
- package/dist/{recipes/lens-swarm.json → skills/swarm/recipes/lens-review.json} +1 -1
- package/dist/{recipes/pipeline-quorum-review.json → skills/swarm/recipes/quorum-review.json} +4 -4
- package/{recipes/pipeline-research-synthesis.json → dist/skills/swarm/recipes/research-synthesis.json} +6 -6
- package/{recipes/pipeline-review-readiness.json → dist/skills/swarm/recipes/review-readiness.json} +1 -1
- package/{recipes/subagents-prompts.json → dist/skills/swarm/recipes/subagent-prompts.json} +1 -1
- package/{recipes → dist/skills/swarm/recipes}/subagent-quorum.json +1 -1
- package/dist/{recipes → skills/swarm/recipes}/subagent-review-coordinator.json +6 -6
- package/docs/actor-inspector.md +4 -3
- package/docs/async-runs.md +2 -2
- package/docs/recipe-library.md +23 -23
- package/docs/releasing.md +2 -2
- package/docs/template-recipes.md +30 -14
- package/docs/tool-registry.md +2 -2
- package/fixtures/protocol/recipe-summary.json +1 -1
- package/index.ts +18 -112
- package/lib/async-runs.ts +47 -24
- package/lib/automatic-review-runtime.ts +16 -3
- package/lib/draft-sleep.ts +0 -1
- package/lib/extension-runtime.ts +158 -0
- package/lib/inspector.ts +56 -11
- package/lib/paths.ts +0 -7
- package/lib/prompts.ts +2 -2
- package/lib/recipes-context.ts +2 -2
- package/lib/recipes-references.ts +223 -134
- package/lib/runtime.ts +1 -5
- package/lib/tools-inspect.ts +42 -9
- package/lib/tools-local.ts +2 -0
- package/lib/tools-spawn.ts +37 -74
- package/package.json +3 -5
- package/scripts/async-runner.mjs +0 -16
- package/scripts/build-dist.mjs +13 -4
- package/skills/actors/SKILL.md +6 -6
- package/skills/actors/recipes/recipe-validate.json +11 -0
- package/{dist/recipes/utility-coordinator-lock-snapshot.json → skills/actors/recipes/resource-locker-snapshot.json} +1 -3
- package/{recipes → skills/actors/recipes}/resource-locker.json +1 -3
- package/{recipes/utility-run-ops-snapshot.json → skills/actors/recipes/run-ops-snapshot.json} +1 -3
- package/skills/actors/recipes/run-summary.json +9 -0
- package/{scripts/locker.mjs → skills/actors/scripts/resource-locker.mjs} +2 -23
- package/skills/actors/scripts/run-utils.mjs +164 -0
- package/{dist → skills/actors}/scripts/validate-recipe.mjs +134 -18
- package/skills/artifacts/SKILL.md +16 -0
- package/{dist/recipes/pipeline-artifact-bundle.json → skills/artifacts/recipes/bundle.json} +4 -4
- package/{recipes/utility-artifact-write.json → skills/artifacts/recipes/file-write.json} +1 -3
- package/{recipes/utility-artifact-manifest.json → skills/artifacts/recipes/manifest.json} +1 -3
- package/{dist/recipes/pipeline-artifact-report.json → skills/artifacts/recipes/report.json} +2 -2
- package/{recipes/pipeline-artifact-write.json → skills/artifacts/recipes/write.json} +3 -3
- package/skills/artifacts/scripts/artifact-utils.mjs +63 -0
- package/skills/media/SKILL.md +16 -0
- package/{recipes/pipeline-media-library.json → skills/media/recipes/library.json} +2 -2
- package/{dist/recipes/music-player.json → skills/media/recipes/player.json} +1 -3
- package/{recipes/utility-playlist-build.json → skills/media/recipes/playlist-build.json} +1 -3
- package/skills/media/scripts/media-utils.mjs +47 -0
- package/{scripts → skills/media/scripts}/music-player.mjs +9 -3
- package/skills/project-work/SKILL.md +16 -0
- package/skills/project-work/recipes/changelog-section.json +11 -0
- package/{dist/recipes/pipeline-docs-maintenance.json → skills/project-work/recipes/docs-maintenance.json} +4 -4
- package/skills/project-work/recipes/package-summary.json +9 -0
- package/{dist/recipes/pipeline-release-readiness.json → skills/project-work/recipes/release-readiness.json} +6 -6
- package/{recipes/pipeline-release-summary.json → skills/project-work/recipes/release-summary.json} +5 -5
- package/{dist/recipes/pipeline-repo-health.json → skills/project-work/recipes/repo-health.json} +6 -6
- package/{dist/recipes/pipeline-async-run-ops.json → skills/project-work/recipes/run-ops.json} +3 -3
- package/skills/project-work/recipes/skill-summary.json +11 -0
- package/skills/project-work/scripts/project-utils.mjs +89 -0
- package/skills/recipe-memory/SKILL.md +16 -0
- package/skills/swarm/SKILL.md +4 -4
- package/{dist/recipes/pipeline-architect-coordinator.json → skills/swarm/recipes/architect.json} +5 -5
- package/{dist/recipes/pipeline-checkpoint-continuation.json → skills/swarm/recipes/checkpoint-continuation.json} +3 -3
- package/{recipes/pipeline-development-tasking.json → skills/swarm/recipes/development-tasking.json} +4 -4
- package/{recipes/lens-swarm.json → skills/swarm/recipes/lens-review.json} +1 -1
- package/{recipes/pipeline-quorum-review.json → skills/swarm/recipes/quorum-review.json} +4 -4
- package/{dist/recipes/pipeline-research-synthesis.json → skills/swarm/recipes/research-synthesis.json} +6 -6
- package/{dist/recipes/pipeline-review-readiness.json → skills/swarm/recipes/review-readiness.json} +1 -1
- package/{dist/recipes/subagents-prompts.json → skills/swarm/recipes/subagent-prompts.json} +1 -1
- package/{dist → skills/swarm}/recipes/subagent-quorum.json +1 -1
- package/{recipes → skills/swarm/recipes}/subagent-review-coordinator.json +6 -6
- package/dist/recipes/utility-changelog-section.json +0 -13
- package/dist/recipes/utility-package-summary.json +0 -11
- package/dist/recipes/utility-run-summary.json +0 -11
- package/dist/recipes/utility-skill-summary.json +0 -13
- package/dist/recipes/utility-validate-recipe.json +0 -13
- package/dist/scripts/recipe-utils.mjs +0 -405
- package/dist/scripts/release-gates.mjs +0 -278
- package/recipes/utility-changelog-section.json +0 -13
- package/recipes/utility-package-summary.json +0 -11
- package/recipes/utility-run-summary.json +0 -11
- package/recipes/utility-skill-summary.json +0 -13
- package/recipes/utility-validate-recipe.json +0 -13
- package/scripts/recipe-utils.mjs +0 -405
- package/scripts/release-gates.mjs +0 -278
- /package/dist/{recipes/utility-validation-wrapper.json → skills/actors/recipes/command-validate.json} +0 -0
- /package/dist/{recipes/utility-jsonl-tail.json → skills/actors/recipes/jsonl-tail.json} +0 -0
- /package/dist/{recipes/utility-run-state-files.json → skills/actors/recipes/run-state-files.json} +0 -0
- /package/dist/{recipes/utility-playlist-scan.json → skills/media/recipes/playlist-scan.json} +0 -0
- /package/dist/{recipes/utility-changelog-head.json → skills/project-work/recipes/changelog-head.json} +0 -0
- /package/dist/{recipes/utility-git-log.json → skills/project-work/recipes/git-log.json} +0 -0
- /package/dist/{recipes/utility-git-status.json → skills/project-work/recipes/git-status.json} +0 -0
- /package/dist/{recipes/utility-markdown-index.json → skills/project-work/recipes/markdown-index.json} +0 -0
- /package/dist/{recipes → skills/recipe-memory/recipes}/draft-review.json +0 -0
- /package/dist/{recipes → skills/recipe-memory/recipes}/tool-review.json +0 -0
- /package/dist/{recipes → skills/swarm/recipes}/subagent-artifact.json +0 -0
- /package/dist/{recipes → skills/swarm/recipes}/subagent-checkpoint.json +0 -0
- /package/dist/{recipes → skills/swarm/recipes}/subagent-conflict-report.json +0 -0
- /package/dist/{recipes → skills/swarm/recipes}/subagent-contradiction-map.json +0 -0
- /package/dist/{recipes → skills/swarm/recipes}/subagent-critic.json +0 -0
- /package/dist/{recipes → skills/swarm/recipes}/subagent-evidence-map.json +0 -0
- /package/dist/{recipes → skills/swarm/recipes}/subagent-followup.json +0 -0
- /package/dist/{recipes → skills/swarm/recipes}/subagent-judge.json +0 -0
- /package/dist/{recipes → skills/swarm/recipes}/subagent-merge.json +0 -0
- /package/dist/{recipes → skills/swarm/recipes}/subagent-normalize.json +0 -0
- /package/dist/{recipes → skills/swarm/recipes}/subagent-plan.json +0 -0
- /package/dist/{recipes → skills/swarm/recipes}/subagent-preflight.json +0 -0
- /package/dist/{recipes → skills/swarm/recipes}/subagent-prompt.json +0 -0
- /package/dist/{recipes → skills/swarm/recipes}/subagent-review.json +0 -0
- /package/dist/{recipes → skills/swarm/recipes}/subagent-task-card.json +0 -0
- /package/dist/{recipes → skills/swarm/recipes}/subagent-tools.json +0 -0
- /package/dist/{recipes → skills/swarm/recipes}/subagent-verify.json +0 -0
- /package/{recipes/utility-validation-wrapper.json → skills/actors/recipes/command-validate.json} +0 -0
- /package/{recipes/utility-jsonl-tail.json → skills/actors/recipes/jsonl-tail.json} +0 -0
- /package/{recipes/utility-run-state-files.json → skills/actors/recipes/run-state-files.json} +0 -0
- /package/{recipes/utility-playlist-scan.json → skills/media/recipes/playlist-scan.json} +0 -0
- /package/{recipes/utility-changelog-head.json → skills/project-work/recipes/changelog-head.json} +0 -0
- /package/{recipes/utility-git-log.json → skills/project-work/recipes/git-log.json} +0 -0
- /package/{recipes/utility-git-status.json → skills/project-work/recipes/git-status.json} +0 -0
- /package/{recipes/utility-markdown-index.json → skills/project-work/recipes/markdown-index.json} +0 -0
- /package/{recipes → skills/recipe-memory/recipes}/draft-review.json +0 -0
- /package/{recipes → skills/recipe-memory/recipes}/tool-review.json +0 -0
- /package/{recipes → skills/swarm/recipes}/subagent-artifact.json +0 -0
- /package/{recipes → skills/swarm/recipes}/subagent-checkpoint.json +0 -0
- /package/{recipes → skills/swarm/recipes}/subagent-conflict-report.json +0 -0
- /package/{recipes → skills/swarm/recipes}/subagent-contradiction-map.json +0 -0
- /package/{recipes → skills/swarm/recipes}/subagent-critic.json +0 -0
- /package/{recipes → skills/swarm/recipes}/subagent-evidence-map.json +0 -0
- /package/{recipes → skills/swarm/recipes}/subagent-followup.json +0 -0
- /package/{recipes → skills/swarm/recipes}/subagent-judge.json +0 -0
- /package/{recipes → skills/swarm/recipes}/subagent-merge.json +0 -0
- /package/{recipes → skills/swarm/recipes}/subagent-normalize.json +0 -0
- /package/{recipes → skills/swarm/recipes}/subagent-plan.json +0 -0
- /package/{recipes → skills/swarm/recipes}/subagent-preflight.json +0 -0
- /package/{recipes → skills/swarm/recipes}/subagent-prompt.json +0 -0
- /package/{recipes → skills/swarm/recipes}/subagent-review.json +0 -0
- /package/{recipes → skills/swarm/recipes}/subagent-task-card.json +0 -0
- /package/{recipes → skills/swarm/recipes}/subagent-tools.json +0 -0
- /package/{recipes → skills/swarm/recipes}/subagent-verify.json +0 -0
package/scripts/recipe-utils.mjs
DELETED
|
@@ -1,405 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Recipe utility command bundle.
|
|
5
|
-
* Zones: recipe utilities, deterministic helper subcommands
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
// @ts-nocheck
|
|
9
|
-
|
|
10
|
-
import {
|
|
11
|
-
appendFileSync,
|
|
12
|
-
existsSync,
|
|
13
|
-
mkdirSync,
|
|
14
|
-
readFileSync,
|
|
15
|
-
readdirSync,
|
|
16
|
-
statSync,
|
|
17
|
-
writeFileSync,
|
|
18
|
-
} from "node:fs";
|
|
19
|
-
import { basename, dirname, extname, join, relative, resolve, sep } from "node:path";
|
|
20
|
-
|
|
21
|
-
function usage() {
|
|
22
|
-
console.error(`Usage:
|
|
23
|
-
recipe-utils.mjs run-summary <state-root>
|
|
24
|
-
recipe-utils.mjs run-ops-snapshot <state-root> <run-id> [lines] [stale-minutes]
|
|
25
|
-
recipe-utils.mjs playlist <source-dir> [extensions] [max-depth] [paths|m3u|inline]
|
|
26
|
-
recipe-utils.mjs changelog-section <file> <version>
|
|
27
|
-
recipe-utils.mjs artifact-manifest <artifact-path> <title> <status> [summary]
|
|
28
|
-
recipe-utils.mjs artifact-write <artifact-path> [create|overwrite|append]
|
|
29
|
-
recipe-utils.mjs package-summary <package-json>
|
|
30
|
-
recipe-utils.mjs skill-summary <skill-md> [package-json]`);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function fail(message) {
|
|
34
|
-
console.error(message);
|
|
35
|
-
process.exit(1);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function walkFiles(dir, maxDepth = 2, depth = 0, out = []) {
|
|
39
|
-
if (depth > maxDepth || !existsSync(dir)) return out;
|
|
40
|
-
for (const entry of readdirSync(dir)) {
|
|
41
|
-
const path = join(dir, entry);
|
|
42
|
-
const stat = statSync(path);
|
|
43
|
-
if (stat.isDirectory()) walkFiles(path, maxDepth, depth + 1, out);
|
|
44
|
-
else if (stat.isFile()) out.push(path);
|
|
45
|
-
}
|
|
46
|
-
return out;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function readJson(file) {
|
|
50
|
-
if (!existsSync(file)) return undefined;
|
|
51
|
-
try {
|
|
52
|
-
return JSON.parse(readFileSync(file, "utf8"));
|
|
53
|
-
} catch {
|
|
54
|
-
return undefined;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function getRunStatus(run, progress, result) {
|
|
59
|
-
if (progress?.phase) return progress.phase;
|
|
60
|
-
if (result?.code !== undefined) return result.code === 0 ? "done" : "failed";
|
|
61
|
-
return run.status ?? "unknown";
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function collectRunSummary(rootValue) {
|
|
65
|
-
const root = resolve(
|
|
66
|
-
rootValue.replace(/^~(?=\/|$)/, process.env.HOME ?? "~"),
|
|
67
|
-
);
|
|
68
|
-
const files = walkFiles(root, 2).filter((file) => basename(file) === "run.json");
|
|
69
|
-
const rows = [];
|
|
70
|
-
for (const file of files) {
|
|
71
|
-
const run = readJson(file);
|
|
72
|
-
if (!run) {
|
|
73
|
-
rows.push({
|
|
74
|
-
run: relative(root, file),
|
|
75
|
-
status: "invalid-json",
|
|
76
|
-
recipe: "",
|
|
77
|
-
updated: "",
|
|
78
|
-
});
|
|
79
|
-
continue;
|
|
80
|
-
}
|
|
81
|
-
const runDir = dirname(file);
|
|
82
|
-
const progress = readJson(join(runDir, "progress.json"));
|
|
83
|
-
const result = readJson(join(runDir, "result.json"));
|
|
84
|
-
rows.push({
|
|
85
|
-
run: run.run_id ?? run.run ?? relative(root, file).split(sep)[0],
|
|
86
|
-
status: getRunStatus(run, progress, result),
|
|
87
|
-
recipe: run.recipe ?? run.recipe_file ?? "",
|
|
88
|
-
updated:
|
|
89
|
-
progress?.updatedAt ??
|
|
90
|
-
result?.completedAt ??
|
|
91
|
-
run.updated_at ??
|
|
92
|
-
run.completed_at ??
|
|
93
|
-
run.started_at ??
|
|
94
|
-
"",
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
rows.sort((a, b) =>
|
|
98
|
-
`${a.status}:${a.run}`.localeCompare(`${b.status}:${b.run}`),
|
|
99
|
-
);
|
|
100
|
-
return rows;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
function runSummary(rootValue) {
|
|
104
|
-
console.log(JSON.stringify(collectRunSummary(rootValue), null, 2));
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
function tailJsonl(fileValue, linesValue = "80") {
|
|
108
|
-
const file = resolve(
|
|
109
|
-
fileValue.replace(/^~(?=\/|$)/, process.env.HOME ?? "~"),
|
|
110
|
-
);
|
|
111
|
-
if (!existsSync(file)) return [];
|
|
112
|
-
const lines = Number.parseInt(linesValue, 10);
|
|
113
|
-
const count = Number.isFinite(lines) && lines > 0 ? lines : 80;
|
|
114
|
-
return readFileSync(file, "utf8")
|
|
115
|
-
.trimEnd()
|
|
116
|
-
.split("\n")
|
|
117
|
-
.filter(Boolean)
|
|
118
|
-
.slice(-count)
|
|
119
|
-
.map((line) => {
|
|
120
|
-
try {
|
|
121
|
-
return JSON.parse(line);
|
|
122
|
-
} catch {
|
|
123
|
-
return { raw: line };
|
|
124
|
-
}
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
function runOpsSnapshot(
|
|
129
|
-
rootValue,
|
|
130
|
-
runIdValue = "music",
|
|
131
|
-
linesValue = "80",
|
|
132
|
-
staleMinutesValue = "60",
|
|
133
|
-
) {
|
|
134
|
-
const runs = collectRunSummary(rootValue);
|
|
135
|
-
const root = resolve(
|
|
136
|
-
rootValue.replace(/^~(?=\/|$)/, process.env.HOME ?? "~"),
|
|
137
|
-
);
|
|
138
|
-
const inspectedRun = String(runIdValue || "music");
|
|
139
|
-
const traceFile = join(root, inspectedRun, "trace.jsonl");
|
|
140
|
-
const staleMs = Number(staleMinutesValue) * 60 * 1000;
|
|
141
|
-
const now = Date.now();
|
|
142
|
-
const recommendations = runs.flatMap((run) => {
|
|
143
|
-
const updatedMs = Date.parse(run.updated || "");
|
|
144
|
-
const stale =
|
|
145
|
-
Number.isFinite(updatedMs) &&
|
|
146
|
-
Number.isFinite(staleMs) &&
|
|
147
|
-
now - updatedMs > staleMs;
|
|
148
|
-
if (run.status === "running" && stale) {
|
|
149
|
-
return [
|
|
150
|
-
{
|
|
151
|
-
run: run.run,
|
|
152
|
-
reason: "running-stale",
|
|
153
|
-
suggested_inspect: {
|
|
154
|
-
target: `run:${run.run}`,
|
|
155
|
-
view: "control",
|
|
156
|
-
},
|
|
157
|
-
},
|
|
158
|
-
];
|
|
159
|
-
}
|
|
160
|
-
if (["failed", "exited", "killed"].includes(run.status)) {
|
|
161
|
-
return [
|
|
162
|
-
{
|
|
163
|
-
run: run.run,
|
|
164
|
-
reason: `terminal-${run.status}`,
|
|
165
|
-
suggested_inspect: { target: `run:${run.run}`, view: "trace" },
|
|
166
|
-
},
|
|
167
|
-
];
|
|
168
|
-
}
|
|
169
|
-
return [];
|
|
170
|
-
});
|
|
171
|
-
console.log(
|
|
172
|
-
JSON.stringify(
|
|
173
|
-
{
|
|
174
|
-
runs,
|
|
175
|
-
inspectedRun,
|
|
176
|
-
trace: tailJsonl(traceFile, linesValue),
|
|
177
|
-
recommendations,
|
|
178
|
-
},
|
|
179
|
-
null,
|
|
180
|
-
2,
|
|
181
|
-
),
|
|
182
|
-
);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
function playlist(
|
|
186
|
-
sourceValue,
|
|
187
|
-
extensionsValue = ".mp3,.ogg,.wav,.flac,.m4a",
|
|
188
|
-
maxDepthValue = "2",
|
|
189
|
-
outputMode = "paths",
|
|
190
|
-
) {
|
|
191
|
-
const source = resolve(
|
|
192
|
-
sourceValue.replace(/^~(?=\/|$)/, process.env.HOME ?? "~"),
|
|
193
|
-
);
|
|
194
|
-
const maxDepth = Number.parseInt(maxDepthValue, 10);
|
|
195
|
-
const extensions = new Set(
|
|
196
|
-
extensionsValue
|
|
197
|
-
.split(",")
|
|
198
|
-
.map((item) => item.trim().toLowerCase())
|
|
199
|
-
.filter(Boolean),
|
|
200
|
-
);
|
|
201
|
-
const files = walkFiles(source, Number.isFinite(maxDepth) ? maxDepth : 2)
|
|
202
|
-
.filter((file) => extensions.has(extname(file).toLowerCase()))
|
|
203
|
-
.sort((a, b) => a.localeCompare(b));
|
|
204
|
-
if (outputMode === "m3u") console.log(["#EXTM3U", ...files].join("\n"));
|
|
205
|
-
else if (outputMode === "inline") console.log(files.join("|"));
|
|
206
|
-
else console.log(files.join("\n"));
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
function artifactManifest(
|
|
210
|
-
pathValue,
|
|
211
|
-
title = "Artifact",
|
|
212
|
-
status = "draft",
|
|
213
|
-
summary = "",
|
|
214
|
-
) {
|
|
215
|
-
const path = resolve(
|
|
216
|
-
pathValue.replace(/^~(?=\/|$)/, process.env.HOME ?? "~"),
|
|
217
|
-
);
|
|
218
|
-
const exists = existsSync(path);
|
|
219
|
-
const stat = exists ? statSync(path) : undefined;
|
|
220
|
-
console.log(
|
|
221
|
-
JSON.stringify(
|
|
222
|
-
{
|
|
223
|
-
title,
|
|
224
|
-
status,
|
|
225
|
-
path,
|
|
226
|
-
exists,
|
|
227
|
-
bytes: stat?.size ?? 0,
|
|
228
|
-
modified: stat?.mtime?.toISOString?.() ?? null,
|
|
229
|
-
summary,
|
|
230
|
-
},
|
|
231
|
-
null,
|
|
232
|
-
2,
|
|
233
|
-
),
|
|
234
|
-
);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
function artifactWrite(pathValue, mode = "create") {
|
|
238
|
-
const path = resolve(
|
|
239
|
-
pathValue.replace(/^~(?=\/|$)/, process.env.HOME ?? "~"),
|
|
240
|
-
);
|
|
241
|
-
if (!["create", "overwrite", "append"].includes(mode)) {
|
|
242
|
-
fail(`Invalid artifact write mode: ${mode}`);
|
|
243
|
-
}
|
|
244
|
-
if (mode === "create" && existsSync(path)) {
|
|
245
|
-
fail(`Artifact already exists: ${path}`);
|
|
246
|
-
}
|
|
247
|
-
const content = readFileSync(0, "utf8");
|
|
248
|
-
mkdirSync(dirname(path), { recursive: true });
|
|
249
|
-
if (mode === "append") appendFileSync(path, content);
|
|
250
|
-
else writeFileSync(path, content, "utf8");
|
|
251
|
-
const stat = statSync(path);
|
|
252
|
-
console.log(
|
|
253
|
-
JSON.stringify({ path, mode, bytes: stat.size, written: true }, null, 2),
|
|
254
|
-
);
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
function packageSummary(fileValue = "package.json") {
|
|
258
|
-
const file = resolve(
|
|
259
|
-
fileValue.replace(/^~(?=\/|$)/, process.env.HOME ?? "~"),
|
|
260
|
-
);
|
|
261
|
-
const pkg = readJson(file);
|
|
262
|
-
if (!pkg) fail(`Package JSON not found or invalid: ${fileValue}`);
|
|
263
|
-
const scripts =
|
|
264
|
-
pkg.scripts && typeof pkg.scripts === "object"
|
|
265
|
-
? Object.keys(pkg.scripts).sort()
|
|
266
|
-
: [];
|
|
267
|
-
const dependencies =
|
|
268
|
-
pkg.dependencies && typeof pkg.dependencies === "object"
|
|
269
|
-
? Object.keys(pkg.dependencies).sort()
|
|
270
|
-
: [];
|
|
271
|
-
const devDependencies =
|
|
272
|
-
pkg.devDependencies && typeof pkg.devDependencies === "object"
|
|
273
|
-
? Object.keys(pkg.devDependencies).sort()
|
|
274
|
-
: [];
|
|
275
|
-
console.log(
|
|
276
|
-
JSON.stringify(
|
|
277
|
-
{
|
|
278
|
-
name: pkg.name ?? "",
|
|
279
|
-
version: pkg.version ?? "",
|
|
280
|
-
type: pkg.type ?? "",
|
|
281
|
-
private: Boolean(pkg.private),
|
|
282
|
-
packageManager: pkg.packageManager ?? "",
|
|
283
|
-
files: Array.isArray(pkg.files) ? pkg.files : [],
|
|
284
|
-
bin: pkg.bin ?? null,
|
|
285
|
-
main: pkg.main ?? "",
|
|
286
|
-
exports: pkg.exports ?? null,
|
|
287
|
-
scripts,
|
|
288
|
-
dependencyCount: dependencies.length,
|
|
289
|
-
devDependencyCount: devDependencies.length,
|
|
290
|
-
dependencies,
|
|
291
|
-
devDependencies,
|
|
292
|
-
},
|
|
293
|
-
null,
|
|
294
|
-
2,
|
|
295
|
-
),
|
|
296
|
-
);
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
function parseSkillFrontmatter(content) {
|
|
300
|
-
const frontmatter = content.match(/^---\n([\s\S]*?)\n---/)?.[1] ?? "";
|
|
301
|
-
const fields = {};
|
|
302
|
-
for (const line of frontmatter.split(/\r?\n/)) {
|
|
303
|
-
const match = line.match(/^([A-Za-z_][\w-]*):\s*(.*)$/);
|
|
304
|
-
if (match) fields[match[1]] = match[2].trim();
|
|
305
|
-
}
|
|
306
|
-
return { frontmatter, fields };
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
function skillSummary(skillValue, packageValue = "package.json") {
|
|
310
|
-
const skillFile = resolve(
|
|
311
|
-
skillValue.replace(/^~(?=\/|$)/, process.env.HOME ?? "~"),
|
|
312
|
-
);
|
|
313
|
-
const packageFile = resolve(
|
|
314
|
-
packageValue.replace(/^~(?=\/|$)/, process.env.HOME ?? "~"),
|
|
315
|
-
);
|
|
316
|
-
const content = readFileSync(skillFile, "utf8");
|
|
317
|
-
const pkg = readJson(packageFile) ?? {};
|
|
318
|
-
const { frontmatter, fields } = parseSkillFrontmatter(content);
|
|
319
|
-
const scalarLines = frontmatter
|
|
320
|
-
.split(/\r?\n/)
|
|
321
|
-
.filter((line) => /^\w+:\s*\S/.test(line));
|
|
322
|
-
const extraColonLines = scalarLines.filter(
|
|
323
|
-
(line) => (line.match(/:/g) ?? []).length > 1,
|
|
324
|
-
);
|
|
325
|
-
const body = content.replace(/^---\n[\s\S]*?\n---\n?/, "");
|
|
326
|
-
const version =
|
|
327
|
-
fields.version ??
|
|
328
|
-
frontmatter.match(/^\s+version:\s*([^\n]+)\s*$/m)?.[1]?.trim() ??
|
|
329
|
-
"";
|
|
330
|
-
console.log(
|
|
331
|
-
JSON.stringify(
|
|
332
|
-
{
|
|
333
|
-
path: skillValue,
|
|
334
|
-
name: fields.name ?? "",
|
|
335
|
-
description: fields.description ?? "",
|
|
336
|
-
version,
|
|
337
|
-
packageVersion: pkg.version ?? "",
|
|
338
|
-
versionMatchesPackage: version === pkg.version,
|
|
339
|
-
frontmatterExtraColonLines: extraColonLines,
|
|
340
|
-
bodyLineCount: body.split(/\r?\n/).length,
|
|
341
|
-
headings: body.split(/\r?\n/).filter((line) => /^#{1,6}\s/.test(line)),
|
|
342
|
-
},
|
|
343
|
-
null,
|
|
344
|
-
2,
|
|
345
|
-
),
|
|
346
|
-
);
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
function changelogSection(fileValue, version) {
|
|
350
|
-
const file = resolve(fileValue);
|
|
351
|
-
const lines = readFileSync(file, "utf8").split(/\r?\n/);
|
|
352
|
-
const start = lines.findIndex(
|
|
353
|
-
(line) => line.startsWith("## ") && line.includes(version),
|
|
354
|
-
);
|
|
355
|
-
if (start < 0) fail(`Version section not found: ${version}`);
|
|
356
|
-
let end = lines.length;
|
|
357
|
-
for (let index = start + 1; index < lines.length; index += 1) {
|
|
358
|
-
if (lines[index].startsWith("## ")) {
|
|
359
|
-
end = index;
|
|
360
|
-
break;
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
console.log(lines.slice(start, end).join("\n").trimEnd());
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
function runRecipeUtils(argv = process.argv.slice(2)) {
|
|
367
|
-
const [command, ...args] = argv;
|
|
368
|
-
if (!command) {
|
|
369
|
-
usage();
|
|
370
|
-
process.exit(1);
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
if (command === "run-summary")
|
|
374
|
-
runSummary(args[0] ?? "~/.pi/agent/tmp/pi-actors/runs");
|
|
375
|
-
else if (command === "run-ops-snapshot")
|
|
376
|
-
runOpsSnapshot(
|
|
377
|
-
args[0] ?? "~/.pi/agent/tmp/pi-actors/runs",
|
|
378
|
-
args[1] ?? "music",
|
|
379
|
-
args[2],
|
|
380
|
-
args[3],
|
|
381
|
-
);
|
|
382
|
-
else if (command === "playlist")
|
|
383
|
-
playlist(args[0] ?? "~/Music", args[1], args[2], args[3]);
|
|
384
|
-
else if (command === "changelog-section")
|
|
385
|
-
changelogSection(args[0] ?? "CHANGELOG.md", args[1] ?? "Unreleased");
|
|
386
|
-
else if (command === "artifact-manifest")
|
|
387
|
-
artifactManifest(args[0] ?? "artifact.md", args[1], args[2], args[3]);
|
|
388
|
-
else if (command === "artifact-write")
|
|
389
|
-
artifactWrite(args[0] ?? "artifact.md", args[1] ?? "create");
|
|
390
|
-
else if (command === "package-summary")
|
|
391
|
-
packageSummary(args[0] ?? "package.json");
|
|
392
|
-
else if (command === "skill-summary")
|
|
393
|
-
skillSummary(args[0] ?? "skills/actors/SKILL.md", args[1] ?? "package.json");
|
|
394
|
-
else {
|
|
395
|
-
usage();
|
|
396
|
-
fail(`Unknown command: ${command}`);
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
try {
|
|
401
|
-
runRecipeUtils(process.argv.slice(2));
|
|
402
|
-
} catch (error) {
|
|
403
|
-
console.error(error instanceof Error ? error.message : String(error));
|
|
404
|
-
process.exit(1);
|
|
405
|
-
}
|
|
@@ -1,278 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/** Reproducible exact-tree, Domain DAG, secret-hygiene, and ABCd release gates. */
|
|
3
|
-
|
|
4
|
-
import { mkdtempSync, rmSync } from "node:fs";
|
|
5
|
-
import { tmpdir } from "node:os";
|
|
6
|
-
import { dirname, extname, join, normalize, relative, resolve } from "node:path";
|
|
7
|
-
import { spawnSync } from "node:child_process";
|
|
8
|
-
import { fileURLToPath } from "node:url";
|
|
9
|
-
|
|
10
|
-
const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
11
|
-
const temp = mkdtempSync(join(tmpdir(), "pi-actors-release-index-"));
|
|
12
|
-
const indexPath = join(temp, "index");
|
|
13
|
-
const gitEnv = { ...process.env, GIT_INDEX_FILE: indexPath };
|
|
14
|
-
const failures = [];
|
|
15
|
-
|
|
16
|
-
function run(command, args, options = {}) {
|
|
17
|
-
const result = spawnSync(command, args, {
|
|
18
|
-
cwd: root,
|
|
19
|
-
encoding: "utf8",
|
|
20
|
-
maxBuffer: 32 * 1024 * 1024,
|
|
21
|
-
...options,
|
|
22
|
-
});
|
|
23
|
-
if (result.status !== 0) {
|
|
24
|
-
throw new Error(
|
|
25
|
-
`${command} ${args.join(" ")} failed (${result.status}): ${(result.stderr || result.stdout || "no output").trim()}`,
|
|
26
|
-
);
|
|
27
|
-
}
|
|
28
|
-
return result.stdout;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function check(condition, message) {
|
|
32
|
-
if (!condition) failures.push(message);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function stagedText(path) {
|
|
36
|
-
const result = spawnSync("git", ["show", `:${path}`], {
|
|
37
|
-
cwd: root,
|
|
38
|
-
env: gitEnv,
|
|
39
|
-
encoding: "buffer",
|
|
40
|
-
maxBuffer: 16 * 1024 * 1024,
|
|
41
|
-
});
|
|
42
|
-
if (result.status !== 0) {
|
|
43
|
-
failures.push(`could not read staged file: ${path}`);
|
|
44
|
-
return undefined;
|
|
45
|
-
}
|
|
46
|
-
const bytes = result.stdout;
|
|
47
|
-
if (bytes.includes(0)) return undefined;
|
|
48
|
-
return bytes.toString("utf8");
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function sourceImports(source) {
|
|
52
|
-
return [...source.matchAll(/^import(?:\s+type)?\s+(?:[^"']+?\s+from\s+)?["']([^"']+)["'];?$/gmu)]
|
|
53
|
-
.map((match) => match[1]);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function resolveLocalImport(from, specifier, sourceSet) {
|
|
57
|
-
if (!specifier.startsWith(".")) return undefined;
|
|
58
|
-
const base = normalize(join(dirname(from), specifier)).replaceAll("\\", "/");
|
|
59
|
-
const candidates = extname(base)
|
|
60
|
-
? [base.replace(/\.js$/u, ".ts")]
|
|
61
|
-
: [`${base}.ts`, `${base}/index.ts`];
|
|
62
|
-
return candidates.find((candidate) => sourceSet.has(candidate));
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
try {
|
|
66
|
-
run("git", ["read-tree", "HEAD"], { env: gitEnv });
|
|
67
|
-
run("git", ["add", "-A"], { env: gitEnv });
|
|
68
|
-
run("git", ["diff", "--cached", "--check"], { env: gitEnv });
|
|
69
|
-
const files = run("git", ["ls-files", "-z"], { env: gitEnv })
|
|
70
|
-
.split("\0")
|
|
71
|
-
.filter(Boolean);
|
|
72
|
-
const fileSet = new Set(files);
|
|
73
|
-
console.log(`[release] exact temporary index: ${files.length} files`);
|
|
74
|
-
|
|
75
|
-
const secretPatterns = [
|
|
76
|
-
[/-----BEGIN(?: [A-Z0-9]+)? PRIVATE KEY-----[\s\S]{80,}?-----END(?: [A-Z0-9]+)? PRIVATE KEY-----/u, "private-key block"],
|
|
77
|
-
[/\b(?:AKIA|ASIA)[A-Z0-9]{16}\b/u, "AWS access key"],
|
|
78
|
-
[/\bgithub_pat_[A-Za-z0-9_]{20,}\b|\bgh[pousr]_[A-Za-z0-9]{20,}\b/u, "GitHub token"],
|
|
79
|
-
[/\bglpat-[A-Za-z0-9_-]{20,}\b|\bnpm_[A-Za-z0-9]{20,}\b|\bxox[baprs]-[A-Za-z0-9-]{10,}\b/u, "service token"],
|
|
80
|
-
];
|
|
81
|
-
const publicPath = /^(?:README\.md|AGENTS\.md|BACKLOG\.md|CHANGELOG\.md|docs\/|skills\/|recipes\/)/u;
|
|
82
|
-
for (const path of files) {
|
|
83
|
-
const text = stagedText(path);
|
|
84
|
-
if (text === undefined) continue;
|
|
85
|
-
if (!path.startsWith("tests/") && !path.startsWith("fixtures/")) {
|
|
86
|
-
for (const [pattern, label] of secretPatterns) {
|
|
87
|
-
check(!pattern.test(text), `${label} detected in ${path}`);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
if (publicPath.test(path)) {
|
|
91
|
-
check(!/(?:^|[\s"'`])\/home\/[A-Za-z0-9._-]+\//u.test(text), `machine-local path in ${path}`);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
console.log("[release] bounded secret and public-path hygiene checked");
|
|
95
|
-
|
|
96
|
-
const removedPaths = [
|
|
97
|
-
"lib/messages.ts",
|
|
98
|
-
"lib/rooms.ts",
|
|
99
|
-
"lib/mailbox-loop.ts",
|
|
100
|
-
"lib/runs-mailbox.ts",
|
|
101
|
-
"lib/runs-messages.ts",
|
|
102
|
-
"lib/runs-outbox.ts",
|
|
103
|
-
"lib/tools-mailbox.ts",
|
|
104
|
-
"scripts/actor-worker.mjs",
|
|
105
|
-
"scripts/coordinator.mjs",
|
|
106
|
-
"recipes/actor-worker.json",
|
|
107
|
-
"recipes/pipeline-room-swarm.json",
|
|
108
|
-
"recipes/subagent-message.json",
|
|
109
|
-
"recipes/utility-actor-message.json",
|
|
110
|
-
"docs/actor-messages.md",
|
|
111
|
-
"docs/run-inspector.md",
|
|
112
|
-
"lib/run-inspector.ts",
|
|
113
|
-
"lib/run-inspector-overlay.ts",
|
|
114
|
-
];
|
|
115
|
-
for (const path of removedPaths) check(!fileSet.has(path), `removed surface returned: ${path}`);
|
|
116
|
-
|
|
117
|
-
const currentSurface = /^(?:lib\/|scripts\/|recipes\/|fixtures\/|skills\/|docs\/|README\.md|AGENTS\.md)/u;
|
|
118
|
-
const forbiddenPatterns = [
|
|
119
|
-
[/room:</u, "room address"],
|
|
120
|
-
[/branch:</u, "branch address"],
|
|
121
|
-
[/communication\.json/u, "communication snapshot"],
|
|
122
|
-
[/\bActor(?:Address|Message)\b/u, "legacy actor type"],
|
|
123
|
-
[/\b(?:actor_address|default_room)\b/u, "legacy spawn placeholder"],
|
|
124
|
-
[/inbox\.jsonl/u, "legacy inbox state"],
|
|
125
|
-
];
|
|
126
|
-
for (const path of files.filter((candidate) => currentSurface.test(candidate))) {
|
|
127
|
-
if (path === "BACKLOG.md" || path === "CHANGELOG.md" || path === "scripts/release-gates.mjs") continue;
|
|
128
|
-
const text = stagedText(path) ?? "";
|
|
129
|
-
for (const [pattern, label] of forbiddenPatterns) {
|
|
130
|
-
check(!pattern.test(text), `${label} in current surface: ${path}`);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
const legacyFallbacks = new Map([
|
|
134
|
-
["lib/observability.ts", "outbox.jsonl"],
|
|
135
|
-
["lib/runs-status.ts", "events.jsonl"],
|
|
136
|
-
["lib/runs-start.ts", "events.jsonl"],
|
|
137
|
-
]);
|
|
138
|
-
for (const path of files.filter((candidate) => currentSurface.test(candidate))) {
|
|
139
|
-
if (path === "BACKLOG.md" || path === "CHANGELOG.md" || path === "scripts/release-gates.mjs") continue;
|
|
140
|
-
const text = stagedText(path) ?? "";
|
|
141
|
-
for (const term of ["outbox.jsonl", "events.jsonl"]) {
|
|
142
|
-
if (!text.includes(term)) continue;
|
|
143
|
-
check(legacyFallbacks.get(path) === term, `legacy state reference outside read-only fallback: ${path} (${term})`);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
console.log("[release] removed-surface and legacy-fallback allowlists checked");
|
|
147
|
-
|
|
148
|
-
const directTraceWrite = /(?:appendFileSync|writeFileSync|writeText(?:Atomic)?)\s*\(\s*[A-Za-z0-9_.]*?(?:trace|event)(?:Path|File)/iu;
|
|
149
|
-
for (const path of files.filter((candidate) =>
|
|
150
|
-
(candidate.startsWith("lib/") && candidate.endsWith(".ts")) ||
|
|
151
|
-
(candidate.startsWith("scripts/") && candidate.endsWith(".mjs"))
|
|
152
|
-
)) {
|
|
153
|
-
if (path === "lib/runs-trace.ts") continue;
|
|
154
|
-
check(!directTraceWrite.test(stagedText(path) ?? ""), `direct Trace writer outside canonical authority: ${path}`);
|
|
155
|
-
}
|
|
156
|
-
const canonicalTrace = stagedText("lib/runs-trace.ts") ?? "";
|
|
157
|
-
check(/withFileMutationLock\(path/u.test(canonicalTrace), "canonical Trace append lacks mutation lock");
|
|
158
|
-
for (const path of [
|
|
159
|
-
"scripts/async-runner.mjs",
|
|
160
|
-
"scripts/locker.mjs",
|
|
161
|
-
"scripts/music-player.mjs",
|
|
162
|
-
]) {
|
|
163
|
-
const text = stagedText(path) ?? "";
|
|
164
|
-
check(
|
|
165
|
-
text.includes('importRuntimeModule("runs-trace")') && text.includes("appendRunTraceEvent"),
|
|
166
|
-
`first-party Trace writer bypasses canonical runtime module: ${path}`,
|
|
167
|
-
);
|
|
168
|
-
if (path !== "scripts/async-runner.mjs") check(
|
|
169
|
-
text.includes('importRuntimeModule("runs-controls")') &&
|
|
170
|
-
!/function (?:read|write|claimControls|finalizeControls)\b/u.test(text),
|
|
171
|
-
`first-party Control writer bypasses canonical runtime module: ${path}`,
|
|
172
|
-
);
|
|
173
|
-
}
|
|
174
|
-
console.log("[release] canonical Trace and Control writer residue checked");
|
|
175
|
-
|
|
176
|
-
// owner | class | lock | record bound | byte bound | recovery
|
|
177
|
-
const appendOnlyOwners = new Set([
|
|
178
|
-
"lib/runs-trace.ts", // canonical Run evidence | token lock | 2048 | 4 MiB | compact valid suffix
|
|
179
|
-
"lib/runs-retention.ts", // shared kernel evidence | token lock | 256 | 1 MiB | skip malformed
|
|
180
|
-
"scripts/locker.mjs", // first-party actor state | token lock | 512 | 1 MiB | skip malformed
|
|
181
|
-
"lib/command-templates.ts", // complete execution capture | command owner | n/a | artifact policy | preserve
|
|
182
|
-
"scripts/async-runner.mjs", // captures/prompts | Run owner | n/a | artifact policy | preserve
|
|
183
|
-
"scripts/recipe-utils.mjs", // user-declared artifact | caller owner | n/a | caller policy | preserve
|
|
184
|
-
"scripts/release-gates.mjs", // build/test-only source inventory | n/a | n/a | n/a | self-match
|
|
185
|
-
]);
|
|
186
|
-
const appendOnlyPattern = /\bappendFileSync\b|\b(?:writeFileSync|writeTextAtomic)\s*\([^)]*\{\s*flag:\s*["']a["']/su;
|
|
187
|
-
for (const path of files.filter((candidate) => /^(?:lib\/.*\.ts|scripts\/.*\.mjs)$/u.test(candidate)))
|
|
188
|
-
if (appendOnlyPattern.test(stagedText(path) ?? ""))
|
|
189
|
-
check(appendOnlyOwners.has(path), `unregistered shipped append-only writer: ${path}`);
|
|
190
|
-
for (const path of appendOnlyOwners) check(fileSet.has(path), `stale append-only owner inventory: ${path}`);
|
|
191
|
-
console.log(`[release] append-only owner inventory checked: ${appendOnlyOwners.size} source owners`);
|
|
192
|
-
|
|
193
|
-
check(
|
|
194
|
-
(stagedText("scripts/validate-recipe.mjs") ?? "").includes(
|
|
195
|
-
"qaReport.diagnostics.length === 0 && qaReport.warnings.length === 0",
|
|
196
|
-
),
|
|
197
|
-
"Recipe QA warnings are not release-blocking",
|
|
198
|
-
);
|
|
199
|
-
console.log("[release] zero-warning Recipe QA gate checked");
|
|
200
|
-
|
|
201
|
-
const maximumShippedLines = 29_767;
|
|
202
|
-
const shippedPath = /^(?:lib\/|scripts\/|recipes\/|docs\/|skills\/)/u;
|
|
203
|
-
const shippedLines = files
|
|
204
|
-
.filter((path) => shippedPath.test(path))
|
|
205
|
-
.reduce((total, path) => total + (((stagedText(path) ?? "").match(/\n/gu) ?? []).length + 1), 0);
|
|
206
|
-
check(shippedLines <= maximumShippedLines, `shipped lines ${shippedLines} exceed release maximum ${maximumShippedLines}`);
|
|
207
|
-
console.log(`[release] shipped lines ${shippedLines} <= release maximum ${maximumShippedLines}`);
|
|
208
|
-
|
|
209
|
-
const sources = files.filter((path) => path === "index.ts" || (path.startsWith("lib/") && path.endsWith(".ts")));
|
|
210
|
-
const sourceSet = new Set(sources);
|
|
211
|
-
const graph = new Map();
|
|
212
|
-
for (const path of sources) {
|
|
213
|
-
const text = stagedText(path) ?? "";
|
|
214
|
-
const headerEnd = text.indexOf("*/");
|
|
215
|
-
const header = headerEnd >= 0 ? text.slice(0, headerEnd + 2) : "";
|
|
216
|
-
check(
|
|
217
|
-
header.startsWith("/**") && (header.includes("Zones:") || header.includes("Owns:")),
|
|
218
|
-
`missing domain header: ${path}`,
|
|
219
|
-
);
|
|
220
|
-
const edges = sourceImports(text)
|
|
221
|
-
.map((specifier) => resolveLocalImport(path, specifier, sourceSet))
|
|
222
|
-
.filter(Boolean);
|
|
223
|
-
graph.set(path, edges);
|
|
224
|
-
if (path.startsWith("lib/")) {
|
|
225
|
-
check(!edges.includes("index.ts"), `domain imports entrypoint: ${path}`);
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
const visiting = new Set();
|
|
229
|
-
const visited = new Set();
|
|
230
|
-
function visit(path, stack = []) {
|
|
231
|
-
if (visiting.has(path)) {
|
|
232
|
-
failures.push(`Domain DAG cycle: ${[...stack, path].join(" -> ")}`);
|
|
233
|
-
return;
|
|
234
|
-
}
|
|
235
|
-
if (visited.has(path)) return;
|
|
236
|
-
visiting.add(path);
|
|
237
|
-
for (const next of graph.get(path) ?? []) visit(next, [...stack, path]);
|
|
238
|
-
visiting.delete(path);
|
|
239
|
-
visited.add(path);
|
|
240
|
-
}
|
|
241
|
-
for (const path of sources) visit(path);
|
|
242
|
-
console.log(`[release] strict Domain DAG: ${sources.length} sources, acyclic`);
|
|
243
|
-
|
|
244
|
-
for (const path of ["README.md", "AGENTS.md", "BACKLOG.md", "CHANGELOG.md", "docs/README.md"]) {
|
|
245
|
-
check(fileSet.has(path), `missing ABCd root/context file: ${path}`);
|
|
246
|
-
}
|
|
247
|
-
const readStaged = (path) => stagedText(path) ?? "";
|
|
248
|
-
const readme = readStaged("README.md");
|
|
249
|
-
for (const path of ["AGENTS.md", "BACKLOG.md", "CHANGELOG.md", "docs/README.md"]) {
|
|
250
|
-
check(readme.includes(path), `README.md does not route to ${path}`);
|
|
251
|
-
}
|
|
252
|
-
for (const path of files.filter((candidate) => candidate.endsWith(".md"))) {
|
|
253
|
-
const text = readStaged(path);
|
|
254
|
-
for (const match of text.matchAll(/\[[^\]]*\]\(([^)]+)\)/gu)) {
|
|
255
|
-
const target = match[1].split("#", 1)[0];
|
|
256
|
-
if (!target || /^(?:https?:|mailto:)/u.test(target)) continue;
|
|
257
|
-
const resolved = normalize(join(dirname(path), decodeURIComponent(target))).replaceAll("\\", "/");
|
|
258
|
-
check(fileSet.has(resolved), `broken Markdown link: ${path} -> ${target}`);
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
const docsIndex = readStaged("docs/README.md");
|
|
262
|
-
for (const path of files.filter((candidate) => candidate.startsWith("docs/") && candidate.endsWith(".md") && candidate !== "docs/README.md")) {
|
|
263
|
-
check(docsIndex.includes(relative("docs", path).replaceAll("\\", "/")), `docs/README.md omits ${path}`);
|
|
264
|
-
}
|
|
265
|
-
console.log("[release] ABCd context roots, routing, and Markdown links checked");
|
|
266
|
-
|
|
267
|
-
if (failures.length > 0) {
|
|
268
|
-
for (const failure of failures) console.error(`[FAIL] ${failure}`);
|
|
269
|
-
process.exitCode = 1;
|
|
270
|
-
} else {
|
|
271
|
-
console.log("[release] all supplemental release gates passed");
|
|
272
|
-
}
|
|
273
|
-
} catch (error) {
|
|
274
|
-
console.error(`[FAIL] ${error instanceof Error ? error.message : String(error)}`);
|
|
275
|
-
process.exitCode = 1;
|
|
276
|
-
} finally {
|
|
277
|
-
rmSync(temp, { recursive: true, force: true });
|
|
278
|
-
}
|
|
File without changes
|
|
File without changes
|
/package/dist/{recipes/utility-run-state-files.json → skills/actors/recipes/run-state-files.json}
RENAMED
|
File without changes
|
/package/dist/{recipes/utility-playlist-scan.json → skills/media/recipes/playlist-scan.json}
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/dist/{recipes/utility-git-status.json → skills/project-work/recipes/git-status.json}
RENAMED
|
File without changes
|