project-tiny-context-harness 0.2.82 → 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.
Files changed (55) hide show
  1. package/README.md +10 -4
  2. package/assets/README.md +13 -7
  3. package/assets/README.zh-CN.md +8 -2
  4. package/assets/protected-harness-baseline.json +20 -0
  5. package/assets/skills/composite-long-task-workflow/SKILL.md +27 -3
  6. package/assets/skills/composite-long-task-workflow/assets/execution-binding.template.md +24 -0
  7. package/assets/skills/composite-long-task-workflow/assets/goal-objective.template.md +6 -15
  8. package/assets/skills/composite-long-task-workflow/references/composite-long-task-workflow-protocol.md +51 -25
  9. package/dist/commands/composite-long-task.js +60 -5
  10. package/dist/lib/superpowers-task-ac010.d.ts +6 -0
  11. package/dist/lib/superpowers-task-ac010.js +26 -0
  12. package/dist/lib/superpowers-task-assertion-normalizers.js +4 -0
  13. package/dist/lib/superpowers-task-assertions.js +18 -4
  14. package/dist/lib/superpowers-task-attempt.d.ts +4 -0
  15. package/dist/lib/superpowers-task-attempt.js +102 -0
  16. package/dist/lib/superpowers-task-command-run-correlation.d.ts +8 -0
  17. package/dist/lib/superpowers-task-command-run-correlation.js +103 -0
  18. package/dist/lib/superpowers-task-command-specs.d.ts +3 -0
  19. package/dist/lib/superpowers-task-command-specs.js +52 -0
  20. package/dist/lib/superpowers-task-compile.d.ts +4 -1
  21. package/dist/lib/superpowers-task-compile.js +7 -1
  22. package/dist/lib/superpowers-task-completion-output.d.ts +52 -0
  23. package/dist/lib/superpowers-task-completion-output.js +228 -0
  24. package/dist/lib/superpowers-task-contradictions.d.ts +6 -0
  25. package/dist/lib/superpowers-task-contradictions.js +126 -0
  26. package/dist/lib/superpowers-task-current-evidence.d.ts +3 -0
  27. package/dist/lib/superpowers-task-current-evidence.js +176 -0
  28. package/dist/lib/superpowers-task-derive.js +69 -8
  29. package/dist/lib/superpowers-task-evidence-kernel.d.ts +19 -0
  30. package/dist/lib/superpowers-task-evidence-kernel.js +347 -0
  31. package/dist/lib/superpowers-task-evidence-records.d.ts +2 -0
  32. package/dist/lib/superpowers-task-evidence-records.js +55 -0
  33. package/dist/lib/superpowers-task-evidence.d.ts +10 -0
  34. package/dist/lib/superpowers-task-evidence.js +147 -0
  35. package/dist/lib/superpowers-task-final-card.d.ts +3 -0
  36. package/dist/lib/superpowers-task-final-card.js +24 -0
  37. package/dist/lib/superpowers-task-gates.d.ts +2 -2
  38. package/dist/lib/superpowers-task-gates.js +87 -35
  39. package/dist/lib/superpowers-task-harness-drift.d.ts +11 -0
  40. package/dist/lib/superpowers-task-harness-drift.js +90 -0
  41. package/dist/lib/superpowers-task-protected-baseline.d.ts +10 -0
  42. package/dist/lib/superpowers-task-protected-baseline.js +66 -0
  43. package/dist/lib/superpowers-task-state-schema.d.ts +116 -3
  44. package/dist/lib/superpowers-task-state-schema.js +23 -1
  45. package/dist/lib/superpowers-task-state-shape.d.ts +3 -0
  46. package/dist/lib/superpowers-task-state-shape.js +50 -0
  47. package/dist/lib/superpowers-task-state.js +17 -37
  48. package/dist/lib/superpowers-task-status.js +11 -1
  49. package/dist/lib/superpowers-task-under-specified.d.ts +7 -0
  50. package/dist/lib/superpowers-task-under-specified.js +61 -0
  51. package/dist/lib/superpowers-task-unregistered-evidence.d.ts +11 -0
  52. package/dist/lib/superpowers-task-unregistered-evidence.js +72 -0
  53. package/dist/lib/superpowers-task-validator.js +43 -27
  54. package/package.json +69 -69
  55. package/source-mappings.yaml +3 -0
