project-tiny-context-harness 0.2.83 → 0.2.84
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 +5 -5
- package/assets/README.md +8 -8
- package/assets/README.zh-CN.md +4 -4
- package/assets/protected-harness-baseline.json +5 -3
- package/assets/skills/composite-long-task-workflow/SKILL.md +11 -5
- package/assets/skills/composite-long-task-workflow/assets/execution-binding.template.md +14 -0
- package/assets/skills/composite-long-task-workflow/assets/goal-objective.template.md +2 -2
- package/assets/skills/composite-long-task-workflow/references/composite-long-task-workflow-protocol.md +39 -25
- package/dist/commands/composite-long-task.js +15 -3
- package/dist/lib/superpowers-task-command-run-correlation.d.ts +8 -0
- package/dist/lib/superpowers-task-command-run-correlation.js +103 -0
- package/dist/lib/superpowers-task-completion-output.d.ts +52 -0
- package/dist/lib/superpowers-task-completion-output.js +228 -0
- package/dist/lib/superpowers-task-current-evidence.js +22 -0
- package/dist/lib/superpowers-task-derive.js +55 -8
- package/dist/lib/superpowers-task-evidence-kernel.d.ts +7 -0
- package/dist/lib/superpowers-task-evidence-kernel.js +67 -71
- package/dist/lib/superpowers-task-evidence.js +7 -1
- package/dist/lib/superpowers-task-final-card.d.ts +3 -0
- package/dist/lib/superpowers-task-final-card.js +24 -0
- package/dist/lib/superpowers-task-gates.d.ts +2 -2
- package/dist/lib/superpowers-task-gates.js +67 -27
- package/dist/lib/superpowers-task-harness-drift.js +6 -2
- package/dist/lib/superpowers-task-protected-baseline.js +21 -2
- package/dist/lib/superpowers-task-state-schema.d.ts +17 -0
- package/dist/lib/superpowers-task-state.js +10 -1
- package/dist/lib/superpowers-task-unregistered-evidence.d.ts +11 -0
- package/dist/lib/superpowers-task-unregistered-evidence.js +72 -0
- package/dist/lib/superpowers-task-validator.js +38 -0
- package/package.json +69 -69
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { listFiles, readText } from "./fs.js";
|
|
3
|
+
import { isRecord } from "./superpowers-task-state-schema.js";
|
|
4
|
+
export async function scanUnregisteredAssertionEvidence(workdir, state) {
|
|
5
|
+
const registeredPaths = new Set();
|
|
6
|
+
for (const evidence of state.evidence ?? []) {
|
|
7
|
+
for (const candidate of [evidence.artifact_path, ...(evidence.artifact_paths ?? []), ...(evidence.assertion_result?.artifacts ?? [])]) {
|
|
8
|
+
if (candidate) {
|
|
9
|
+
registeredPaths.add(slash(candidate));
|
|
10
|
+
registeredPaths.add(slash(path.join(workdir, candidate)));
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
const ignored = [];
|
|
15
|
+
for (const file of await listFiles(workdir)) {
|
|
16
|
+
if (!/\.json$/i.test(file)) {
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
const relative = slash(path.relative(workdir, file));
|
|
20
|
+
if (registeredPaths.has(relative) || registeredPaths.has(slash(file))) {
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
const parsed = await readJson(file);
|
|
24
|
+
const assertion = assertionRecord(parsed);
|
|
25
|
+
if (!assertion) {
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
const status = String(assertion.status ?? "");
|
|
29
|
+
if (status !== "passed") {
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
ignored.push({
|
|
33
|
+
path: relative,
|
|
34
|
+
status,
|
|
35
|
+
target_ac_ids: asStringArray(assertion.target_ac_ids),
|
|
36
|
+
target_proof_layers: asStringArray(assertion.target_proof_layers)
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
ignored,
|
|
41
|
+
errors: ignored.map((item) => `ignored_unregistered_evidence: unregistered assertion JSON ${item.path} status=passed is not proof`)
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
async function readJson(file) {
|
|
45
|
+
try {
|
|
46
|
+
return JSON.parse(await readText(file));
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function assertionRecord(value) {
|
|
53
|
+
if (!isRecord(value)) {
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
if (looksLikeAssertion(value)) {
|
|
57
|
+
return value;
|
|
58
|
+
}
|
|
59
|
+
if (isRecord(value.assertion_result) && looksLikeAssertion(value.assertion_result)) {
|
|
60
|
+
return value.assertion_result;
|
|
61
|
+
}
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
function looksLikeAssertion(value) {
|
|
65
|
+
return /assertion-result-v\d+/i.test(String(value.schema_version ?? "")) || (typeof value.status === "string" && Array.isArray(value.target_ac_ids));
|
|
66
|
+
}
|
|
67
|
+
function asStringArray(value) {
|
|
68
|
+
return Array.isArray(value) ? value.map(String).filter(Boolean) : [];
|
|
69
|
+
}
|
|
70
|
+
function slash(value) {
|
|
71
|
+
return value.replace(/\\/g, "/");
|
|
72
|
+
}
|
|
@@ -12,6 +12,7 @@ import { loadSuperpowersState, sha256 } from "./superpowers-task-state.js";
|
|
|
12
12
|
import { validateCanonicalStatuses } from "./superpowers-task-status.js";
|
|
13
13
|
import { isRecord } from "./superpowers-task-state-schema.js";
|
|
14
14
|
import { evaluateProofLayerAssertions, isUiBrowserLayer, proofLayerName } from "./superpowers-task-assertions.js";
|
|
15
|
+
import { completionOutputContractFromState, completionPhraseFindingMessages, scanGeneratedCompletionOutputSurfaces } from "./superpowers-task-completion-output.js";
|
|
15
16
|
export async function validateSuperpowersState(projectRoot, args = []) {
|
|
16
17
|
const info = [];
|
|
17
18
|
const warnings = [];
|
|
@@ -47,12 +48,49 @@ export async function validateSuperpowersState(projectRoot, args = []) {
|
|
|
47
48
|
validateAuditor(state, errors);
|
|
48
49
|
validateFinalCompletion(state, errors);
|
|
49
50
|
errors.push(...(await derivedMatchesState(targetDir, state)));
|
|
51
|
+
await validateCompletionOutputConsistency(targetDir, state, errors);
|
|
50
52
|
info.push(`checked superpowers task state ${repoRelative(projectRoot, targetDir)} plan_items=${Object.keys(state.graph?.plan_items ?? {}).length} acs=${Object.keys(state.graph?.acceptance_criteria ?? {}).length} evidence=${state.evidence?.length ?? 0}`);
|
|
51
53
|
if (errors.length === 0) {
|
|
52
54
|
info.push("Superpowers task state validation passed");
|
|
53
55
|
}
|
|
54
56
|
return { info, warnings, hygiene, errors };
|
|
55
57
|
}
|
|
58
|
+
async function validateCompletionOutputConsistency(workdir, state, errors) {
|
|
59
|
+
const contract = completionOutputContractFromState(state);
|
|
60
|
+
const finalRecord = state.final;
|
|
61
|
+
const gate = isRecord(state.gates?.final_gate) ? state.gates.final_gate : {};
|
|
62
|
+
const storedStatus = finalRecord.completion_output_status ?? (typeof gate.completion_output_status === "string" ? gate.completion_output_status : undefined);
|
|
63
|
+
if (storedStatus && storedStatus !== contract.completion_output_status) {
|
|
64
|
+
errors.push(`completion_output_status mismatch: expected ${contract.completion_output_status}, found ${storedStatus}`);
|
|
65
|
+
}
|
|
66
|
+
if (contract.completion_output_status === "accept" && state.final.product_goal_complete !== true) {
|
|
67
|
+
errors.push("completion_output_status=accept but product_goal_complete is not true");
|
|
68
|
+
}
|
|
69
|
+
if (finalRecord.final_answer_allowed !== undefined && finalRecord.final_answer_allowed !== contract.final_answer_allowed) {
|
|
70
|
+
errors.push(`final_answer_allowed mismatch: expected ${contract.final_answer_allowed}, found ${finalRecord.final_answer_allowed}`);
|
|
71
|
+
}
|
|
72
|
+
if (finalRecord.required_user_visible_status && finalRecord.required_user_visible_status !== contract.required_user_visible_status) {
|
|
73
|
+
errors.push(`required_user_visible_status mismatch: expected ${contract.required_user_visible_status}, found ${finalRecord.required_user_visible_status}`);
|
|
74
|
+
}
|
|
75
|
+
if (finalRecord.exit_code !== undefined && finalRecord.exit_code !== contract.exit_code) {
|
|
76
|
+
errors.push(`completion output exit_code mismatch: expected ${contract.exit_code}, found ${finalRecord.exit_code}`);
|
|
77
|
+
}
|
|
78
|
+
errors.push(...completionPhraseFindingMessages(await scanGeneratedCompletionOutputSurfaces(workdir, contract)));
|
|
79
|
+
await validateMarkdownCompletionStatus(workdir, contract.completion_output_status, errors);
|
|
80
|
+
}
|
|
81
|
+
async function validateMarkdownCompletionStatus(workdir, expectedStatus, errors) {
|
|
82
|
+
for (const relative of ["derived/final-summary.md", "derived/final-card.md", "derived/local-audit.md"]) {
|
|
83
|
+
const file = path.join(workdir, ...relative.split("/"));
|
|
84
|
+
if (!(await pathExists(file))) {
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
const text = await readText(file);
|
|
88
|
+
const match = /^completion_output_status:\s*(\S+)\s*$/im.exec(text);
|
|
89
|
+
if (match && match[1] !== expectedStatus) {
|
|
90
|
+
errors.push(`${relative} completion_output_status mismatch: expected ${expectedStatus}, found ${match[1]}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
56
94
|
async function validateSourceHashes(workdir, state, errors) {
|
|
57
95
|
for (const [key, source] of Object.entries(state.sources ?? {})) {
|
|
58
96
|
const file = path.join(workdir, source.path);
|
package/package.json
CHANGED
|
@@ -1,69 +1,69 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "project-tiny-context-harness",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"description": "Minimal project memory and validation harness for AI coding agents.",
|
|
5
|
-
"license": "MIT",
|
|
6
|
-
"author": "Seven128",
|
|
7
|
-
"homepage": "https://github.com/Seven128/project-tiny-context-harness#readme",
|
|
8
|
-
"repository": {
|
|
9
|
-
"type": "git",
|
|
10
|
-
"url": "git+https://github.com/Seven128/project-tiny-context-harness.git",
|
|
11
|
-
"directory": "packages/ty-context"
|
|
12
|
-
},
|
|
13
|
-
"bugs": {
|
|
14
|
-
"url": "https://github.com/Seven128/project-tiny-context-harness/issues"
|
|
15
|
-
},
|
|
16
|
-
"keywords": [
|
|
17
|
-
"ai-agents",
|
|
18
|
-
"coding-agent",
|
|
19
|
-
"codex",
|
|
20
|
-
"claude-code",
|
|
21
|
-
"cursor",
|
|
22
|
-
"gemini-cli",
|
|
23
|
-
"opencode",
|
|
24
|
-
"agent-context",
|
|
25
|
-
"context-engineering",
|
|
26
|
-
"context-management",
|
|
27
|
-
"agents-md",
|
|
28
|
-
"project-memory",
|
|
29
|
-
"agent-memory",
|
|
30
|
-
"ai-coding",
|
|
31
|
-
"multi-agent",
|
|
32
|
-
"llm",
|
|
33
|
-
"developer-tools",
|
|
34
|
-
"developer-productivity",
|
|
35
|
-
"cli",
|
|
36
|
-
"ty-context",
|
|
37
|
-
"workflow"
|
|
38
|
-
],
|
|
39
|
-
"type": "module",
|
|
40
|
-
"bin": {
|
|
41
|
-
"ty-context": "dist/cli.js"
|
|
42
|
-
},
|
|
43
|
-
"files": [
|
|
44
|
-
"README.md",
|
|
45
|
-
"dist",
|
|
46
|
-
"assets",
|
|
47
|
-
"migrations",
|
|
48
|
-
"source-mappings.yaml"
|
|
49
|
-
],
|
|
50
|
-
"scripts": {
|
|
51
|
-
"build": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.json",
|
|
52
|
-
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
53
|
-
"test:built": "node --test ../../tests/ty-context/*.test.mjs",
|
|
54
|
-
"test": "npm run build && node --test ../../tests/ty-context/*.test.mjs",
|
|
55
|
-
"prepack": "npm run build"
|
|
56
|
-
},
|
|
57
|
-
"engines": {
|
|
58
|
-
"node": ">=24"
|
|
59
|
-
},
|
|
60
|
-
"dependencies": {
|
|
61
|
-
"@google/design.md": "^0.3.0",
|
|
62
|
-
"impeccable": "^3.1.0",
|
|
63
|
-
"yaml": "^2.9.0"
|
|
64
|
-
},
|
|
65
|
-
"devDependencies": {
|
|
66
|
-
"@types/node": "^26.0.0",
|
|
67
|
-
"typescript": "^5.5.0"
|
|
68
|
-
}
|
|
69
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "project-tiny-context-harness",
|
|
3
|
+
"version": "0.2.84",
|
|
4
|
+
"description": "Minimal project memory and validation harness for AI coding agents.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Seven128",
|
|
7
|
+
"homepage": "https://github.com/Seven128/project-tiny-context-harness#readme",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/Seven128/project-tiny-context-harness.git",
|
|
11
|
+
"directory": "packages/ty-context"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/Seven128/project-tiny-context-harness/issues"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"ai-agents",
|
|
18
|
+
"coding-agent",
|
|
19
|
+
"codex",
|
|
20
|
+
"claude-code",
|
|
21
|
+
"cursor",
|
|
22
|
+
"gemini-cli",
|
|
23
|
+
"opencode",
|
|
24
|
+
"agent-context",
|
|
25
|
+
"context-engineering",
|
|
26
|
+
"context-management",
|
|
27
|
+
"agents-md",
|
|
28
|
+
"project-memory",
|
|
29
|
+
"agent-memory",
|
|
30
|
+
"ai-coding",
|
|
31
|
+
"multi-agent",
|
|
32
|
+
"llm",
|
|
33
|
+
"developer-tools",
|
|
34
|
+
"developer-productivity",
|
|
35
|
+
"cli",
|
|
36
|
+
"ty-context",
|
|
37
|
+
"workflow"
|
|
38
|
+
],
|
|
39
|
+
"type": "module",
|
|
40
|
+
"bin": {
|
|
41
|
+
"ty-context": "dist/cli.js"
|
|
42
|
+
},
|
|
43
|
+
"files": [
|
|
44
|
+
"README.md",
|
|
45
|
+
"dist",
|
|
46
|
+
"assets",
|
|
47
|
+
"migrations",
|
|
48
|
+
"source-mappings.yaml"
|
|
49
|
+
],
|
|
50
|
+
"scripts": {
|
|
51
|
+
"build": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.json",
|
|
52
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
53
|
+
"test:built": "node --test ../../tests/ty-context/*.test.mjs",
|
|
54
|
+
"test": "npm run build && node --test ../../tests/ty-context/*.test.mjs",
|
|
55
|
+
"prepack": "npm run build"
|
|
56
|
+
},
|
|
57
|
+
"engines": {
|
|
58
|
+
"node": ">=24"
|
|
59
|
+
},
|
|
60
|
+
"dependencies": {
|
|
61
|
+
"@google/design.md": "^0.3.0",
|
|
62
|
+
"impeccable": "^3.1.0",
|
|
63
|
+
"yaml": "^2.9.0"
|
|
64
|
+
},
|
|
65
|
+
"devDependencies": {
|
|
66
|
+
"@types/node": "^26.0.0",
|
|
67
|
+
"typescript": "^5.5.0"
|
|
68
|
+
}
|
|
69
|
+
}
|