project-tiny-context-harness 0.2.78 → 0.2.80
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/README.md +15 -11
- package/assets/README.md +16 -12
- package/assets/README.zh-CN.md +10 -6
- package/assets/agents/AGENTS_CORE.md +42 -42
- package/assets/skills/composite-long-task-workflow/SKILL.md +212 -0
- package/assets/skills/composite-long-task-workflow/assets/execution-binding.template.md +31 -0
- package/assets/skills/composite-long-task-workflow/assets/goal-objective.template.md +22 -0
- package/assets/skills/composite-long-task-workflow/references/composite-long-task-workflow-protocol.md +633 -0
- package/assets/skills/normal-long-task/SKILL.md +12 -12
- package/dist/commands/composite-long-task.d.ts +6 -0
- package/dist/commands/composite-long-task.js +103 -0
- package/dist/commands/index.js +5 -3
- package/dist/commands/superpowers.js +6 -87
- package/dist/lib/composite-long-task-renderer.d.ts +12 -0
- package/dist/lib/composite-long-task-renderer.js +153 -0
- package/dist/lib/superpowers-task-compile.js +5 -121
- package/dist/lib/superpowers-task-derive.js +1 -1
- package/dist/lib/superpowers-task-gates.js +6 -4
- package/dist/lib/superpowers-task-source-compile.d.ts +5 -0
- package/dist/lib/superpowers-task-source-compile.js +190 -0
- package/dist/lib/superpowers-task-source-parser.d.ts +23 -0
- package/dist/lib/superpowers-task-source-parser.js +224 -0
- package/dist/lib/superpowers-task-state-schema.d.ts +6 -0
- package/dist/lib/superpowers-task-validator.d.ts +1 -0
- package/dist/lib/superpowers-task-validator.js +16 -3
- package/package.json +1 -1
- package/assets/skills/superpowers-long-task/SKILL.md +0 -602
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { applySliceDelta, initializeSuperpowersTask } from "../lib/superpowers-task-state.js";
|
|
3
|
+
import { compileSuperpowersTask } from "../lib/superpowers-task-compile.js";
|
|
4
|
+
import { deriveSuperpowersArtifacts } from "../lib/superpowers-task-derive.js";
|
|
5
|
+
import { runEpochGate, runFinalGate, runSliceGate } from "../lib/superpowers-task-gates.js";
|
|
6
|
+
import { nextSuperpowersSlices } from "../lib/superpowers-task-next-slices.js";
|
|
7
|
+
import { renderCompositeLongTaskGoal } from "../lib/composite-long-task-renderer.js";
|
|
8
|
+
export async function compositeLongTask(args) {
|
|
9
|
+
await runCompositeLongTaskCommand(args, {
|
|
10
|
+
commandName: "ty-context composite-long-task",
|
|
11
|
+
label: "composite long-task",
|
|
12
|
+
showHelp: true
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
export async function runCompositeLongTaskCommand(args, options) {
|
|
16
|
+
const subcommand = args[0] ?? "help";
|
|
17
|
+
const workdirArg = args[1];
|
|
18
|
+
if (!workdirArg || subcommand === "help") {
|
|
19
|
+
help(options.commandName, options.showHelp);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const workdir = path.resolve(process.cwd(), workdirArg);
|
|
23
|
+
if (subcommand === "init") {
|
|
24
|
+
await initializeSuperpowersTask(workdir, { planSlug: path.basename(workdir) });
|
|
25
|
+
console.log(`initialized ${options.label} state at ${workdirArg}/task-state.json`);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (subcommand === "compile") {
|
|
29
|
+
const state = await compileSuperpowersTask(workdir);
|
|
30
|
+
console.log(`compiled ${options.label} graph plan_items=${Object.keys(state.graph.plan_items).length} acs=${Object.keys(state.graph.acceptance_criteria).length}`);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
if (subcommand === "apply-slice-delta") {
|
|
34
|
+
const delta = args[2];
|
|
35
|
+
if (!delta) {
|
|
36
|
+
throw new Error("apply-slice-delta requires <slice-delta.json>");
|
|
37
|
+
}
|
|
38
|
+
await applySliceDelta(workdir, path.resolve(process.cwd(), delta));
|
|
39
|
+
const result = await deriveSuperpowersArtifacts(workdir);
|
|
40
|
+
console.log(`applied ${options.label} slice delta and derived files=${result.files.length}`);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (subcommand === "derive") {
|
|
44
|
+
const result = await deriveSuperpowersArtifacts(workdir);
|
|
45
|
+
console.log(`derived ${options.label} artifacts files=${result.files.length}`);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (subcommand === "slice-gate") {
|
|
49
|
+
const sliceId = optionValue(args, "--slice") ?? "";
|
|
50
|
+
const result = await runSliceGate(workdir, sliceId);
|
|
51
|
+
console.log(result.passed ? `slice gate passed ${sliceId}` : `slice gate blocked ${result.messages.join("; ")}`);
|
|
52
|
+
if (!result.passed) {
|
|
53
|
+
process.exitCode = 1;
|
|
54
|
+
}
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
if (subcommand === "epoch-gate") {
|
|
58
|
+
const epochId = optionValue(args, "--epoch") ?? "";
|
|
59
|
+
const result = await runEpochGate(workdir, epochId);
|
|
60
|
+
console.log(result.passed ? `epoch gate passed ${epochId}` : `epoch gate blocked ${result.messages.join("; ")}`);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if (subcommand === "final-gate") {
|
|
64
|
+
const result = await runFinalGate(workdir);
|
|
65
|
+
console.log(`final gate product_goal_complete=${result.product_goal_complete}`);
|
|
66
|
+
if (!result.product_goal_complete) {
|
|
67
|
+
process.exitCode = 1;
|
|
68
|
+
for (const error of result.errors) {
|
|
69
|
+
console.error(`error: ${error}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
if (subcommand === "next-slices") {
|
|
75
|
+
const limit = Number.parseInt(optionValue(args, "--limit") ?? "5", 10);
|
|
76
|
+
const slices = await nextSuperpowersSlices(workdir, Number.isFinite(limit) ? limit : 5);
|
|
77
|
+
console.log(`Next ${Math.min(Number.isFinite(limit) ? limit : 5, 5)} high-value clusters:`);
|
|
78
|
+
console.log(slices.join("\n"));
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
if (subcommand === "render-goal") {
|
|
82
|
+
const result = await renderCompositeLongTaskGoal(workdir);
|
|
83
|
+
console.log(`rendered ${options.label} goal artifacts: ${path.basename(result.goalObjectivePath)} ${path.basename(result.protocolPath)} ${path.basename(result.executionBindingPath)} length=${result.goalObjectiveLength}`);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
help(options.commandName, options.showHelp);
|
|
87
|
+
}
|
|
88
|
+
function help(commandName, showRenderGoal) {
|
|
89
|
+
const renderGoal = showRenderGoal ? "\n render-goal <workdir> Render workflow-protocol.md, execution-binding.md and goal-objective.txt" : "";
|
|
90
|
+
console.log(`${commandName} commands:
|
|
91
|
+
init <workdir> Initialize task-state.json and events.ndjson
|
|
92
|
+
compile <workdir> Compile sources into task graph
|
|
93
|
+
apply-slice-delta <workdir> <delta> Apply structured slice delta, evidence and derived views
|
|
94
|
+
derive <workdir> Generate derived/** views
|
|
95
|
+
slice-gate <workdir> --slice <id> Validate one slice has real progress
|
|
96
|
+
epoch-gate <workdir> --epoch <id> Refresh shared epoch evidence views
|
|
97
|
+
final-gate <workdir> Compute product_goal_complete
|
|
98
|
+
next-slices <workdir> --limit 5 Recommend next proof clusters${renderGoal}`);
|
|
99
|
+
}
|
|
100
|
+
function optionValue(args, name) {
|
|
101
|
+
const index = args.indexOf(name);
|
|
102
|
+
return index >= 0 ? args[index + 1] : undefined;
|
|
103
|
+
}
|
package/dist/commands/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { checkModularity } from "./check-modularity.js";
|
|
2
|
+
import { compositeLongTask } from "./composite-long-task.js";
|
|
2
3
|
import { doctor } from "./doctor.js";
|
|
3
4
|
import { exportContext } from "./export-context.js";
|
|
4
5
|
import { init } from "./init.js";
|
|
@@ -22,6 +23,7 @@ export const commands = {
|
|
|
22
23
|
"validate-plan-contract": (args) => validate(["validate-plan-contract", ...args]),
|
|
23
24
|
"validate-plan-acceptance": (args) => validate(["validate-plan-acceptance", ...args]),
|
|
24
25
|
"validate-superpowers-state": (args) => validate(["validate-superpowers-state", ...args]),
|
|
26
|
+
"composite-long-task": compositeLongTask,
|
|
25
27
|
superpowers,
|
|
26
28
|
package: packageSource
|
|
27
29
|
};
|
|
@@ -47,8 +49,8 @@ export function help() {
|
|
|
47
49
|
validate-plan-acceptance <dir>
|
|
48
50
|
Validate plan-conformance matrix and final verdict consistency
|
|
49
51
|
validate-superpowers-state <dir>
|
|
50
|
-
Validate canonical Superpowers task-state.json
|
|
51
|
-
|
|
52
|
-
Manage explicit
|
|
52
|
+
Validate canonical Superpowers-backed task-state.json
|
|
53
|
+
composite-long-task <subcommand>
|
|
54
|
+
Manage explicit composite long-task workflow workdirs
|
|
53
55
|
package <subcommand> Maintain package canonical source`);
|
|
54
56
|
}
|
|
@@ -1,89 +1,8 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { applySliceDelta, initializeSuperpowersTask } from "../lib/superpowers-task-state.js";
|
|
3
|
-
import { compileSuperpowersTask } from "../lib/superpowers-task-compile.js";
|
|
4
|
-
import { deriveSuperpowersArtifacts } from "../lib/superpowers-task-derive.js";
|
|
5
|
-
import { runEpochGate, runFinalGate, runSliceGate } from "../lib/superpowers-task-gates.js";
|
|
6
|
-
import { nextSuperpowersSlices } from "../lib/superpowers-task-next-slices.js";
|
|
1
|
+
import { runCompositeLongTaskCommand } from "./composite-long-task.js";
|
|
7
2
|
export async function superpowers(args) {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
14
|
-
const workdir = path.resolve(process.cwd(), workdirArg);
|
|
15
|
-
if (subcommand === "init") {
|
|
16
|
-
await initializeSuperpowersTask(workdir, { planSlug: path.basename(workdir) });
|
|
17
|
-
console.log(`initialized superpowers task state at ${workdirArg}/task-state.json`);
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
if (subcommand === "compile") {
|
|
21
|
-
const state = await compileSuperpowersTask(workdir);
|
|
22
|
-
console.log(`compiled superpowers task graph plan_items=${Object.keys(state.graph.plan_items).length} acs=${Object.keys(state.graph.acceptance_criteria).length}`);
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
if (subcommand === "apply-slice-delta") {
|
|
26
|
-
const delta = args[2];
|
|
27
|
-
if (!delta) {
|
|
28
|
-
throw new Error("apply-slice-delta requires <slice-delta.json>");
|
|
29
|
-
}
|
|
30
|
-
await applySliceDelta(workdir, path.resolve(process.cwd(), delta));
|
|
31
|
-
const result = await deriveSuperpowersArtifacts(workdir);
|
|
32
|
-
console.log(`applied superpowers slice delta and derived files=${result.files.length}`);
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
if (subcommand === "derive") {
|
|
36
|
-
const result = await deriveSuperpowersArtifacts(workdir);
|
|
37
|
-
console.log(`derived superpowers artifacts files=${result.files.length}`);
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
if (subcommand === "slice-gate") {
|
|
41
|
-
const sliceId = optionValue(args, "--slice") ?? "";
|
|
42
|
-
const result = await runSliceGate(workdir, sliceId);
|
|
43
|
-
console.log(result.passed ? `slice gate passed ${sliceId}` : `slice gate blocked ${result.messages.join("; ")}`);
|
|
44
|
-
if (!result.passed) {
|
|
45
|
-
process.exitCode = 1;
|
|
46
|
-
}
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
if (subcommand === "epoch-gate") {
|
|
50
|
-
const epochId = optionValue(args, "--epoch") ?? "";
|
|
51
|
-
const result = await runEpochGate(workdir, epochId);
|
|
52
|
-
console.log(result.passed ? `epoch gate passed ${epochId}` : `epoch gate blocked ${result.messages.join("; ")}`);
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
if (subcommand === "final-gate") {
|
|
56
|
-
const result = await runFinalGate(workdir);
|
|
57
|
-
console.log(`final gate product_goal_complete=${result.product_goal_complete}`);
|
|
58
|
-
if (!result.product_goal_complete) {
|
|
59
|
-
process.exitCode = 1;
|
|
60
|
-
for (const error of result.errors) {
|
|
61
|
-
console.error(`error: ${error}`);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
if (subcommand === "next-slices") {
|
|
67
|
-
const limit = Number.parseInt(optionValue(args, "--limit") ?? "5", 10);
|
|
68
|
-
const slices = await nextSuperpowersSlices(workdir, Number.isFinite(limit) ? limit : 5);
|
|
69
|
-
console.log(`Next ${Math.min(Number.isFinite(limit) ? limit : 5, 5)} high-value clusters:`);
|
|
70
|
-
console.log(slices.join("\n"));
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
help();
|
|
74
|
-
}
|
|
75
|
-
function help() {
|
|
76
|
-
console.log(`ty-context superpowers commands:
|
|
77
|
-
init <workdir> Initialize task-state.json and events.ndjson
|
|
78
|
-
compile <workdir> Compile sources into task graph
|
|
79
|
-
apply-slice-delta <workdir> <delta> Apply structured slice delta, evidence and derived views
|
|
80
|
-
derive <workdir> Generate derived/** views
|
|
81
|
-
slice-gate <workdir> --slice <id> Validate one slice has real progress
|
|
82
|
-
epoch-gate <workdir> --epoch <id> Refresh shared epoch evidence views
|
|
83
|
-
final-gate <workdir> Compute product_goal_complete
|
|
84
|
-
next-slices <workdir> --limit 5 Recommend next proof clusters`);
|
|
85
|
-
}
|
|
86
|
-
function optionValue(args, name) {
|
|
87
|
-
const index = args.indexOf(name);
|
|
88
|
-
return index >= 0 ? args[index + 1] : undefined;
|
|
3
|
+
await runCompositeLongTaskCommand(args, {
|
|
4
|
+
commandName: "ty-context superpowers",
|
|
5
|
+
label: "superpowers task",
|
|
6
|
+
showHelp: false
|
|
7
|
+
});
|
|
89
8
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface CompositeLongTaskGoalRenderResult {
|
|
2
|
+
workdir: string;
|
|
3
|
+
protocolPath: string;
|
|
4
|
+
protocolSha256: string;
|
|
5
|
+
executionBindingPath: string;
|
|
6
|
+
goalObjectivePath: string;
|
|
7
|
+
goalObjectiveLength: number;
|
|
8
|
+
}
|
|
9
|
+
export declare function renderCompositeLongTaskGoal(workdir: string): Promise<CompositeLongTaskGoalRenderResult>;
|
|
10
|
+
export declare const COMPOSITE_LONG_TASK_WORKFLOW_SKILL_NAME = "composite-long-task-workflow";
|
|
11
|
+
export declare const COMPOSITE_LONG_TASK_PUBLIC_COMMAND = "composite-long-task";
|
|
12
|
+
export declare const COMPOSITE_LONG_TASK_PACKAGE = "project-tiny-context-harness";
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { CANONICAL_CORE_PACKAGE } from "./constants.js";
|
|
4
|
+
import { pathExists, readText, writeTextIfChanged } from "./fs.js";
|
|
5
|
+
import { packageAssetPath, packageRoot } from "./paths.js";
|
|
6
|
+
const SOURCE_ASSET = "packages/ty-context/assets/skills/composite-long-task-workflow/references/composite-long-task-workflow-protocol.md";
|
|
7
|
+
const REQUIRED_SOURCE_FILES = [
|
|
8
|
+
"product-architecture-source.md",
|
|
9
|
+
"technical-realization-plan.md",
|
|
10
|
+
"acceptance-checklist.md"
|
|
11
|
+
];
|
|
12
|
+
export async function renderCompositeLongTaskGoal(workdir) {
|
|
13
|
+
const resolvedWorkdir = path.resolve(workdir);
|
|
14
|
+
await assertReadyWorkdir(resolvedWorkdir);
|
|
15
|
+
const protocolBody = ensureTrailingNewline(normalizeNewlines(await readText(packageAssetPath("skills", "composite-long-task-workflow", "references", "composite-long-task-workflow-protocol.md"))));
|
|
16
|
+
const protocolSha256 = sha256(protocolBody);
|
|
17
|
+
const protocolVersion = await packageVersion();
|
|
18
|
+
const protocolSnapshot = [
|
|
19
|
+
"protocol_name: composite-long-task-workflow",
|
|
20
|
+
`protocol_version: ${protocolVersion}`,
|
|
21
|
+
`protocol_sha256: ${protocolSha256}`,
|
|
22
|
+
`generated_at: ${new Date().toISOString()}`,
|
|
23
|
+
`source_asset: ${SOURCE_ASSET}`,
|
|
24
|
+
"---",
|
|
25
|
+
protocolBody.trimEnd()
|
|
26
|
+
].join("\n") + "\n";
|
|
27
|
+
const protocolPath = path.join(resolvedWorkdir, "workflow-protocol.md");
|
|
28
|
+
await writeTextIfChanged(protocolPath, protocolSnapshot);
|
|
29
|
+
const executionBinding = renderExecutionBinding(resolvedWorkdir, protocolSha256);
|
|
30
|
+
const executionBindingPath = path.join(resolvedWorkdir, "execution-binding.md");
|
|
31
|
+
await writeTextIfChanged(executionBindingPath, executionBinding);
|
|
32
|
+
const goalObjective = renderGoalObjective(workdirForPrompt(resolvedWorkdir));
|
|
33
|
+
if (goalObjective.length > 3850) {
|
|
34
|
+
throw new Error(`goal-objective.txt exceeds 3850 characters (${goalObjective.length})`);
|
|
35
|
+
}
|
|
36
|
+
if (/^\/goal\s+read\s+\S+\s*\.?$/i.test(goalObjective.trim())) {
|
|
37
|
+
throw new Error("goal-objective.txt must not be a single read-file pointer");
|
|
38
|
+
}
|
|
39
|
+
const goalObjectivePath = path.join(resolvedWorkdir, "goal-objective.txt");
|
|
40
|
+
await writeTextIfChanged(goalObjectivePath, goalObjective);
|
|
41
|
+
return {
|
|
42
|
+
workdir: resolvedWorkdir,
|
|
43
|
+
protocolPath,
|
|
44
|
+
protocolSha256,
|
|
45
|
+
executionBindingPath,
|
|
46
|
+
goalObjectivePath,
|
|
47
|
+
goalObjectiveLength: goalObjective.length
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
async function assertReadyWorkdir(workdir) {
|
|
51
|
+
for (const sourceFile of REQUIRED_SOURCE_FILES) {
|
|
52
|
+
const sourcePath = path.join(workdir, sourceFile);
|
|
53
|
+
if (!(await pathExists(sourcePath))) {
|
|
54
|
+
throw new Error(`render-goal requires ${sourceFile}; provide the three input files first`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
const statePath = path.join(workdir, "task-state.json");
|
|
58
|
+
if (!(await pathExists(statePath))) {
|
|
59
|
+
throw new Error("render-goal requires task-state.json; run ty-context composite-long-task init/compile before render-goal");
|
|
60
|
+
}
|
|
61
|
+
const state = JSON.parse(await readText(statePath));
|
|
62
|
+
const planItemCount = Object.keys(state.graph?.plan_items ?? {}).length;
|
|
63
|
+
const acceptanceCriteriaCount = Object.keys(state.graph?.acceptance_criteria ?? {}).length;
|
|
64
|
+
if (planItemCount === 0 || acceptanceCriteriaCount === 0) {
|
|
65
|
+
throw new Error("render-goal requires compiled task-state.json; run ty-context composite-long-task compile before render-goal");
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
function renderExecutionBinding(workdir, protocolSha256) {
|
|
69
|
+
return `# Composite Long-Task Execution Binding
|
|
70
|
+
|
|
71
|
+
workdir: ${path.normalize(workdir)}
|
|
72
|
+
protocol: workflow-protocol.md
|
|
73
|
+
protocol_sha256: ${protocolSha256}
|
|
74
|
+
goal_objective: goal-objective.txt
|
|
75
|
+
|
|
76
|
+
authorities:
|
|
77
|
+
product_architecture_source: product-architecture-source.md
|
|
78
|
+
technical_realization_plan: technical-realization-plan.md
|
|
79
|
+
acceptance_checklist: acceptance-checklist.md
|
|
80
|
+
|
|
81
|
+
canonical_state:
|
|
82
|
+
task_state: task-state.json
|
|
83
|
+
events: events.ndjson
|
|
84
|
+
derived_dir: derived/
|
|
85
|
+
|
|
86
|
+
required_commands:
|
|
87
|
+
init: ty-context composite-long-task init <workdir>
|
|
88
|
+
compile: ty-context composite-long-task compile <workdir>
|
|
89
|
+
derive: ty-context composite-long-task derive <workdir>
|
|
90
|
+
apply_slice_delta: ty-context composite-long-task apply-slice-delta <workdir> <slice-delta.json>
|
|
91
|
+
slice_gate: ty-context composite-long-task slice-gate <workdir> --slice <id>
|
|
92
|
+
epoch_gate: ty-context composite-long-task epoch-gate <workdir> --epoch <id>
|
|
93
|
+
state_validator: ty-context validate-superpowers-state <workdir>
|
|
94
|
+
acceptance_validator: ty-context validate-plan-acceptance <workdir>
|
|
95
|
+
final_gate: ty-context composite-long-task final-gate <workdir>
|
|
96
|
+
|
|
97
|
+
completion_gate:
|
|
98
|
+
product_goal_complete_source: final_gate
|
|
99
|
+
cannot_hand_set_product_goal_complete: true
|
|
100
|
+
`;
|
|
101
|
+
}
|
|
102
|
+
function renderGoalObjective(workdir) {
|
|
103
|
+
return `/goal Execute the composite long-task workflow in ${workdir}.
|
|
104
|
+
|
|
105
|
+
First read and obey:
|
|
106
|
+
- workflow-protocol.md
|
|
107
|
+
- execution-binding.md
|
|
108
|
+
- product-architecture-source.md
|
|
109
|
+
- technical-realization-plan.md
|
|
110
|
+
- acceptance-checklist.md
|
|
111
|
+
- task-state.json and generated derived/** views
|
|
112
|
+
|
|
113
|
+
Persistent contract:
|
|
114
|
+
Product / Architecture Source owns intent, scope and boundaries. Technical Realization Plan owns PI implementation and plan conformance. Acceptance Checklist owns AC completion semantics and proof layers. task-state.json is the only execution state source; events.ndjson is append-only; derived/** is generated and must not be hand-edited as authority.
|
|
115
|
+
|
|
116
|
+
Use workflow-protocol.md to combine Tiny Context gates with official Superpowers execution. It is not business Context and must not be registered in project_context/context.toml. Do not redefine, duplicate or fork Superpowers mechanics. Prefer superpowers:subagent-driven-development when subagents are available, otherwise use superpowers:executing-plans. Use TDD for behavior gaps and superpowers:verification-before-completion before completion claims.
|
|
117
|
+
|
|
118
|
+
Work in slices. Each slice must update state through slice-delta.json, canonical evidence records, derive, and slice-gate. Run epoch-gate for shared provider/browser/runtime/security proof environments. Preserve Context Delta, plan conformance, acceptance proof layers, redaction, reviewability and sample/full-population boundaries.
|
|
119
|
+
|
|
120
|
+
Forbidden shortcuts:
|
|
121
|
+
Tests alone do not prove plan conformance. Superpowers review does not override Tiny Context gates. Sample evidence does not prove full population unless AC allows. Manual edits under derived/** are not authority. Local audit cannot mark final completion. Do not claim full implementation while Context Delta is required but Context is not updated, or while Source-to-Context Coverage / Context-to-Implementation Binding has unresolved required gaps.
|
|
122
|
+
|
|
123
|
+
Completion:
|
|
124
|
+
Do not hand-set product_goal_complete. Only complete after derive, verification-before-completion, validate-superpowers-state, validate-plan-acceptance, auditor/stale-overclaim checks when applicable, and final-gate compute product_goal_complete=true. If audit_task_complete is true but acceptance_target_status is not complete, report "Audit workflow completed; acceptance target not complete." and continue or stop with blockers; do not say Goal achieved.
|
|
125
|
+
|
|
126
|
+
Blocked:
|
|
127
|
+
Maximize safe autonomous progress using repo tools, local app/browser sessions, CLI auth and authorized elevation. Stop only for locally unsatisfiable blockers such as MFA, missing permission, external approval or unavailable credentials, and return the minimal user action list plus next agent step.
|
|
128
|
+
`;
|
|
129
|
+
}
|
|
130
|
+
async function packageVersion() {
|
|
131
|
+
const packageJson = JSON.parse(await readText(path.join(packageRoot(), "package.json")));
|
|
132
|
+
return packageJson.version ?? "unknown";
|
|
133
|
+
}
|
|
134
|
+
function workdirForPrompt(workdir) {
|
|
135
|
+
const cwd = process.cwd();
|
|
136
|
+
const relative = path.relative(cwd, workdir);
|
|
137
|
+
if (relative && !relative.startsWith("..") && !path.isAbsolute(relative)) {
|
|
138
|
+
return relative.replace(/\\/g, "/");
|
|
139
|
+
}
|
|
140
|
+
return workdir.replace(/\\/g, "/");
|
|
141
|
+
}
|
|
142
|
+
function normalizeNewlines(value) {
|
|
143
|
+
return value.replace(/\r\n/g, "\n");
|
|
144
|
+
}
|
|
145
|
+
function ensureTrailingNewline(value) {
|
|
146
|
+
return `${value.trimEnd()}\n`;
|
|
147
|
+
}
|
|
148
|
+
function sha256(value) {
|
|
149
|
+
return createHash("sha256").update(value).digest("hex");
|
|
150
|
+
}
|
|
151
|
+
export const COMPOSITE_LONG_TASK_WORKFLOW_SKILL_NAME = "composite-long-task-workflow";
|
|
152
|
+
export const COMPOSITE_LONG_TASK_PUBLIC_COMMAND = "composite-long-task";
|
|
153
|
+
export const COMPOSITE_LONG_TASK_PACKAGE = CANONICAL_CORE_PACKAGE;
|
|
@@ -2,8 +2,7 @@ import path from "node:path";
|
|
|
2
2
|
import { readText } from "./fs.js";
|
|
3
3
|
import { appendSuperpowersEvent } from "./superpowers-task-events.js";
|
|
4
4
|
import { loadSuperpowersState, recomputeStatuses, saveSuperpowersState, refreshSourceHashes } from "./superpowers-task-state.js";
|
|
5
|
-
import {
|
|
6
|
-
const DEFAULT_LAYERS = ["code", "test"];
|
|
5
|
+
import { DEFAULT_LAYERS, parseAcceptanceCriteria, parsePlanItems, parseProductArchitectureScope } from "./superpowers-task-source-compile.js";
|
|
7
6
|
export async function compileSuperpowersTask(workdir) {
|
|
8
7
|
const state = await loadSuperpowersState(workdir);
|
|
9
8
|
await refreshSourceHashes(workdir, state);
|
|
@@ -11,11 +10,11 @@ export async function compileSuperpowersTask(workdir) {
|
|
|
11
10
|
const technicalPlan = await readText(path.join(workdir, state.sources.technical_realization_plan.path));
|
|
12
11
|
const checklist = await readText(path.join(workdir, state.sources.acceptance_checklist.path));
|
|
13
12
|
state.delivery = {
|
|
14
|
-
product_architecture_scope: parseProductArchitectureScope(productSource),
|
|
13
|
+
product_architecture_scope: parseProductArchitectureScope(productSource, state.sources.product_architecture_source.path),
|
|
15
14
|
scope_conflicts: []
|
|
16
15
|
};
|
|
17
|
-
const planItems = parsePlanItems(technicalPlan);
|
|
18
|
-
const acceptanceCriteria = parseAcceptanceCriteria(checklist);
|
|
16
|
+
const planItems = parsePlanItems(technicalPlan, state.sources.technical_realization_plan.path);
|
|
17
|
+
const acceptanceCriteria = parseAcceptanceCriteria(checklist, state.sources.acceptance_checklist.path);
|
|
19
18
|
const acIds = Object.keys(acceptanceCriteria);
|
|
20
19
|
for (const [planId, item] of Object.entries(planItems)) {
|
|
21
20
|
if (item.related_acs.length === 0) {
|
|
@@ -47,95 +46,11 @@ export async function compileSuperpowersTask(workdir) {
|
|
|
47
46
|
});
|
|
48
47
|
return state;
|
|
49
48
|
}
|
|
50
|
-
function parseProductArchitectureScope(content) {
|
|
51
|
-
return {
|
|
52
|
-
delivery_scope: fieldText(content, "delivery_scope"),
|
|
53
|
-
full_population_required: fieldBoolean(content, "full_population_required"),
|
|
54
|
-
representative_samples_validate: field(content, "representative_samples_validate"),
|
|
55
|
-
representative_samples_do_not_validate: field(content, "representative_samples_do_not_validate"),
|
|
56
|
-
out_of_scope_backlog: field(content, "out_of_scope_backlog")
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
function parsePlanItems(content) {
|
|
60
|
-
const items = {};
|
|
61
|
-
const matches = [...content.matchAll(/\b(PI-\d{3,})\b\s*[:.-]?\s*([^\n]*)/gi)];
|
|
62
|
-
for (const [index, match] of matches.entries()) {
|
|
63
|
-
const id = match[1].toUpperCase();
|
|
64
|
-
const block = blockAfter(content, match.index ?? 0, matches[index + 1]?.index);
|
|
65
|
-
items[id] = {
|
|
66
|
-
requirement: cleanText(match[2]) || firstLine(block) || id,
|
|
67
|
-
delivery_scope: fieldText(block, "delivery_scope"),
|
|
68
|
-
capability_target: fieldText(block, "capability_target"),
|
|
69
|
-
representative_samples: field(block, "representative_samples"),
|
|
70
|
-
full_population_boundary: fieldText(block, "full_population_boundary"),
|
|
71
|
-
non_required_population: field(block, "non_required_population"),
|
|
72
|
-
owner_surfaces: field(block, "owner_surfaces"),
|
|
73
|
-
forbidden_surfaces: field(block, "forbidden_surfaces"),
|
|
74
|
-
implementation_paths: field(block, "implementation_paths"),
|
|
75
|
-
required_tests: field(block, "required_tests"),
|
|
76
|
-
status: "not_started",
|
|
77
|
-
related_acs: field(block, "related_acs").map((item) => item.toUpperCase()),
|
|
78
|
-
required_proof_layers: []
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
if (Object.keys(items).length === 0) {
|
|
82
|
-
items["PI-001"] = {
|
|
83
|
-
requirement: firstLine(content) || "Implement technical realization plan",
|
|
84
|
-
delivery_scope: "",
|
|
85
|
-
capability_target: "",
|
|
86
|
-
representative_samples: [],
|
|
87
|
-
full_population_boundary: "",
|
|
88
|
-
non_required_population: [],
|
|
89
|
-
owner_surfaces: [],
|
|
90
|
-
forbidden_surfaces: [],
|
|
91
|
-
implementation_paths: [],
|
|
92
|
-
required_tests: [],
|
|
93
|
-
status: "not_started",
|
|
94
|
-
related_acs: [],
|
|
95
|
-
required_proof_layers: []
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
return items;
|
|
99
|
-
}
|
|
100
|
-
function parseAcceptanceCriteria(content) {
|
|
101
|
-
const items = {};
|
|
102
|
-
const matches = [...content.matchAll(/\b(AC-\d{3,})\b\s*[:.-]?\s*([^\n]*)/gi)];
|
|
103
|
-
for (const [index, match] of matches.entries()) {
|
|
104
|
-
const id = match[1].toUpperCase();
|
|
105
|
-
const block = blockAfter(content, match.index ?? 0, matches[index + 1]?.index);
|
|
106
|
-
const layers = field(block, "required_proof_layers").map(normalizeLayer).filter(Boolean);
|
|
107
|
-
items[id] = {
|
|
108
|
-
scope: cleanText(match[2]) || firstLine(block) || id,
|
|
109
|
-
acceptance_scope: fieldText(block, "acceptance_scope"),
|
|
110
|
-
ac_validates: field(block, "ac_validates"),
|
|
111
|
-
ac_does_not_validate: field(block, "ac_does_not_validate"),
|
|
112
|
-
sample_boundary: fieldText(block, "sample_boundary"),
|
|
113
|
-
full_population_required: fieldBoolean(block, "full_population_required"),
|
|
114
|
-
related_plan_items: field(block, "related_plan_items").map((item) => item.toUpperCase()),
|
|
115
|
-
required_proof_layers: layers.length > 0 ? layers : DEFAULT_LAYERS,
|
|
116
|
-
status: "not_run"
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
if (Object.keys(items).length === 0) {
|
|
120
|
-
items["AC-001"] = {
|
|
121
|
-
scope: firstLine(content) || "Acceptance checklist item",
|
|
122
|
-
acceptance_scope: "",
|
|
123
|
-
ac_validates: [],
|
|
124
|
-
ac_does_not_validate: [],
|
|
125
|
-
sample_boundary: "",
|
|
126
|
-
full_population_required: null,
|
|
127
|
-
related_plan_items: [],
|
|
128
|
-
required_proof_layers: DEFAULT_LAYERS,
|
|
129
|
-
status: "not_run"
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
return items;
|
|
133
|
-
}
|
|
134
49
|
export function computeScopeConflicts(state) {
|
|
135
50
|
const conflicts = [];
|
|
136
51
|
const product = state.delivery?.product_architecture_scope;
|
|
137
52
|
const productScope = product?.delivery_scope ?? "";
|
|
138
|
-
const productRequiresFullPopulation = productScope === "full_population_operation"
|
|
53
|
+
const productRequiresFullPopulation = productScope === "full_population_operation";
|
|
139
54
|
const productIsCapabilityOnly = productScope === "system_capability_build" ||
|
|
140
55
|
productScope === "representative_sample_validation" ||
|
|
141
56
|
product?.full_population_required === false;
|
|
@@ -187,37 +102,6 @@ function compileProgress(state) {
|
|
|
187
102
|
}
|
|
188
103
|
};
|
|
189
104
|
}
|
|
190
|
-
function blockAfter(content, start, end) {
|
|
191
|
-
return content.slice(start, end ?? content.length);
|
|
192
|
-
}
|
|
193
|
-
function field(block, name) {
|
|
194
|
-
const text = fieldText(block, name);
|
|
195
|
-
return text ? asStringArray(text) : [];
|
|
196
|
-
}
|
|
197
|
-
function fieldText(block, name) {
|
|
198
|
-
const pattern = new RegExp(`${name}\\s*:\\s*([^\\n]+)`, "i");
|
|
199
|
-
const match = pattern.exec(block);
|
|
200
|
-
return match ? cleanText(match[1]) : "";
|
|
201
|
-
}
|
|
202
|
-
function fieldBoolean(block, name) {
|
|
203
|
-
const value = fieldText(block, name).toLowerCase();
|
|
204
|
-
if (value === "true") {
|
|
205
|
-
return true;
|
|
206
|
-
}
|
|
207
|
-
if (value === "false") {
|
|
208
|
-
return false;
|
|
209
|
-
}
|
|
210
|
-
return null;
|
|
211
|
-
}
|
|
212
|
-
function normalizeLayer(value) {
|
|
213
|
-
return value.trim().toLowerCase().replace(/[- ]+/g, "_");
|
|
214
|
-
}
|
|
215
|
-
function firstLine(content) {
|
|
216
|
-
return cleanText(content.split(/\r?\n/).find((line) => cleanText(line)) ?? "");
|
|
217
|
-
}
|
|
218
|
-
function cleanText(value) {
|
|
219
|
-
return value.replace(/^[-#*\s]+/, "").trim();
|
|
220
|
-
}
|
|
221
105
|
function unique(values) {
|
|
222
106
|
return [...new Set(values.filter(Boolean))];
|
|
223
107
|
}
|
|
@@ -117,7 +117,7 @@ async function assertDerivedJson(workdir, basename, expected, errors) {
|
|
|
117
117
|
}
|
|
118
118
|
const actual = JSON.parse(await readText(file));
|
|
119
119
|
if (stableJson(actual) !== stableJson(expected)) {
|
|
120
|
-
errors.push(`derived/${basename}.json does not match task-state.json; rerun ty-context
|
|
120
|
+
errors.push(`derived/${basename}.json does not match task-state.json; rerun ty-context composite-long-task derive`);
|
|
121
121
|
}
|
|
122
122
|
}
|
|
123
123
|
function evidenceForLayers(state, layerIds) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { appendSuperpowersEvent } from "./superpowers-task-events.js";
|
|
2
2
|
import { deriveSuperpowersArtifacts } from "./superpowers-task-derive.js";
|
|
3
3
|
import { loadSuperpowersState, recomputeStatuses, saveSuperpowersState } from "./superpowers-task-state.js";
|
|
4
|
-
import {
|
|
4
|
+
import { completionConditionErrors, validateSuperpowersState } from "./superpowers-task-validator.js";
|
|
5
5
|
export async function runSliceGate(workdir, sliceId) {
|
|
6
6
|
const state = await loadSuperpowersState(workdir);
|
|
7
7
|
const slice = state.slices.find((item) => item.slice_id === sliceId);
|
|
@@ -31,7 +31,9 @@ export async function runFinalGate(workdir) {
|
|
|
31
31
|
await deriveSuperpowersArtifacts(workdir);
|
|
32
32
|
const report = await validateSuperpowersState(workdir, [workdir]);
|
|
33
33
|
const latest = await loadSuperpowersState(workdir);
|
|
34
|
-
const
|
|
34
|
+
const completionErrors = completionConditionErrors(latest);
|
|
35
|
+
const errors = [...new Set([...report.errors, ...completionErrors])];
|
|
36
|
+
const complete = errors.length === 0;
|
|
35
37
|
latest.final.product_goal_complete = complete;
|
|
36
38
|
latest.meta.product_goal_complete = complete;
|
|
37
39
|
latest.final.acceptance_target_status = complete ? "complete" : "partial";
|
|
@@ -39,9 +41,9 @@ export async function runFinalGate(workdir) {
|
|
|
39
41
|
latest.final.audit_task_complete = true;
|
|
40
42
|
latest.meta.audit_task_complete = true;
|
|
41
43
|
latest.final.completion_basis = complete ? ["all_required_acs_complete", "validator_passed", "auditor_no_blocker"] : [];
|
|
42
|
-
latest.gates.validator = { status:
|
|
44
|
+
latest.gates.validator = { status: errors.length === 0 ? "pass" : "blocked", errors };
|
|
43
45
|
await saveSuperpowersState(workdir, latest);
|
|
44
46
|
await deriveSuperpowersArtifacts(workdir);
|
|
45
47
|
await appendSuperpowersEvent(workdir, "final_gate", { product_goal_complete: complete });
|
|
46
|
-
return { product_goal_complete: complete, errors
|
|
48
|
+
return { product_goal_complete: complete, errors };
|
|
47
49
|
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type SuperpowersAcceptanceCriterion, type SuperpowersPlanItem, type SuperpowersProductArchitectureScope } from "./superpowers-task-state-schema.js";
|
|
2
|
+
export declare const DEFAULT_LAYERS: string[];
|
|
3
|
+
export declare function parseProductArchitectureScope(content: string, sourceFile: string): SuperpowersProductArchitectureScope;
|
|
4
|
+
export declare function parsePlanItems(content: string, sourceFile: string): Record<string, SuperpowersPlanItem>;
|
|
5
|
+
export declare function parseAcceptanceCriteria(content: string, sourceFile: string): Record<string, SuperpowersAcceptanceCriterion>;
|