project-tiny-context-harness 0.6.1 → 0.7.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/README.md +29 -22
- package/assets/README.md +33 -24
- package/assets/README.zh-CN.md +46 -37
- package/assets/context_templates/architecture.md +5 -5
- package/assets/context_templates/area.md +5 -5
- package/assets/context_templates/product-surface-contract.md +11 -11
- package/assets/github/harness.yml +2 -2
- package/assets/skills/context_development_engineer/SKILL.md +17 -17
- package/assets/skills/context_full_project_export/SKILL.md +42 -42
- package/assets/skills/context_product_plan/SKILL.md +9 -9
- package/assets/skills/context_surface_contract/SKILL.md +5 -5
- package/assets/skills/context_uiux_design/SKILL.md +24 -24
- package/assets/skills/long-task-workflow/SKILL.md +9 -6
- package/assets/skills/long-task-workflow/agents/openai.yaml +4 -4
- package/assets/skills/long-task-workflow/references/authority-lifecycle.md +9 -1
- package/assets/skills/long-task-workflow/references/contract-authoring.md +22 -20
- package/assets/skills/long-task-workflow/references/evidence-design.md +13 -13
- package/assets/skills/normal-long-task/SKILL.md +12 -12
- package/assets/skills/source-plan-authoring/SKILL.md +187 -172
- package/assets/tools/validate_context.py +442 -442
- package/dist/commands/check-modularity.js +10 -10
- package/dist/commands/long-task-command-args.d.ts +3 -0
- package/dist/commands/long-task-command-args.js +20 -0
- package/dist/commands/long-task-revision.d.ts +1 -0
- package/dist/commands/long-task-revision.js +137 -0
- package/dist/commands/long-task.js +20 -120
- package/dist/lib/long-task-authority-revision-details.d.ts +2 -0
- package/dist/lib/long-task-authority-revision-details.js +10 -0
- package/dist/lib/long-task-authority-revision-diagnosis.d.ts +24 -0
- package/dist/lib/long-task-authority-revision-diagnosis.js +141 -0
- package/dist/lib/long-task-authority-revision-enforcement.d.ts +3 -1
- package/dist/lib/long-task-authority-revision-enforcement.js +17 -9
- package/dist/lib/long-task-authority-revision-summary.d.ts +11 -0
- package/dist/lib/long-task-authority-revision-summary.js +99 -0
- package/dist/lib/long-task-authority-revision-types.d.ts +44 -1
- package/dist/lib/long-task-authority-revision.js +9 -1
- package/dist/lib/long-task-delivery-compiler.d.ts +3 -0
- package/dist/lib/long-task-delivery-compiler.js +5 -2
- package/dist/lib/long-task-state.d.ts +3 -15
- package/dist/lib/long-task-status-v2.d.ts +2 -0
- package/dist/lib/long-task-status-v2.js +12 -2
- package/dist/lib/long-task-verifier-v2.d.ts +4 -0
- package/dist/lib/long-task-verifier-v2.js +1 -1
- package/migrations/README.md +8 -8
- package/package.json +5 -1
|
@@ -154,15 +154,15 @@ function parseLimit(value) {
|
|
|
154
154
|
return limit;
|
|
155
155
|
}
|
|
156
156
|
function helpText() {
|
|
157
|
-
return `ty-context check-modularity:
|
|
158
|
-
check-modularity --touched [--limit 300] [--fail-on-warning]
|
|
159
|
-
check-modularity --file <path> [--file <path> ...] [--limit 300] [--fail-on-warning]
|
|
160
|
-
check-modularity --base <ref> [--limit 300] [--fail-on-warning]
|
|
161
|
-
check-modularity --config-only
|
|
162
|
-
|
|
163
|
-
Audits physical lines, per-function statements and branch complexity, exports, state transitions and module responsibilities.
|
|
164
|
-
For --touched and --base, existing findings are reported but only new or worsened non-line complexity is a warning; physical lines remain a risk signal and new files are audited in full.
|
|
165
|
-
The default is warning-only; --fail-on-warning lets projects opt into CI enforcement.
|
|
166
|
-
Generated configs default to modularity.policy: strict_except_generated; omitted policy is treated as scoped_waivers for compatibility.
|
|
157
|
+
return `ty-context check-modularity:
|
|
158
|
+
check-modularity --touched [--limit 300] [--fail-on-warning]
|
|
159
|
+
check-modularity --file <path> [--file <path> ...] [--limit 300] [--fail-on-warning]
|
|
160
|
+
check-modularity --base <ref> [--limit 300] [--fail-on-warning]
|
|
161
|
+
check-modularity --config-only
|
|
162
|
+
|
|
163
|
+
Audits physical lines, per-function statements and branch complexity, exports, state transitions and module responsibilities.
|
|
164
|
+
For --touched and --base, existing findings are reported but only new or worsened non-line complexity is a warning; physical lines remain a risk signal and new files are audited in full.
|
|
165
|
+
The default is warning-only; --fail-on-warning lets projects opt into CI enforcement.
|
|
166
|
+
Generated configs default to modularity.policy: strict_except_generated; omitted policy is treated as scoped_waivers for compatibility.
|
|
167
167
|
Risks can be waived only through lifecycle-complete <harnessRoot>/config.yaml modularity.waivers when policy is scoped_waivers.`;
|
|
168
168
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export function option(args, name) {
|
|
2
|
+
const indexes = args.flatMap((value, index) => value === name ? [index] : []);
|
|
3
|
+
if (indexes.length > 1)
|
|
4
|
+
throw new Error(`duplicate option: ${name}`);
|
|
5
|
+
if (!indexes.length)
|
|
6
|
+
return undefined;
|
|
7
|
+
const value = args[indexes[0] + 1];
|
|
8
|
+
if (!value || value.startsWith("--"))
|
|
9
|
+
throw new Error(`${name} requires a value`);
|
|
10
|
+
return value;
|
|
11
|
+
}
|
|
12
|
+
export function rejectOptions(args, allowed) {
|
|
13
|
+
for (let index = 0; index < args.length; index += 2)
|
|
14
|
+
if (!allowed.includes(args[index]) || !args[index + 1])
|
|
15
|
+
throw new Error(`Unknown or injected arguments: ${args.join(" ")}`);
|
|
16
|
+
}
|
|
17
|
+
export function rejectUnknown(actual, allowed) {
|
|
18
|
+
if (actual.join("\0") !== allowed.join("\0"))
|
|
19
|
+
throw new Error(`Unknown or injected arguments: ${actual.join(" ")}`);
|
|
20
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function handleLongTaskRevisionCommand(subcommand: string, workdir: string, args: string[]): Promise<boolean>;
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { diagnoseAuthorityRevision } from "../lib/long-task-authority-revision-diagnosis.js";
|
|
3
|
+
import { projectAuthorityRevisionDecision } from "../lib/long-task-authority-revision-summary.js";
|
|
4
|
+
import { canRetainProgressForSupportingContextRevision } from "../lib/long-task-context-authority.js";
|
|
5
|
+
import { compileDeliveryContract } from "../lib/long-task-delivery-compiler.js";
|
|
6
|
+
import { approvePendingAuthorityRevision, clearFinalReceipt, clearAuthorityRevision, commitActiveAuthority, invalidateDerivedProgress, loadActiveLongTaskAuthority, readPendingAuthorityRevision, stageCompiledDeliveryContract, } from "../lib/long-task-state.js";
|
|
7
|
+
import { repositoryRoot } from "../lib/long-task-workspace.js";
|
|
8
|
+
import { option, rejectOptions } from "./long-task-command-args.js";
|
|
9
|
+
export async function handleLongTaskRevisionCommand(subcommand, workdir, args) {
|
|
10
|
+
if (subcommand === "compile")
|
|
11
|
+
await compile(workdir, args);
|
|
12
|
+
else if (subcommand === "diagnose-revision")
|
|
13
|
+
await diagnoseRevision(workdir, args);
|
|
14
|
+
else if (subcommand === "approve-authority-revision")
|
|
15
|
+
await approveRevision(workdir, args);
|
|
16
|
+
else
|
|
17
|
+
return false;
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
async function compile(workdir, args) {
|
|
21
|
+
const revise = args.length === 1 && args[0] === "--revise";
|
|
22
|
+
if (args.length && !revise)
|
|
23
|
+
throw new Error(`Unknown or injected arguments: ${args.join(" ")}`);
|
|
24
|
+
const root = await repositoryRoot(process.cwd());
|
|
25
|
+
const loaded = await loadActiveLongTaskAuthority(root, {
|
|
26
|
+
migrate_legacy: true,
|
|
27
|
+
});
|
|
28
|
+
const previous = loaded.authority?.authority_snapshot ?? null;
|
|
29
|
+
if (loaded.authority && loaded.authority.workdir !== path.resolve(workdir))
|
|
30
|
+
throw new Error(`active_task_exists:${loaded.authority.workdir}`);
|
|
31
|
+
const revisionCapture = {
|
|
32
|
+
proposal: null,
|
|
33
|
+
};
|
|
34
|
+
const compiled = await compileForCommand(workdir, revise, previous, revisionCapture);
|
|
35
|
+
if (loaded.authority && loaded.authority.task_id !== compiled.task.id)
|
|
36
|
+
throw new Error(`active_task_exists:${loaded.authority.workdir}`);
|
|
37
|
+
const preserveProgress = previous !== null &&
|
|
38
|
+
canRetainProgressForSupportingContextRevision(previous, compiled);
|
|
39
|
+
const stagedCache = await stageCompiledDeliveryContract(compiled);
|
|
40
|
+
let authorityCommitted = false;
|
|
41
|
+
try {
|
|
42
|
+
await commitActiveAuthority({
|
|
43
|
+
candidate: compiled,
|
|
44
|
+
expected_previous_identity: loaded.authority?.active_authority_identity ?? null,
|
|
45
|
+
});
|
|
46
|
+
authorityCommitted = true;
|
|
47
|
+
try {
|
|
48
|
+
await stagedCache.publish();
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
await stagedCache.discard();
|
|
52
|
+
throw new Error(`compiled_cache_projection_publish_failed:${message(error)}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
if (!authorityCommitted)
|
|
57
|
+
await stagedCache.discard();
|
|
58
|
+
throw error;
|
|
59
|
+
}
|
|
60
|
+
if (!previous || previous.compiled_identity !== compiled.compiled_identity) {
|
|
61
|
+
if (!preserveProgress)
|
|
62
|
+
await invalidateDerivedProgress(workdir);
|
|
63
|
+
await clearFinalReceipt(compiled.repository_root, workdir);
|
|
64
|
+
}
|
|
65
|
+
await clearAuthorityRevision(workdir);
|
|
66
|
+
console.log(JSON.stringify({
|
|
67
|
+
status: "compiled",
|
|
68
|
+
task_id: compiled.task.id,
|
|
69
|
+
compiled_identity: compiled.compiled_identity,
|
|
70
|
+
authority_revision: compiled.authority_revision,
|
|
71
|
+
effective_risk: compiled.effective_risk,
|
|
72
|
+
outcomes: compiled.outcomes.map((outcome) => outcome.key),
|
|
73
|
+
claim_coverage: compiled.claim_coverage,
|
|
74
|
+
progress_preserved: preserveProgress,
|
|
75
|
+
authority_revision_change: revisionCapture.proposal
|
|
76
|
+
? projectAuthorityRevisionDecision(revisionCapture.proposal)
|
|
77
|
+
: null,
|
|
78
|
+
execution_model_checkpoint: executionModelCheckpoint(previous === null),
|
|
79
|
+
}));
|
|
80
|
+
}
|
|
81
|
+
async function compileForCommand(workdir, revise, previous, capture) {
|
|
82
|
+
try {
|
|
83
|
+
return await compileDeliveryContract(workdir, process.cwd(), {
|
|
84
|
+
revise,
|
|
85
|
+
previous_authority: previous,
|
|
86
|
+
on_authority_revision(value) {
|
|
87
|
+
capture.proposal = value;
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
if (message(error).startsWith("authority_change_requires_user_decision:"))
|
|
93
|
+
await printPendingDecision(workdir, previous);
|
|
94
|
+
throw error;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
async function printPendingDecision(workdir, previous) {
|
|
98
|
+
const pending = await readPendingAuthorityRevision(workdir);
|
|
99
|
+
if (!pending)
|
|
100
|
+
throw new Error("authority_revision_pending_state_missing");
|
|
101
|
+
console.log(JSON.stringify({
|
|
102
|
+
status: "authority_revision_pending",
|
|
103
|
+
acceptance_authorized: false,
|
|
104
|
+
active_compiled_identity: previous?.compiled_identity ?? null,
|
|
105
|
+
pending_authority_revision: projectAuthorityRevisionDecision(pending),
|
|
106
|
+
}));
|
|
107
|
+
}
|
|
108
|
+
async function diagnoseRevision(workdir, args) {
|
|
109
|
+
const outcome = option(args, "--outcome");
|
|
110
|
+
const check = option(args, "--check");
|
|
111
|
+
rejectOptions(args, ["--outcome", "--check"]);
|
|
112
|
+
const result = await diagnoseAuthorityRevision(workdir, { outcome, check });
|
|
113
|
+
console.log(JSON.stringify(result));
|
|
114
|
+
if (result.findings.length)
|
|
115
|
+
process.exitCode = 1;
|
|
116
|
+
}
|
|
117
|
+
async function approveRevision(workdir, args) {
|
|
118
|
+
const revision = option(args, "--revision");
|
|
119
|
+
rejectOptions(args, ["--revision"]);
|
|
120
|
+
if (!revision)
|
|
121
|
+
throw new Error("--revision requires a value");
|
|
122
|
+
await approvePendingAuthorityRevision(workdir, revision);
|
|
123
|
+
console.log(JSON.stringify({ status: "authority_revision_approved", revision }));
|
|
124
|
+
}
|
|
125
|
+
function executionModelCheckpoint(firstAuthorityLock) {
|
|
126
|
+
if (!firstAuthorityLock)
|
|
127
|
+
return { required: false };
|
|
128
|
+
return {
|
|
129
|
+
required: true,
|
|
130
|
+
phase: "post_authority_lock_pre_implementation",
|
|
131
|
+
options: ["continue_current_model", "switch_model_then_resume"],
|
|
132
|
+
message: "Authority Lock created. Pause before implementation and ask the user whether to continue with the current model or switch models, then resume this active Long-Task.",
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
function message(error) {
|
|
136
|
+
return error instanceof Error ? error.message : String(error);
|
|
137
|
+
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
import { canRetainProgressForSupportingContextRevision } from "../lib/long-task-context-authority.js";
|
|
3
|
-
import { compileDeliveryContract } from "../lib/long-task-delivery-compiler.js";
|
|
4
2
|
import { runDeliveryFinalGate } from "../lib/long-task-final-v2.js";
|
|
5
3
|
import { closeDeliveryTask, doctorDeliveryTask, readDeliveryStatus, resumeDeliveryTask, stopCheckDeliveryTask, } from "../lib/long-task-status-v2.js";
|
|
6
|
-
import { abandonLongTaskState,
|
|
4
|
+
import { abandonLongTaskState, forceClearCorruptActiveState, } from "../lib/long-task-state.js";
|
|
7
5
|
import { verifyDeliveryContract } from "../lib/long-task-verifier-v2.js";
|
|
8
6
|
import { repositoryRoot } from "../lib/long-task-workspace.js";
|
|
9
7
|
import { initializeLongTask, preflightLongTask, } from "./long-task-authoring.js";
|
|
8
|
+
import { option, rejectOptions, rejectUnknown, } from "./long-task-command-args.js";
|
|
10
9
|
import { explainLongTask } from "./long-task-explain.js";
|
|
10
|
+
import { handleLongTaskRevisionCommand } from "./long-task-revision.js";
|
|
11
11
|
export async function longTask(args) {
|
|
12
12
|
const subcommand = args[0] ?? "help";
|
|
13
13
|
if (subcommand === "help")
|
|
@@ -26,10 +26,8 @@ export async function longTask(args) {
|
|
|
26
26
|
rejectUnknown(args.slice(2), []);
|
|
27
27
|
return preflightLongTask(workdir);
|
|
28
28
|
}
|
|
29
|
-
if (subcommand
|
|
30
|
-
return
|
|
31
|
-
if (subcommand === "approve-authority-revision")
|
|
32
|
-
return approveRevision(workdir, args.slice(2));
|
|
29
|
+
if (await handleLongTaskRevisionCommand(subcommand, workdir, args.slice(2)))
|
|
30
|
+
return;
|
|
33
31
|
if (subcommand === "explain") {
|
|
34
32
|
rejectUnknown(args.slice(2), []);
|
|
35
33
|
return explainLongTask(workdir);
|
|
@@ -91,85 +89,6 @@ export async function longTask(args) {
|
|
|
91
89
|
}
|
|
92
90
|
throw new Error(`Unknown long-task subcommand: ${subcommand}`);
|
|
93
91
|
}
|
|
94
|
-
async function compile(workdir, args) {
|
|
95
|
-
const revise = args.length === 1 && args[0] === "--revise";
|
|
96
|
-
if (args.length && !revise)
|
|
97
|
-
throw new Error(`Unknown or injected arguments: ${args.join(" ")}`);
|
|
98
|
-
const root = await repositoryRoot(process.cwd());
|
|
99
|
-
const loaded = await loadActiveLongTaskAuthority(root, {
|
|
100
|
-
migrate_legacy: true,
|
|
101
|
-
});
|
|
102
|
-
const previous = loaded.authority?.authority_snapshot ?? null;
|
|
103
|
-
if (loaded.authority && loaded.authority.workdir !== path.resolve(workdir))
|
|
104
|
-
throw new Error(`active_task_exists:${loaded.authority.workdir}`);
|
|
105
|
-
const compiled = await compileDeliveryContract(workdir, process.cwd(), {
|
|
106
|
-
revise,
|
|
107
|
-
previous_authority: previous,
|
|
108
|
-
});
|
|
109
|
-
if (loaded.authority && loaded.authority.task_id !== compiled.task.id)
|
|
110
|
-
throw new Error(`active_task_exists:${loaded.authority.workdir}`);
|
|
111
|
-
const preserveProgress = previous !== null &&
|
|
112
|
-
canRetainProgressForSupportingContextRevision(previous, compiled);
|
|
113
|
-
const stagedCache = await stageCompiledDeliveryContract(compiled);
|
|
114
|
-
let authorityCommitted = false;
|
|
115
|
-
try {
|
|
116
|
-
await commitActiveAuthority({
|
|
117
|
-
candidate: compiled,
|
|
118
|
-
expected_previous_identity: loaded.authority?.active_authority_identity ?? null,
|
|
119
|
-
});
|
|
120
|
-
authorityCommitted = true;
|
|
121
|
-
try {
|
|
122
|
-
await stagedCache.publish();
|
|
123
|
-
}
|
|
124
|
-
catch (error) {
|
|
125
|
-
await stagedCache.discard();
|
|
126
|
-
throw new Error(`compiled_cache_projection_publish_failed:${message(error)}`);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
catch (error) {
|
|
130
|
-
if (!authorityCommitted)
|
|
131
|
-
await stagedCache.discard();
|
|
132
|
-
throw error;
|
|
133
|
-
}
|
|
134
|
-
if (!previous || previous.compiled_identity !== compiled.compiled_identity) {
|
|
135
|
-
if (!preserveProgress)
|
|
136
|
-
await invalidateDerivedProgress(workdir);
|
|
137
|
-
await clearFinalReceipt(compiled.repository_root, workdir);
|
|
138
|
-
}
|
|
139
|
-
await clearAuthorityRevision(workdir);
|
|
140
|
-
console.log(JSON.stringify({
|
|
141
|
-
status: "compiled",
|
|
142
|
-
task_id: compiled.task.id,
|
|
143
|
-
compiled_identity: compiled.compiled_identity,
|
|
144
|
-
authority_revision: compiled.authority_revision,
|
|
145
|
-
effective_risk: compiled.effective_risk,
|
|
146
|
-
outcomes: compiled.outcomes.map((outcome) => outcome.key),
|
|
147
|
-
claim_coverage: compiled.claim_coverage,
|
|
148
|
-
progress_preserved: preserveProgress,
|
|
149
|
-
execution_model_checkpoint: executionModelCheckpoint(previous === null),
|
|
150
|
-
}));
|
|
151
|
-
}
|
|
152
|
-
function executionModelCheckpoint(firstAuthorityLock) {
|
|
153
|
-
if (!firstAuthorityLock)
|
|
154
|
-
return { required: false };
|
|
155
|
-
return {
|
|
156
|
-
required: true,
|
|
157
|
-
phase: "post_authority_lock_pre_implementation",
|
|
158
|
-
options: ["continue_current_model", "switch_model_then_resume"],
|
|
159
|
-
message: "Authority Lock created. Pause before implementation and ask the user whether to continue with the current model or switch models, then resume this active Long-Task.",
|
|
160
|
-
};
|
|
161
|
-
}
|
|
162
|
-
function message(error) {
|
|
163
|
-
return error instanceof Error ? error.message : String(error);
|
|
164
|
-
}
|
|
165
|
-
async function approveRevision(workdir, args) {
|
|
166
|
-
const revision = option(args, "--revision");
|
|
167
|
-
rejectOptions(args, ["--revision"]);
|
|
168
|
-
if (!revision)
|
|
169
|
-
throw new Error("--revision requires a value");
|
|
170
|
-
await approvePendingAuthorityRevision(workdir, revision);
|
|
171
|
-
console.log(JSON.stringify({ status: "authority_revision_approved", revision }));
|
|
172
|
-
}
|
|
173
92
|
async function verify(workdir, args) {
|
|
174
93
|
const outcome = option(args, "--outcome");
|
|
175
94
|
const check = option(args, "--check");
|
|
@@ -187,40 +106,21 @@ async function finalGate(workdir, args) {
|
|
|
187
106
|
result.workflow_status !== "machine_accepted_external_pending")
|
|
188
107
|
process.exitCode = 1;
|
|
189
108
|
}
|
|
190
|
-
function option(args, name) {
|
|
191
|
-
const indexes = args.flatMap((value, index) => value === name ? [index] : []);
|
|
192
|
-
if (indexes.length > 1)
|
|
193
|
-
throw new Error(`duplicate option: ${name}`);
|
|
194
|
-
if (!indexes.length)
|
|
195
|
-
return undefined;
|
|
196
|
-
const value = args[indexes[0] + 1];
|
|
197
|
-
if (!value || value.startsWith("--"))
|
|
198
|
-
throw new Error(`${name} requires a value`);
|
|
199
|
-
return value;
|
|
200
|
-
}
|
|
201
|
-
function rejectOptions(args, allowed) {
|
|
202
|
-
for (let index = 0; index < args.length; index += 2)
|
|
203
|
-
if (!allowed.includes(args[index]) || !args[index + 1])
|
|
204
|
-
throw new Error(`Unknown or injected arguments: ${args.join(" ")}`);
|
|
205
|
-
}
|
|
206
|
-
function rejectUnknown(actual, allowed) {
|
|
207
|
-
if (actual.join("\0") !== allowed.join("\0"))
|
|
208
|
-
throw new Error(`Unknown or injected arguments: ${actual.join(" ")}`);
|
|
209
|
-
}
|
|
210
109
|
function help() {
|
|
211
|
-
console.log(`ty-context long-task commands:
|
|
212
|
-
init <workdir>
|
|
213
|
-
preflight <workdir>
|
|
214
|
-
compile <workdir>
|
|
215
|
-
compile <workdir> --revise
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
110
|
+
console.log(`ty-context long-task commands:
|
|
111
|
+
init <workdir>
|
|
112
|
+
preflight <workdir>
|
|
113
|
+
compile <workdir>
|
|
114
|
+
compile <workdir> --revise
|
|
115
|
+
diagnose-revision <workdir> [--outcome <key>] [--check <key>]
|
|
116
|
+
approve-authority-revision <workdir> --revision <sha>
|
|
117
|
+
explain <workdir>
|
|
118
|
+
verify <workdir> [--outcome <key>] [--check <key>]
|
|
119
|
+
status <workdir>
|
|
120
|
+
resume <workdir>
|
|
121
|
+
doctor <workdir>
|
|
122
|
+
final-gate <workdir>
|
|
123
|
+
stop-check <workdir> [--message <text>]
|
|
124
|
+
close <workdir>
|
|
225
125
|
abandon <workdir> [--force-corrupt-state]`);
|
|
226
126
|
}
|
|
@@ -2,6 +2,8 @@ import type { CompiledCheckV2, CompiledDeliveryContractV2, CompiledOutcomeV2, De
|
|
|
2
2
|
export declare function checkIndex(globalChecks: CompiledCheckV2[], outcomes: CompiledOutcomeV2[]): Map<string, CompiledCheckV2>;
|
|
3
3
|
export declare function changedRunnerFields(identity: string, before: CompiledCheckV2, after: CompiledCheckV2): string[];
|
|
4
4
|
export declare function removedOrReplacedVerificationInputs(identity: string, before: CompiledCheckV2, after: CompiledCheckV2): string[];
|
|
5
|
+
export declare function addedVerificationInputs(identity: string, before: CompiledCheckV2, after: CompiledCheckV2): string[];
|
|
6
|
+
export declare function addedInputPaths(identity: string, before: CompiledCheckV2, after: CompiledCheckV2): string[];
|
|
5
7
|
export declare function removedOrNarrowedInputPaths(identity: string, before: CompiledCheckV2, after: CompiledCheckV2): string[];
|
|
6
8
|
export declare function removedOrWeakenedExpectedOutputPaths(identity: string, before: CompiledCheckV2, after: CompiledCheckV2): string[];
|
|
7
9
|
export declare function sourceClaimReductions(beforeClaims: SourceClaimV2[], afterClaims: SourceClaimV2[]): string[];
|
|
@@ -25,6 +25,16 @@ export function removedOrReplacedVerificationInputs(identity, before, after) {
|
|
|
25
25
|
.filter(([file, hash]) => after.verification_input_hashes[file] !== hash)
|
|
26
26
|
.map(([file]) => `${identity}:${file}`);
|
|
27
27
|
}
|
|
28
|
+
export function addedVerificationInputs(identity, before, after) {
|
|
29
|
+
return Object.keys(after.verification_input_hashes)
|
|
30
|
+
.filter((file) => before.verification_input_hashes[file] === undefined)
|
|
31
|
+
.map((file) => `${identity}:${file}`);
|
|
32
|
+
}
|
|
33
|
+
export function addedInputPaths(identity, before, after) {
|
|
34
|
+
return after.input_paths
|
|
35
|
+
.filter((pattern) => !before.input_paths.includes(pattern))
|
|
36
|
+
.map((pattern) => `${identity}:${pattern}`);
|
|
37
|
+
}
|
|
28
38
|
export function removedOrNarrowedInputPaths(identity, before, after) {
|
|
29
39
|
return before.input_paths
|
|
30
40
|
.filter((oldPattern) => !after.input_paths.some((newPattern) => proveRepositoryPatternSubset(oldPattern, newPattern).status ===
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { CheckExecutionResultV2, LongTaskFindingV2 } from "./long-task-delivery-types.js";
|
|
2
|
+
import type { AuthorityRevisionDecisionV2 } from "./long-task-authority-revision-types.js";
|
|
3
|
+
export interface AuthorityRevisionDiagnosisV2 {
|
|
4
|
+
schema_version: "long-task-authority-revision-diagnosis-v2";
|
|
5
|
+
status: "no_authority_change" | "monotonic_revision_available" | "scope_candidate_exercised" | "scope_candidate_previewed" | "protected_change_previewed" | "candidate_stale";
|
|
6
|
+
active_compiled_identity: string;
|
|
7
|
+
candidate_compiled_identity: string;
|
|
8
|
+
revision: AuthorityRevisionDecisionV2 | null;
|
|
9
|
+
acceptance_authorized: false;
|
|
10
|
+
progress_written: false;
|
|
11
|
+
pending_revision_written: false;
|
|
12
|
+
diagnostics_executed: boolean;
|
|
13
|
+
selected_outcome: string | null;
|
|
14
|
+
selected_check: string | null;
|
|
15
|
+
skipped_candidate_checks: string[];
|
|
16
|
+
snapshot_sha256: string | null;
|
|
17
|
+
check_results: CheckExecutionResultV2[];
|
|
18
|
+
findings: LongTaskFindingV2[];
|
|
19
|
+
completed_at: string;
|
|
20
|
+
}
|
|
21
|
+
export declare function diagnoseAuthorityRevision(workdirInput: string, selection?: {
|
|
22
|
+
outcome?: string;
|
|
23
|
+
check?: string;
|
|
24
|
+
}): Promise<AuthorityRevisionDiagnosisV2>;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { compileDeliveryContract } from "./long-task-delivery-compiler.js";
|
|
3
|
+
import { projectAuthorityRevisionDecision } from "./long-task-authority-revision-summary.js";
|
|
4
|
+
import { activeAuthorityIdentityMatches, loadActiveLongTaskAuthority, } from "./long-task-state.js";
|
|
5
|
+
import { allCompiledChecks, runDeliveryChecks, selectChecks, } from "./long-task-verifier-v2.js";
|
|
6
|
+
import { createWorkspaceSnapshot, repositoryRoot, } from "./long-task-workspace.js";
|
|
7
|
+
export async function diagnoseAuthorityRevision(workdirInput, selection = {}) {
|
|
8
|
+
const repository = await repositoryRoot(process.cwd());
|
|
9
|
+
const loaded = await loadActiveLongTaskAuthority(repository, {
|
|
10
|
+
migrate_legacy: true,
|
|
11
|
+
});
|
|
12
|
+
const active = loaded.authority;
|
|
13
|
+
if (!active)
|
|
14
|
+
throw new Error("active_task_missing");
|
|
15
|
+
const workdir = path.resolve(workdirInput);
|
|
16
|
+
if (active.workdir !== workdir)
|
|
17
|
+
throw new Error("active_task_workdir_mismatch");
|
|
18
|
+
const revisionCapture = {
|
|
19
|
+
proposal: null,
|
|
20
|
+
};
|
|
21
|
+
const candidate = await compileDeliveryContract(workdir, repository, {
|
|
22
|
+
revise: true,
|
|
23
|
+
previous_authority: active.authority_snapshot,
|
|
24
|
+
authority_revision_mode: "diagnose",
|
|
25
|
+
on_authority_revision(value) {
|
|
26
|
+
revisionCapture.proposal = value;
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
if (candidate.task.id !== active.task_id)
|
|
30
|
+
throw new Error("active_task_id_mismatch");
|
|
31
|
+
if (!(await activeIdentityStillMatches(repository, active)))
|
|
32
|
+
throw new Error("active_authority_changed_during_revision_diagnosis");
|
|
33
|
+
const proposal = revisionCapture.proposal;
|
|
34
|
+
if (!proposal)
|
|
35
|
+
return result({
|
|
36
|
+
status: "no_authority_change",
|
|
37
|
+
activeIdentity: active.active_authority_identity,
|
|
38
|
+
candidateIdentity: candidate.compiled_identity,
|
|
39
|
+
revision: null,
|
|
40
|
+
selection,
|
|
41
|
+
});
|
|
42
|
+
const decision = projectAuthorityRevisionDecision(proposal);
|
|
43
|
+
if (proposal.change_class === "monotonic_evidence_strengthening")
|
|
44
|
+
return result({
|
|
45
|
+
status: "monotonic_revision_available",
|
|
46
|
+
activeIdentity: active.active_authority_identity,
|
|
47
|
+
candidateIdentity: candidate.compiled_identity,
|
|
48
|
+
revision: decision,
|
|
49
|
+
selection,
|
|
50
|
+
});
|
|
51
|
+
if (proposal.change_class !== "scope_only_expansion")
|
|
52
|
+
return result({
|
|
53
|
+
status: "protected_change_previewed",
|
|
54
|
+
activeIdentity: active.active_authority_identity,
|
|
55
|
+
candidateIdentity: candidate.compiled_identity,
|
|
56
|
+
revision: decision,
|
|
57
|
+
selection,
|
|
58
|
+
});
|
|
59
|
+
const selected = selectChecks(candidate, selection);
|
|
60
|
+
const activeCheckIds = new Set(allCompiledChecks(active.authority_snapshot).map((check) => check.internal_id));
|
|
61
|
+
const runnable = selected.filter((check) => activeCheckIds.has(check.internal_id));
|
|
62
|
+
const skipped = selected
|
|
63
|
+
.filter((check) => !activeCheckIds.has(check.internal_id))
|
|
64
|
+
.map((check) => check.internal_id)
|
|
65
|
+
.sort();
|
|
66
|
+
if (!runnable.length)
|
|
67
|
+
return result({
|
|
68
|
+
status: "scope_candidate_previewed",
|
|
69
|
+
activeIdentity: active.active_authority_identity,
|
|
70
|
+
candidateIdentity: candidate.compiled_identity,
|
|
71
|
+
revision: decision,
|
|
72
|
+
selection,
|
|
73
|
+
skipped,
|
|
74
|
+
});
|
|
75
|
+
const snapshot = await createWorkspaceSnapshot(candidate.repository_root, candidate.workdir, `revision-diagnosis-${candidate.task.id}`);
|
|
76
|
+
try {
|
|
77
|
+
const run = await runDeliveryChecks(candidate, snapshot, runnable, true);
|
|
78
|
+
const unchanged = await activeIdentityStillMatches(repository, active);
|
|
79
|
+
if (!unchanged)
|
|
80
|
+
run.findings.push(activeAuthorityChangedFinding());
|
|
81
|
+
return result({
|
|
82
|
+
status: unchanged ? "scope_candidate_exercised" : "candidate_stale",
|
|
83
|
+
activeIdentity: active.active_authority_identity,
|
|
84
|
+
candidateIdentity: candidate.compiled_identity,
|
|
85
|
+
revision: decision,
|
|
86
|
+
selection,
|
|
87
|
+
skipped,
|
|
88
|
+
diagnosticsExecuted: true,
|
|
89
|
+
snapshotSha256: snapshot.manifest.snapshot_sha256,
|
|
90
|
+
checkResults: run.check_results,
|
|
91
|
+
findings: run.findings,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
finally {
|
|
95
|
+
await snapshot.dispose();
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
async function activeIdentityStillMatches(repository, active) {
|
|
99
|
+
try {
|
|
100
|
+
const current = (await loadActiveLongTaskAuthority(repository)).authority;
|
|
101
|
+
return (current !== null &&
|
|
102
|
+
activeAuthorityIdentityMatches(current, {
|
|
103
|
+
task_id: active.task_id,
|
|
104
|
+
authority_revision: active.authority_revision,
|
|
105
|
+
compiled_identity: active.active_authority_identity,
|
|
106
|
+
worktree_identity: active.worktree_identity,
|
|
107
|
+
}));
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
function activeAuthorityChangedFinding() {
|
|
114
|
+
return {
|
|
115
|
+
code: "active_authority_changed_during_revision_diagnosis",
|
|
116
|
+
outcome_key: null,
|
|
117
|
+
check_key: null,
|
|
118
|
+
message: "Active Authority changed while candidate diagnosis was running.",
|
|
119
|
+
next_action: "Discard these transient results and diagnose the current Contract against the new Active Authority.",
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
function result(input) {
|
|
123
|
+
return {
|
|
124
|
+
schema_version: "long-task-authority-revision-diagnosis-v2",
|
|
125
|
+
status: input.status,
|
|
126
|
+
active_compiled_identity: input.activeIdentity,
|
|
127
|
+
candidate_compiled_identity: input.candidateIdentity,
|
|
128
|
+
revision: input.revision,
|
|
129
|
+
acceptance_authorized: false,
|
|
130
|
+
progress_written: false,
|
|
131
|
+
pending_revision_written: false,
|
|
132
|
+
diagnostics_executed: input.diagnosticsExecuted ?? false,
|
|
133
|
+
selected_outcome: input.selection.outcome ?? null,
|
|
134
|
+
selected_check: input.selection.check ?? null,
|
|
135
|
+
skipped_candidate_checks: input.skipped ?? [],
|
|
136
|
+
snapshot_sha256: input.snapshotSha256 ?? null,
|
|
137
|
+
check_results: input.checkResults ?? [],
|
|
138
|
+
findings: input.findings ?? [],
|
|
139
|
+
completed_at: new Date().toISOString(),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { AuthorityRevisionProposalV2 } from "./long-task-authority-revision-types.js";
|
|
1
2
|
import type { AuthorityHashesV2, CompiledDeliveryContractV2, CompiledOutcomeV2, DeliveryContractV2, NextAuthorityMaterialsV2, VerifierIdentityV2 } from "./long-task-delivery-types.js";
|
|
2
3
|
export declare function assertRiskNotDowngraded(previous: CompiledDeliveryContractV2, nextLevel: "standard" | "strict", nextReasons: string[]): void;
|
|
3
|
-
export declare function
|
|
4
|
+
export declare function buildAuthorityRevisionProposal(previous: CompiledDeliveryContractV2, nextContract: DeliveryContractV2, nextHashes: AuthorityHashesV2, nextMaterials: NextAuthorityMaterialsV2, nextGlobalChecks: CompiledDeliveryContractV2["global"]["acceptance"]["checks"], nextOutcomes: CompiledOutcomeV2[], nextVerifier: VerifierIdentityV2, riskFloor: "standard" | "strict"): AuthorityRevisionProposalV2;
|
|
5
|
+
export declare function enforceAuthorityRevision(proposal: AuthorityRevisionProposalV2, workdir: string): Promise<void>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { changedAuthoritySections } from "./long-task-authority.js";
|
|
2
2
|
import { authorityMaterialHashes, compiledAuthorityMaterials, } from "./long-task-authority-materials.js";
|
|
3
|
-
import { authorityRevisionDiff
|
|
3
|
+
import { authorityRevisionDiff } from "./long-task-authority-revision.js";
|
|
4
|
+
import { classifyAuthorityRevision, summarizeAuthorityRevision, } from "./long-task-authority-revision-summary.js";
|
|
4
5
|
import { authorityRevisionApproved, writePendingAuthorityRevision, } from "./long-task-state.js";
|
|
5
6
|
import { canonicalValueJson, sha256Hex } from "./strict-codec.js";
|
|
6
7
|
export function assertRiskNotDowngraded(previous, nextLevel, nextReasons) {
|
|
@@ -11,11 +12,11 @@ export function assertRiskNotDowngraded(previous, nextLevel, nextReasons) {
|
|
|
11
12
|
if (removed.length)
|
|
12
13
|
throw new Error(`authority_risk_downgrade_rejected:${removed.join(",")}`);
|
|
13
14
|
}
|
|
14
|
-
export
|
|
15
|
+
export function buildAuthorityRevisionProposal(previous, nextContract, nextHashes, nextMaterials, nextGlobalChecks, nextOutcomes, nextVerifier, riskFloor) {
|
|
15
16
|
const previousMaterials = compiledAuthorityMaterials(previous);
|
|
16
17
|
const diff = authorityRevisionDiff(previous, nextContract, nextHashes, nextMaterials, nextGlobalChecks, nextOutcomes, nextVerifier);
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
const changeClass = classifyAuthorityRevision(diff);
|
|
19
|
+
const approvalSummary = summarizeAuthorityRevision(diff, nextContract.outcomes.map((outcome) => outcome.key));
|
|
19
20
|
const unsignedRevision = {
|
|
20
21
|
previous_hashes: previous.authority_hashes,
|
|
21
22
|
next_hashes: nextHashes,
|
|
@@ -26,16 +27,23 @@ export async function enforceAuthorityRevision(previous, nextContract, nextHashe
|
|
|
26
27
|
changed_authority_sections: changedAuthoritySections(previous.authority_hashes, nextHashes),
|
|
27
28
|
revision_diff: diff,
|
|
28
29
|
new_risk_floor: riskFloor,
|
|
29
|
-
affected_outcomes_or_contracts:
|
|
30
|
+
affected_outcomes_or_contracts: approvalSummary.affected_outcomes,
|
|
31
|
+
change_class: changeClass,
|
|
32
|
+
approval_required: diff.reduction_reasons.length > 0,
|
|
33
|
+
approval_summary: approvalSummary,
|
|
30
34
|
};
|
|
31
35
|
const revisionIdentity = sha256Hex(canonicalValueJson(unsignedRevision));
|
|
32
|
-
|
|
36
|
+
return { ...unsignedRevision, revision_identity: revisionIdentity };
|
|
37
|
+
}
|
|
38
|
+
export async function enforceAuthorityRevision(proposal, workdir) {
|
|
39
|
+
if (!proposal.approval_required)
|
|
40
|
+
return;
|
|
41
|
+
if (!(await authorityRevisionApproved(workdir, proposal.revision_identity))) {
|
|
33
42
|
await writePendingAuthorityRevision(workdir, {
|
|
34
43
|
schema_version: "long-task-authority-revision-pending-v2",
|
|
35
|
-
...
|
|
36
|
-
revision_identity: revisionIdentity,
|
|
44
|
+
...proposal,
|
|
37
45
|
created_at: new Date().toISOString(),
|
|
38
46
|
});
|
|
39
|
-
throw new Error(`authority_change_requires_user_decision:${
|
|
47
|
+
throw new Error(`authority_change_requires_user_decision:${proposal.revision_identity}`);
|
|
40
48
|
}
|
|
41
49
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { AuthorityRevisionApprovalSummaryV2, AuthorityRevisionChangeClassV2, AuthorityRevisionDecisionV2, AuthorityRevisionDiffV2 } from "./long-task-authority-revision-types.js";
|
|
2
|
+
export declare function classifyAuthorityRevision(diff: AuthorityRevisionDiffV2): AuthorityRevisionChangeClassV2;
|
|
3
|
+
export declare function summarizeAuthorityRevision(diff: AuthorityRevisionDiffV2, outcomeKeys: string[]): AuthorityRevisionApprovalSummaryV2;
|
|
4
|
+
export declare function projectAuthorityRevisionDecision(value: {
|
|
5
|
+
revision_identity: string;
|
|
6
|
+
revision_diff: AuthorityRevisionDiffV2;
|
|
7
|
+
affected_outcomes_or_contracts: string[];
|
|
8
|
+
change_class?: AuthorityRevisionChangeClassV2;
|
|
9
|
+
approval_required?: boolean;
|
|
10
|
+
approval_summary?: AuthorityRevisionApprovalSummaryV2;
|
|
11
|
+
}): AuthorityRevisionDecisionV2;
|