@@ -0,0 +1,176 @@
1
+ import { createHash } from "node:crypto";
2
+ import path from "node:path";
3
+ import { pathExists, readText } from "./fs.js";
4
+ import { normalizeProofLayerName } from "./superpowers-task-fields.js";
5
+ export function evaluateCurrentAttemptEvidence(state, evidence, layerId) {
6
+ if (!state.current_attempt_id) {
7
+ return [];
8
+ }
9
+ const failures = [];
10
+ const label = `proof layer ${layerId} evidence ${evidence.evidence_id}`;
11
+ if (evidence.schema_version !== "evidence-record-v2") {
12
+ failures.push(`${label} must be EvidenceRecordV2 for current-attempt machine completion`);
13
+ }
14
+ if (evidence.task_attempt_id !== state.current_attempt_id) {
15
+ failures.push(`${label} stale evidence from old attempt ${evidence.task_attempt_id || "(missing)"}; expected current attempt ${state.current_attempt_id}`);
16
+ }
17
+ for (const field of [
18
+ "generated_at",
19
+ "source_bundle_hash",
20
+ "product_source_hash",
21
+ "technical_plan_hash",
22
+ "acceptance_checklist_hash",
23
+ "git_head",
24
+ "git_status_short",
25
+ "tracked_diff_hash",
26
+ "relevant_untracked_hash",
27
+ "worktree_fingerprint",
28
+ "command_spec_id",
29
+ "command_run_id",
30
+ "command_line",
31
+ "artifact_path",
32
+ "artifact_sha256",
33
+ "artifact_mtime"
34
+ ]) {
35
+ if (!evidence[field]) {
36
+ failures.push(`${label} EvidenceRecordV2 missing ${field}`);
37
+ }
38
+ }
39
+ const attempt = state.attempts?.find((item) => item.task_attempt_id === state.current_attempt_id);
40
+ if (attempt && evidence.source_bundle_hash && evidence.source_bundle_hash !== attempt.source_bundle_hash) {
41
+ failures.push(`${label} stale evidence source_bundle_hash mismatch for current attempt`);
42
+ }
43
+ if (attempt && evidence.product_source_hash && evidence.product_source_hash !== attempt.product_source_hash) {
44
+ failures.push(`${label} stale evidence product_source_hash mismatch for current attempt`);
45
+ }
46
+ if (attempt && evidence.technical_plan_hash && evidence.technical_plan_hash !== attempt.technical_plan_hash) {
47
+ failures.push(`${label} stale evidence technical_plan_hash mismatch for current attempt`);
48
+ }
49
+ if (attempt && evidence.acceptance_checklist_hash && evidence.acceptance_checklist_hash !== attempt.acceptance_checklist_hash) {
50
+ failures.push(`${label} stale evidence acceptance_checklist_hash mismatch for current attempt`);
51
+ }
52
+ if (attempt && evidence.git_head && evidence.git_head !== attempt.git_head) {
53
+ failures.push(`${label} stale evidence git_head mismatch for current attempt`);
54
+ }
55
+ if (attempt && evidence.git_status_short !== undefined && evidence.git_status_short !== attempt.git_status_short) {
56
+ failures.push(`${label} stale evidence git_status_short mismatch for current attempt`);
57
+ }
58
+ if (attempt && evidence.tracked_diff_hash !== undefined && evidence.tracked_diff_hash !== attempt.tracked_diff_hash) {
59
+ failures.push(`${label} stale evidence tracked_diff_hash mismatch for current attempt`);
60
+ }
61
+ if (attempt && evidence.relevant_untracked_hash !== undefined && evidence.relevant_untracked_hash !== attempt.relevant_untracked_hash) {
62
+ failures.push(`${label} stale evidence relevant_untracked_hash mismatch for current attempt`);
63
+ }
64
+ if (attempt && evidence.worktree_fingerprint && evidence.worktree_fingerprint !== attempt.worktree_fingerprint) {
65
+ failures.push(`${label} stale evidence worktree_fingerprint mismatch for current attempt`);
66
+ }
67
+ if (attempt && (attempt.git_status_short || attempt.relevant_untracked_hash !== "none") && evidence.covers_dirty_worktree !== true) {
68
+ failures.push(`${label} dirty worktree evidence must set covers_dirty_worktree=true for current attempt`);
69
+ }
70
+ if (attempt && evidence.generated_at && Date.parse(evidence.generated_at) < Date.parse(attempt.started_at)) {
71
+ failures.push(`${label} stale evidence generated_at predates current attempt`);
72
+ }
73
+ if (attempt && evidence.artifact_mtime && Date.parse(evidence.artifact_mtime) < Date.parse(attempt.started_at)) {
74
+ failures.push(`${label} stale evidence artifact_mtime predates current attempt`);
75
+ }
76
+ const commandRun = state.command_runs?.find((item) => item.command_run_id === evidence.command_run_id);
77
+ if (!commandRun) {
78
+ failures.push(`${label} missing command run ${evidence.command_run_id || "(missing)"}`);
79
+ }
80
+ else {
81
+ if (commandRun.task_attempt_id !== state.current_attempt_id) {
82
+ failures.push(`${label} command run ${commandRun.command_run_id} is from old attempt ${commandRun.task_attempt_id}`);
83
+ }
84
+ if (commandRun.exit_code !== 0) {
85
+ failures.push(`${label} command run ${commandRun.command_run_id} exit_code=${commandRun.exit_code}; expected 0`);
86
+ }
87
+ if (!commandRun.completed_at) {
88
+ failures.push(`${label} command run ${commandRun.command_run_id} missing completed_at`);
89
+ }
90
+ if (commandRun.command_spec_id !== evidence.command_spec_id) {
91
+ failures.push(`${label} command_spec_id mismatch between evidence and command run`);
92
+ }
93
+ }
94
+ const commandSpec = state.required_command_specs?.find((item) => item.command_spec_id === evidence.command_spec_id);
95
+ if (!commandSpec) {
96
+ failures.push(`${label} missing required command spec ${evidence.command_spec_id || "(missing)"}`);
97
+ }
98
+ else if (commandSpec.ac_id !== proofLayerAcId(layerId) || !commandSpec.proof_layers.map(normalizeProofLayerName).includes(proofLayerName(layerId))) {
99
+ failures.push(`${label} command spec does not cover ${layerId}`);
100
+ }
101
+ const ac = state.graph.acceptance_criteria?.[proofLayerAcId(layerId)];
102
+ const targetAcIds = evidence.target_ac_ids ?? evidence.assertion_result?.target_ac_ids ?? [];
103
+ if (!targetAcIds.includes(proofLayerAcId(layerId))) {
104
+ failures.push(`${label} target_ac_ids ${targetAcIds.join(", ") || "(none)"} do not include ${proofLayerAcId(layerId)}`);
105
+ }
106
+ const expectedPiIds = ac?.related_plan_items ?? [];
107
+ const targetPiIds = evidence.target_pi_ids ?? evidence.assertion_result?.target_pi_ids ?? [];
108
+ for (const piId of expectedPiIds) {
109
+ if (!targetPiIds.includes(piId)) {
110
+ failures.push(`${label} target_pi_ids ${targetPiIds.join(", ") || "(none)"} do not include related plan item ${piId}`);
111
+ }
112
+ }
113
+ const targetLayers = (evidence.target_proof_layers ?? evidence.assertion_result?.target_proof_layers ?? []).map(normalizeLayerId);
114
+ if (!targetLayers.includes(normalizeLayerId(layerId)) && !targetLayers.includes(proofLayerName(layerId))) {
115
+ failures.push(`${label} target_proof_layers ${targetLayers.join(", ") || "(none)"} do not include ${layerId}`);
116
+ }
117
+ if (evidence.assertion_result?.schema_version !== "assertion-result-v2") {
118
+ failures.push(`${label} assertion_result.schema_version must be assertion-result-v2 for current-attempt machine completion`);
119
+ }
120
+ return failures;
121
+ }
122
+ export async function evaluateCurrentAttemptArtifact(workdir, evidence, layerId) {
123
+ const label = `proof layer ${layerId} evidence ${evidence.evidence_id}`;
124
+ return validateArtifact(workdir, evidence, label);
125
+ }
126
+ async function validateArtifact(workdir, evidence, label) {
127
+ const failures = [];
128
+ if (!evidence.artifact_path) {
129
+ failures.push(`${label} EvidenceRecordV2 missing artifact_path`);
130
+ return failures;
131
+ }
132
+ const artifact = await resolveArtifactPath(workdir, evidence.artifact_path);
133
+ if (!artifact) {
134
+ failures.push(`${label} artifact_path does not exist: ${evidence.artifact_path}`);
135
+ return failures;
136
+ }
137
+ if (evidence.artifact_sha256) {
138
+ const actual = createHash("sha256").update(await readText(artifact)).digest("hex");
139
+ if (actual !== evidence.artifact_sha256) {
140
+ failures.push(`${label} stale evidence artifact_sha256 mismatch for ${evidence.artifact_path}`);
141
+ }
142
+ }
143
+ return failures;
144
+ }
145
+ async function resolveArtifactPath(workdir, artifactPath) {
146
+ const candidates = path.isAbsolute(artifactPath) ? [artifactPath] : [path.join(workdir, artifactPath), path.join(projectRootFromWorkdir(workdir), artifactPath)];
147
+ for (const candidate of candidates) {
148
+ if (await pathExists(candidate)) {
149
+ return candidate;
150
+ }
151
+ }
152
+ return undefined;
153
+ }
154
+ function proofLayerName(layerId) {
155
+ const raw = layerId.includes(".") ? layerId.slice(layerId.lastIndexOf(".") + 1) : layerId;
156
+ return normalizeProofLayerName(raw);
157
+ }
158
+ function proofLayerAcId(layerId) {
159
+ return layerId.includes(".") ? layerId.slice(0, layerId.lastIndexOf(".")) : "";
160
+ }
161
+ function normalizeLayerId(layerId) {
162
+ if (!layerId.includes(".")) {
163
+ return normalizeProofLayerName(layerId);
164
+ }
165
+ const acId = proofLayerAcId(layerId);
166
+ return `${acId}.${proofLayerName(layerId)}`;
167
+ }
168
+ function projectRootFromWorkdir(workdir) {
169
+ const normalized = workdir.replace(/\\/g, "/");
170
+ const marker = "/tmp/ty-context/plan-acceptance/";
171
+ const index = normalized.lastIndexOf(marker);
172
+ if (index < 0) {
173
+ return workdir;
174
+ }
175
+ return normalized.slice(0, index);
176
+ }
@@ -2,9 +2,12 @@ import path from "node:path";
2
2
  import { ensureDir, pathExists, readText, writeTextIfChanged } from "./fs.js";
3
3
  import { stableJson, loadSuperpowersState } from "./superpowers-task-state.js";
4
4
  import { evaluateProofLayerAssertions } from "./superpowers-task-assertions.js";
5
+ import { completionOutputContractFromState } from "./superpowers-task-completion-output.js";
6
+ import { renderFinalCard } from "./superpowers-task-final-card.js";
5
7
  export async function deriveSuperpowersArtifacts(workdir) {
6
8
  const state = await loadSuperpowersState(workdir);
7
9
  const derived = deriveObjects(state);
10
+ const contract = completionOutputContractFromState(state);
8
11
  const derivedDir = path.join(workdir, "derived");
9
12
  await ensureDir(derivedDir);
10
13
  const files = [];
@@ -14,11 +17,12 @@ export async function deriveSuperpowersArtifacts(workdir) {
14
17
  await writeDerived(files, path.join(derivedDir, "evidence-index.json"), stableJson(deriveEvidenceIndex(state)));
15
18
  await writeDerived(files, path.join(derivedDir, "plan-conformance-matrix.md"), matrixMarkdown(derived.matrix));
16
19
  await writeDerived(files, path.join(derivedDir, "final-acceptance-verdict.md"), verdictMarkdown(derived.verdict));
17
- await writeDerived(files, path.join(derivedDir, "local-audit.md"), localAuditMarkdown(state));
20
+ await writeDerived(files, path.join(derivedDir, "local-audit.md"), localAuditMarkdown(state, contract));
18
21
  await writeDerived(files, path.join(derivedDir, "progress-ledger.md"), progressMarkdown(derived.progress));
19
22
  await writeDerived(files, path.join(derivedDir, "evidence-index.md"), evidenceMarkdown(state));
20
23
  await writeDerived(files, path.join(derivedDir, "context-alignment.md"), contextMarkdown(state));
21
- await writeDerived(files, path.join(derivedDir, "final-summary.md"), finalSummaryMarkdown(state, derived.verdict));
24
+ await writeDerived(files, path.join(derivedDir, "final-summary.md"), finalSummaryMarkdown(state, derived.verdict, contract));
25
+ await writeDerived(files, path.join(derivedDir, "final-card.md"), renderFinalCard(contract, state));
22
26
  return { matrix: derived.matrix, verdict: derived.verdict, files };
23
27
  }
24
28
  export function deriveObjects(state) {
@@ -140,9 +144,28 @@ export function deriveObjects(state) {
140
144
  .slice(0, 5)
141
145
  .map(([layerId]) => layerId)
142
146
  };
147
+ const contract = completionOutputContractFromState(state);
148
+ const completionMetadata = {
149
+ completion_output_status: contract.completion_output_status,
150
+ final_answer_allowed: contract.final_answer_allowed,
151
+ required_user_visible_status: contract.required_user_visible_status,
152
+ product_goal_complete: contract.product_goal_complete,
153
+ acceptance_target_status: contract.acceptance_target_status,
154
+ audit_task_complete: contract.audit_task_complete
155
+ };
143
156
  return {
144
- matrix: { overall_status: allComplete ? "complete" : "partial", items: matrixRows },
145
- verdict: { overall_status: allComplete ? "complete" : "partial", acceptance_items: verdictRows },
157
+ matrix: {
158
+ diagnostic_scope: "plan_conformance_rows_only",
159
+ overall_status: allComplete ? "complete" : "partial",
160
+ ...completionMetadata,
161
+ items: matrixRows
162
+ },
163
+ verdict: {
164
+ diagnostic_scope: "acceptance_evidence_rows_only",
165
+ overall_status: allComplete ? "complete" : "partial",
166
+ ...completionMetadata,
167
+ acceptance_items: verdictRows
168
+ },
146
169
  progress
147
170
  };
148
171
  }
@@ -169,10 +192,24 @@ export function deriveEvidenceIndex(state) {
169
192
  evidence.evidence_id,
170
193
  {
171
194
  evidence_id: evidence.evidence_id,
195
+ schema_version: evidence.schema_version ?? "",
196
+ task_attempt_id: evidence.task_attempt_id ?? "",
197
+ source_bundle_hash: evidence.source_bundle_hash ?? "",
198
+ product_source_hash: evidence.product_source_hash ?? "",
199
+ technical_plan_hash: evidence.technical_plan_hash ?? "",
200
+ acceptance_checklist_hash: evidence.acceptance_checklist_hash ?? "",
172
201
  type: evidence.type,
202
+ command_spec_id: evidence.command_spec_id ?? "",
203
+ command_run_id: evidence.command_run_id ?? "",
173
204
  command: evidence.command ?? "",
174
205
  command_exit_code: evidence.command_exit_code,
206
+ artifact_path: evidence.artifact_path ?? "",
207
+ artifact_sha256: evidence.artifact_sha256 ?? "",
208
+ artifact_mtime: evidence.artifact_mtime ?? "",
175
209
  artifact_paths: evidence.artifact_paths,
210
+ target_ac_ids: evidence.target_ac_ids ?? evidence.assertion_result?.target_ac_ids ?? [],
211
+ target_pi_ids: evidence.target_pi_ids ?? evidence.assertion_result?.target_pi_ids ?? [],
212
+ target_proof_layers: evidence.target_proof_layers ?? evidence.assertion_result?.target_proof_layers ?? [],
176
213
  proves: evidence.proves,
177
214
  does_not_prove: evidence.does_not_prove,
178
215
  assertion_result: evidence.assertion_result ?? null,
@@ -275,12 +312,19 @@ missing_layer_count: ${missing}
275
312
  ${rows.map((row) => `- ${row.ac_id}: ${row.status}`).join("\n")}
276
313
  `;
277
314
  }
278
- function localAuditMarkdown(state) {
315
+ function localAuditMarkdown(state, contract) {
316
+ const auditLine = contract.completion_output_status === "accept"
317
+ ? "Final-gate accepted the current attempt."
318
+ : "Audit workflow completed; acceptance target not complete.";
279
319
  return `# Local Audit
280
320
 
281
321
  audit_task_complete: ${state.final.audit_task_complete}
282
322
  acceptance_target_status: ${state.final.acceptance_target_status}
283
323
  product_goal_complete: ${state.final.product_goal_complete}
324
+ completion_output_status: ${contract.completion_output_status}
325
+ final_answer_allowed: ${contract.final_answer_allowed}
326
+
327
+ ${auditLine}
284
328
  `;
285
329
  }
286
330
  function progressMarkdown(progress) {
@@ -312,10 +356,27 @@ Product Context Delta: ${state.context.product_context_delta}
312
356
  Technical Context Delta: ${state.context.technical_context_delta}
313
357
  `;
314
358
  }
315
- function finalSummaryMarkdown(state, verdict) {
359
+ function finalSummaryMarkdown(state, verdict, contract) {
360
+ const reasons = contract.completion_output_status === "blocked" ? contract.blocked_reasons : contract.rejection_reasons;
361
+ const reasonBlock = reasons.length > 0 ? reasons.map((reason) => `- ${reason}`).join("\n") : "- none";
362
+ const auditLine = contract.completion_output_status === "accept"
363
+ ? "Final-gate accepted the current attempt."
364
+ : "Audit workflow completed; acceptance target not complete.";
316
365
  return `# Final Summary
317
366
 
318
- overall_status: ${verdict.overall_status}
319
- product_goal_complete: ${state.final.product_goal_complete}
367
+ diagnostic_overall_status: ${verdict.overall_status}
368
+ product_goal_complete: ${contract.product_goal_complete}
369
+ acceptance_target_status: ${contract.acceptance_target_status}
370
+ completion_output_status: ${contract.completion_output_status}
371
+ final_answer_allowed: ${contract.final_answer_allowed}
372
+ required_user_visible_status: ${contract.required_user_visible_status}
373
+ exit_code: ${contract.exit_code}
374
+
375
+ Final answer: ${contract.final_answer}
376
+
377
+ ${auditLine}
378
+
379
+ Reasons:
380
+ ${reasonBlock}
320
381
  `;
321
382
  }
@@ -0,0 +1,19 @@
1
+ import { type IgnoredUnregisteredEvidence } from "./superpowers-task-unregistered-evidence.js";
2
+ import { type SuperpowersTaskState } from "./superpowers-task-state-schema.js";
3
+ export interface TrustedEvidenceKernelResult {
4
+ product_goal_complete: boolean;
5
+ acceptance_target_status: "complete" | "partial" | "blocked" | "invalidated" | "under_specified";
6
+ errors: string[];
7
+ ac_statuses: Record<string, string>;
8
+ pi_statuses: Record<string, string>;
9
+ stale_evidence_ids: string[];
10
+ ignored_unregistered_evidence: IgnoredUnregisteredEvidence[];
11
+ invalidated_evidence_ids: string[];
12
+ kernel_order: string[];
13
+ ac_findings: Record<string, string[]>;
14
+ pi_findings: Record<string, string[]>;
15
+ harness_task_final_verdict?: "passed" | "failed";
16
+ }
17
+ export declare const TRUSTED_EVIDENCE_KERNEL_ORDER: readonly ["load_three_inputs", "recompute_source_hashes", "load_task_state", "resolve_current_attempt", "load_required_command_specs", "load_command_run_records", "load_registered_evidence_records", "discard_stale_evidence", "scan_unregistered_assertion_json", "scan_contradictions", "run_ac010_bootstrap_prevention", "run_under_specified_ac_checks", "run_harness_drift_lock", "run_protected_baseline_guard", "validate_scope_conflicts", "recompute_every_ac", "recompute_every_pi", "recompute_acceptance_target_status", "recompute_product_goal_complete", "regenerate_derived", "append_final_gate_event"];
18
+ export declare function evaluateTrustedEvidenceKernel(workdir: string, providedState?: SuperpowersTaskState): Promise<TrustedEvidenceKernelResult>;
19
+ export declare function applyTrustedEvidenceKernelResult(state: SuperpowersTaskState, result: TrustedEvidenceKernelResult): void;