@christang/keel 5.1.1 → 5.2.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/LICENSE +21 -21
- package/README.md +114 -158
- package/README.zh-CN.md +118 -197
- package/assets/bootstrap/AGENTS.md +1 -1
- package/{plugins/keel/skills/keel-align-expectations/references → assets/lenses}/hardware-dsl.md +4 -2
- package/{plugins/keel/skills/keel-align-expectations/references → assets/lenses}/hardware.md +4 -2
- package/{plugins/keel/skills/keel-align-expectations/references → assets/lenses}/web.md +4 -2
- package/assets/openspec/schemas/keel-spec-driven/schema.yaml +172 -166
- package/assets/openspec/schemas/keel-spec-driven/templates/tasks.md +13 -4
- package/bin/keel.js +218 -15
- package/package.json +1 -1
- package/plugins/keel/.claude-plugin/plugin.json +1 -1
- package/plugins/keel/.codex-plugin/plugin.json +1 -1
- package/plugins/keel/hooks/hooks.json +30 -30
- package/plugins/keel/scripts/pretooluse-guard.js +156 -156
- package/plugins/keel/scripts/session-start.js +182 -182
- package/plugins/keel/skills/keel-align-expectations/SKILL.md +2 -6
- package/plugins/keel/skills/keel-debug-failure/SKILL.md +2 -2
- package/plugins/keel/skills/keel-review-checklist/SKILL.md +2 -2
- package/plugins/keel/skills/keel-tdd-or-test-first/SKILL.md +2 -2
- package/scripts/bump_version.js +140 -0
- package/scripts/install_to_repo.py +0 -70
- package/scripts/run_python.js +63 -63
- package/scripts/validate_plugin.py +408 -96
- package/src/core/capabilities.js +291 -291
- package/src/core/context.js +521 -514
- package/src/core/gates.js +664 -643
- package/src/core/goal.js +230 -230
- package/src/core/guard.js +295 -295
- package/src/core/helper.js +319 -319
- package/src/core/projection.js +195 -195
- package/src/core/task-contract.js +757 -736
- package/src/core/tasksview.js +123 -123
package/src/core/goal.js
CHANGED
|
@@ -1,230 +1,230 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
// Keel 4.1.0 single-task native goal projection contract.
|
|
4
|
-
//
|
|
5
|
-
// Compiles one passing `keel-task-capsule/v1` into an ephemeral
|
|
6
|
-
// `keel-native-goal/v1` projection for a supported native runtime. The
|
|
7
|
-
// projection is never written as Keel-owned state; OpenSpec, Git, the capsule
|
|
8
|
-
// fingerprint, and deterministic gates remain durable authority.
|
|
9
|
-
|
|
10
|
-
const { resolveContext } = require("./context");
|
|
11
|
-
const { loadTaskContract } = require("./task-contract");
|
|
12
|
-
const { probeCapabilities } = require("./capabilities");
|
|
13
|
-
|
|
14
|
-
const GOAL_VERSION = "keel-native-goal/v1";
|
|
15
|
-
const CLAUDE_CONDITION_LIMIT = 4000;
|
|
16
|
-
const SUPPORTED_GOAL_TARGETS = new Set(["codex", "claude"]);
|
|
17
|
-
const TERMINAL_STATES = ["complete", "blocked", "paused"];
|
|
18
|
-
const EVIDENCE_PRESENTATION = [
|
|
19
|
-
"Surface every Command result and gate outcome in the transcript before any success claim.",
|
|
20
|
-
"Native evaluator success alone never marks or reports the OpenSpec task complete.",
|
|
21
|
-
"Achievement requires matching change/task/fingerprint, Acceptance demonstrated by the named Commands, passing Review, passing task-complete, and the durably checked task checkbox.",
|
|
22
|
-
];
|
|
23
|
-
|
|
24
|
-
function blockedGoal(target, reason, warnings = [], extra = {}) {
|
|
25
|
-
return {
|
|
26
|
-
version: GOAL_VERSION,
|
|
27
|
-
status: "blocked",
|
|
28
|
-
target,
|
|
29
|
-
source: null,
|
|
30
|
-
capability: null,
|
|
31
|
-
goal: null,
|
|
32
|
-
reasons: [reason],
|
|
33
|
-
warnings,
|
|
34
|
-
...extra,
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function renderCondition(goal) {
|
|
39
|
-
const lines = [
|
|
40
|
-
`Goal: complete exactly one OpenSpec task — ${goal.owner} (fingerprint ${goal.fingerprint.value}).`,
|
|
41
|
-
`Objective: ${goal.objective}`,
|
|
42
|
-
"Acceptance (all must be demonstrated by the named Commands):",
|
|
43
|
-
...goal.acceptance.map((item) => `- ${item}`),
|
|
44
|
-
"Commands:",
|
|
45
|
-
...goal.commands.map((item) => `- ${item}`),
|
|
46
|
-
`Verification strategy: ${goal.verificationStrategy}.`,
|
|
47
|
-
`Write boundary (Touch): ${goal.touch.join(", ")}.`,
|
|
48
|
-
"Stop/Autonomy boundary:",
|
|
49
|
-
...goal.stopBoundary.map((item) => `- ${item}`),
|
|
50
|
-
"Ownership: the current agent is the sole writer and owns Review, gate invocation, the task checkbox, and completion.",
|
|
51
|
-
"Done only when: task-complete passes and the current agent has durably checked the task; then stop and require a new explicit authorization before any next task.",
|
|
52
|
-
];
|
|
53
|
-
return lines.join("\n");
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function compileGoalProjection(repo, options) {
|
|
57
|
-
const target = options.target;
|
|
58
|
-
if (!SUPPORTED_GOAL_TARGETS.has(target)) {
|
|
59
|
-
return blockedGoal(
|
|
60
|
-
target,
|
|
61
|
-
`Native single-task goal execution supports codex and claude only; `
|
|
62
|
-
+ `${target || "<missing>"} remains manual/compatibility-only.`
|
|
63
|
-
);
|
|
64
|
-
}
|
|
65
|
-
if (!options.change || !options.task) {
|
|
66
|
-
return blockedGoal(
|
|
67
|
-
target,
|
|
68
|
-
"Native goal activation requires one explicit --change and --task "
|
|
69
|
-
+ "selection; ambiguous, multiple, or task-group activation is rejected."
|
|
70
|
-
);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const context = resolveContext(repo, {
|
|
74
|
-
change: options.change,
|
|
75
|
-
task: options.task,
|
|
76
|
-
});
|
|
77
|
-
if (context.status !== "ready" || !context.selection) {
|
|
78
|
-
return blockedGoal(
|
|
79
|
-
target,
|
|
80
|
-
context.reasons.join(" ") || "Current OpenSpec context is not ready.",
|
|
81
|
-
context.warnings
|
|
82
|
-
);
|
|
83
|
-
}
|
|
84
|
-
const change = context.selection.change;
|
|
85
|
-
const taskId = context.selection.task;
|
|
86
|
-
if (!taskId) {
|
|
87
|
-
return blockedGoal(
|
|
88
|
-
target,
|
|
89
|
-
"Native goal activation requires one selected executable task, not a "
|
|
90
|
-
+ "change backlog or contiguous task group.",
|
|
91
|
-
context.warnings
|
|
92
|
-
);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const loaded = loadTaskContract(repo, change, taskId);
|
|
96
|
-
if (!loaded) {
|
|
97
|
-
return blockedGoal(
|
|
98
|
-
target,
|
|
99
|
-
`Selected durable task owner openspec/changes/${change}/tasks.md#${taskId} `
|
|
100
|
-
+ "is missing.",
|
|
101
|
-
context.warnings
|
|
102
|
-
);
|
|
103
|
-
}
|
|
104
|
-
if (loaded.task.checked) {
|
|
105
|
-
return blockedGoal(
|
|
106
|
-
target,
|
|
107
|
-
"Selected task is already complete; a later task requires a new explicit "
|
|
108
|
-
+ "user authorization and a new start fingerprint.",
|
|
109
|
-
context.warnings
|
|
110
|
-
);
|
|
111
|
-
}
|
|
112
|
-
if (loaded.contract.diagnostics.length > 0) {
|
|
113
|
-
return blockedGoal(
|
|
114
|
-
target,
|
|
115
|
-
loaded.contract.diagnostics.map((item) => item.message).join(" "),
|
|
116
|
-
context.warnings
|
|
117
|
-
);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const contract = loaded.contract;
|
|
121
|
-
const capsule = contract.capsule;
|
|
122
|
-
const owner = `openspec/changes/${change}/tasks.md#${taskId}`;
|
|
123
|
-
|
|
124
|
-
// Continuity is reconstructed from durable OpenSpec/Git authority, never a
|
|
125
|
-
// Keel cursor or cache: a resume passes the previously recorded owner and
|
|
126
|
-
// fingerprint, and any divergence hard-stops rather than silently rebinding.
|
|
127
|
-
if (options.expectedOwner && options.expectedOwner !== owner) {
|
|
128
|
-
return blockedGoal(
|
|
129
|
-
target,
|
|
130
|
-
`Recorded authorization owns ${options.expectedOwner}, but the current `
|
|
131
|
-
+ `OpenSpec selection is ${owner}; checkout divergence requires new `
|
|
132
|
-
+ "explicit authorization, not automatic rebinding.",
|
|
133
|
-
context.warnings
|
|
134
|
-
);
|
|
135
|
-
}
|
|
136
|
-
if (
|
|
137
|
-
options.expectedFingerprint
|
|
138
|
-
&& options.expectedFingerprint !== contract.fingerprint.value
|
|
139
|
-
) {
|
|
140
|
-
return blockedGoal(
|
|
141
|
-
target,
|
|
142
|
-
`Recompiled capsule fingerprint ${contract.fingerprint.value} does not `
|
|
143
|
-
+ `match the recorded authorization ${options.expectedFingerprint}; `
|
|
144
|
-
+ "fingerprint drift requires reauthorization before any product write.",
|
|
145
|
-
context.warnings
|
|
146
|
-
);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
const capabilities = probeCapabilities(repo, target);
|
|
150
|
-
const capability = capabilities.capabilities["execution.goal"];
|
|
151
|
-
|
|
152
|
-
const goal = {
|
|
153
|
-
version: GOAL_VERSION,
|
|
154
|
-
target,
|
|
155
|
-
change,
|
|
156
|
-
task: taskId,
|
|
157
|
-
fingerprint: contract.fingerprint,
|
|
158
|
-
objective: capsule.task.title,
|
|
159
|
-
acceptance: capsule.acceptance,
|
|
160
|
-
commands: capsule.verification.commands.map(
|
|
161
|
-
(item) => `${item.label}: ${item.check}`
|
|
162
|
-
),
|
|
163
|
-
verificationStrategy: capsule.verification.strategy,
|
|
164
|
-
touch: capsule.touch,
|
|
165
|
-
stopBoundary: [...capsule.boundaries.stop, ...capsule.boundaries.autonomy],
|
|
166
|
-
owner: capsule.owner,
|
|
167
|
-
ownership: "current-agent-sole-writer",
|
|
168
|
-
helperPolicy: capsule.helperAuthority,
|
|
169
|
-
terminalStates: TERMINAL_STATES,
|
|
170
|
-
evidencePresentation: EVIDENCE_PRESENTATION,
|
|
171
|
-
authorizationEvidence: {
|
|
172
|
-
field: "Automation authorization: single-task",
|
|
173
|
-
target,
|
|
174
|
-
change,
|
|
175
|
-
task: taskId,
|
|
176
|
-
fingerprint: contract.fingerprint.value,
|
|
177
|
-
},
|
|
178
|
-
prohibitions: capsule.prohibitions,
|
|
179
|
-
};
|
|
180
|
-
goal.owner = owner;
|
|
181
|
-
const conditionText = renderCondition(goal);
|
|
182
|
-
goal.condition = conditionText;
|
|
183
|
-
goal.conditionLength = conditionText.length;
|
|
184
|
-
|
|
185
|
-
if (target === "claude" && conditionText.length > CLAUDE_CONDITION_LIMIT) {
|
|
186
|
-
return blockedGoal(
|
|
187
|
-
target,
|
|
188
|
-
`Compiled goal condition is ${conditionText.length} characters, above the `
|
|
189
|
-
+ `Claude ${CLAUDE_CONDITION_LIMIT}-character limit; refusing native `
|
|
190
|
-
+ "activation rather than omitting Acceptance, fingerprint, or stop "
|
|
191
|
-
+ "authority. Use the manual current-agent loop instead.",
|
|
192
|
-
context.warnings,
|
|
193
|
-
{ conditionLength: conditionText.length }
|
|
194
|
-
);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
return {
|
|
198
|
-
version: GOAL_VERSION,
|
|
199
|
-
status: "ready",
|
|
200
|
-
target,
|
|
201
|
-
source: { authority: "OpenSpec", owner, change, task: taskId },
|
|
202
|
-
capability,
|
|
203
|
-
goal,
|
|
204
|
-
reasons: [],
|
|
205
|
-
warnings: context.warnings,
|
|
206
|
-
};
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
function renderGoalProjection(result) {
|
|
210
|
-
const lines = [
|
|
211
|
-
`Keel native goal: ${result.status}`,
|
|
212
|
-
`Target: ${result.target}`,
|
|
213
|
-
];
|
|
214
|
-
if (result.source) lines.push(`Owner: ${result.source.owner}`);
|
|
215
|
-
if (result.goal) {
|
|
216
|
-
lines.push(`Fingerprint: ${result.goal.fingerprint.value}`);
|
|
217
|
-
lines.push(`Condition length: ${result.goal.conditionLength}`);
|
|
218
|
-
}
|
|
219
|
-
for (const reason of result.reasons || []) lines.push(`Reason: ${reason}`);
|
|
220
|
-
for (const warning of result.warnings || []) lines.push(`Warning: ${warning}`);
|
|
221
|
-
return `${lines.join("\n")}\n`;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
module.exports = {
|
|
225
|
-
GOAL_VERSION,
|
|
226
|
-
CLAUDE_CONDITION_LIMIT,
|
|
227
|
-
SUPPORTED_GOAL_TARGETS,
|
|
228
|
-
compileGoalProjection,
|
|
229
|
-
renderGoalProjection,
|
|
230
|
-
};
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// Keel 4.1.0 single-task native goal projection contract.
|
|
4
|
+
//
|
|
5
|
+
// Compiles one passing `keel-task-capsule/v1` into an ephemeral
|
|
6
|
+
// `keel-native-goal/v1` projection for a supported native runtime. The
|
|
7
|
+
// projection is never written as Keel-owned state; OpenSpec, Git, the capsule
|
|
8
|
+
// fingerprint, and deterministic gates remain durable authority.
|
|
9
|
+
|
|
10
|
+
const { resolveContext } = require("./context");
|
|
11
|
+
const { loadTaskContract } = require("./task-contract");
|
|
12
|
+
const { probeCapabilities } = require("./capabilities");
|
|
13
|
+
|
|
14
|
+
const GOAL_VERSION = "keel-native-goal/v1";
|
|
15
|
+
const CLAUDE_CONDITION_LIMIT = 4000;
|
|
16
|
+
const SUPPORTED_GOAL_TARGETS = new Set(["codex", "claude"]);
|
|
17
|
+
const TERMINAL_STATES = ["complete", "blocked", "paused"];
|
|
18
|
+
const EVIDENCE_PRESENTATION = [
|
|
19
|
+
"Surface every Command result and gate outcome in the transcript before any success claim.",
|
|
20
|
+
"Native evaluator success alone never marks or reports the OpenSpec task complete.",
|
|
21
|
+
"Achievement requires matching change/task/fingerprint, Acceptance demonstrated by the named Commands, passing Review, passing task-complete, and the durably checked task checkbox.",
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
function blockedGoal(target, reason, warnings = [], extra = {}) {
|
|
25
|
+
return {
|
|
26
|
+
version: GOAL_VERSION,
|
|
27
|
+
status: "blocked",
|
|
28
|
+
target,
|
|
29
|
+
source: null,
|
|
30
|
+
capability: null,
|
|
31
|
+
goal: null,
|
|
32
|
+
reasons: [reason],
|
|
33
|
+
warnings,
|
|
34
|
+
...extra,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function renderCondition(goal) {
|
|
39
|
+
const lines = [
|
|
40
|
+
`Goal: complete exactly one OpenSpec task — ${goal.owner} (fingerprint ${goal.fingerprint.value}).`,
|
|
41
|
+
`Objective: ${goal.objective}`,
|
|
42
|
+
"Acceptance (all must be demonstrated by the named Commands):",
|
|
43
|
+
...goal.acceptance.map((item) => `- ${item}`),
|
|
44
|
+
"Commands:",
|
|
45
|
+
...goal.commands.map((item) => `- ${item}`),
|
|
46
|
+
`Verification strategy: ${goal.verificationStrategy}.`,
|
|
47
|
+
`Write boundary (Touch): ${goal.touch.join(", ")}.`,
|
|
48
|
+
"Stop/Autonomy boundary:",
|
|
49
|
+
...goal.stopBoundary.map((item) => `- ${item}`),
|
|
50
|
+
"Ownership: the current agent is the sole writer and owns Review, gate invocation, the task checkbox, and completion.",
|
|
51
|
+
"Done only when: task-complete passes and the current agent has durably checked the task; then stop and require a new explicit authorization before any next task.",
|
|
52
|
+
];
|
|
53
|
+
return lines.join("\n");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function compileGoalProjection(repo, options) {
|
|
57
|
+
const target = options.target;
|
|
58
|
+
if (!SUPPORTED_GOAL_TARGETS.has(target)) {
|
|
59
|
+
return blockedGoal(
|
|
60
|
+
target,
|
|
61
|
+
`Native single-task goal execution supports codex and claude only; `
|
|
62
|
+
+ `${target || "<missing>"} remains manual/compatibility-only.`
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
if (!options.change || !options.task) {
|
|
66
|
+
return blockedGoal(
|
|
67
|
+
target,
|
|
68
|
+
"Native goal activation requires one explicit --change and --task "
|
|
69
|
+
+ "selection; ambiguous, multiple, or task-group activation is rejected."
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const context = resolveContext(repo, {
|
|
74
|
+
change: options.change,
|
|
75
|
+
task: options.task,
|
|
76
|
+
});
|
|
77
|
+
if (context.status !== "ready" || !context.selection) {
|
|
78
|
+
return blockedGoal(
|
|
79
|
+
target,
|
|
80
|
+
context.reasons.join(" ") || "Current OpenSpec context is not ready.",
|
|
81
|
+
context.warnings
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
const change = context.selection.change;
|
|
85
|
+
const taskId = context.selection.task;
|
|
86
|
+
if (!taskId) {
|
|
87
|
+
return blockedGoal(
|
|
88
|
+
target,
|
|
89
|
+
"Native goal activation requires one selected executable task, not a "
|
|
90
|
+
+ "change backlog or contiguous task group.",
|
|
91
|
+
context.warnings
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const loaded = loadTaskContract(repo, change, taskId);
|
|
96
|
+
if (!loaded) {
|
|
97
|
+
return blockedGoal(
|
|
98
|
+
target,
|
|
99
|
+
`Selected durable task owner openspec/changes/${change}/tasks.md#${taskId} `
|
|
100
|
+
+ "is missing.",
|
|
101
|
+
context.warnings
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
if (loaded.task.checked) {
|
|
105
|
+
return blockedGoal(
|
|
106
|
+
target,
|
|
107
|
+
"Selected task is already complete; a later task requires a new explicit "
|
|
108
|
+
+ "user authorization and a new start fingerprint.",
|
|
109
|
+
context.warnings
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
if (loaded.contract.diagnostics.length > 0) {
|
|
113
|
+
return blockedGoal(
|
|
114
|
+
target,
|
|
115
|
+
loaded.contract.diagnostics.map((item) => item.message).join(" "),
|
|
116
|
+
context.warnings
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const contract = loaded.contract;
|
|
121
|
+
const capsule = contract.capsule;
|
|
122
|
+
const owner = `openspec/changes/${change}/tasks.md#${taskId}`;
|
|
123
|
+
|
|
124
|
+
// Continuity is reconstructed from durable OpenSpec/Git authority, never a
|
|
125
|
+
// Keel cursor or cache: a resume passes the previously recorded owner and
|
|
126
|
+
// fingerprint, and any divergence hard-stops rather than silently rebinding.
|
|
127
|
+
if (options.expectedOwner && options.expectedOwner !== owner) {
|
|
128
|
+
return blockedGoal(
|
|
129
|
+
target,
|
|
130
|
+
`Recorded authorization owns ${options.expectedOwner}, but the current `
|
|
131
|
+
+ `OpenSpec selection is ${owner}; checkout divergence requires new `
|
|
132
|
+
+ "explicit authorization, not automatic rebinding.",
|
|
133
|
+
context.warnings
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
if (
|
|
137
|
+
options.expectedFingerprint
|
|
138
|
+
&& options.expectedFingerprint !== contract.fingerprint.value
|
|
139
|
+
) {
|
|
140
|
+
return blockedGoal(
|
|
141
|
+
target,
|
|
142
|
+
`Recompiled capsule fingerprint ${contract.fingerprint.value} does not `
|
|
143
|
+
+ `match the recorded authorization ${options.expectedFingerprint}; `
|
|
144
|
+
+ "fingerprint drift requires reauthorization before any product write.",
|
|
145
|
+
context.warnings
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const capabilities = probeCapabilities(repo, target);
|
|
150
|
+
const capability = capabilities.capabilities["execution.goal"];
|
|
151
|
+
|
|
152
|
+
const goal = {
|
|
153
|
+
version: GOAL_VERSION,
|
|
154
|
+
target,
|
|
155
|
+
change,
|
|
156
|
+
task: taskId,
|
|
157
|
+
fingerprint: contract.fingerprint,
|
|
158
|
+
objective: capsule.task.title,
|
|
159
|
+
acceptance: capsule.acceptance,
|
|
160
|
+
commands: capsule.verification.commands.map(
|
|
161
|
+
(item) => `${item.label}: ${item.check}`
|
|
162
|
+
),
|
|
163
|
+
verificationStrategy: capsule.verification.strategy,
|
|
164
|
+
touch: capsule.touch,
|
|
165
|
+
stopBoundary: [...capsule.boundaries.stop, ...capsule.boundaries.autonomy],
|
|
166
|
+
owner: capsule.owner,
|
|
167
|
+
ownership: "current-agent-sole-writer",
|
|
168
|
+
helperPolicy: capsule.helperAuthority,
|
|
169
|
+
terminalStates: TERMINAL_STATES,
|
|
170
|
+
evidencePresentation: EVIDENCE_PRESENTATION,
|
|
171
|
+
authorizationEvidence: {
|
|
172
|
+
field: "Automation authorization: single-task",
|
|
173
|
+
target,
|
|
174
|
+
change,
|
|
175
|
+
task: taskId,
|
|
176
|
+
fingerprint: contract.fingerprint.value,
|
|
177
|
+
},
|
|
178
|
+
prohibitions: capsule.prohibitions,
|
|
179
|
+
};
|
|
180
|
+
goal.owner = owner;
|
|
181
|
+
const conditionText = renderCondition(goal);
|
|
182
|
+
goal.condition = conditionText;
|
|
183
|
+
goal.conditionLength = conditionText.length;
|
|
184
|
+
|
|
185
|
+
if (target === "claude" && conditionText.length > CLAUDE_CONDITION_LIMIT) {
|
|
186
|
+
return blockedGoal(
|
|
187
|
+
target,
|
|
188
|
+
`Compiled goal condition is ${conditionText.length} characters, above the `
|
|
189
|
+
+ `Claude ${CLAUDE_CONDITION_LIMIT}-character limit; refusing native `
|
|
190
|
+
+ "activation rather than omitting Acceptance, fingerprint, or stop "
|
|
191
|
+
+ "authority. Use the manual current-agent loop instead.",
|
|
192
|
+
context.warnings,
|
|
193
|
+
{ conditionLength: conditionText.length }
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return {
|
|
198
|
+
version: GOAL_VERSION,
|
|
199
|
+
status: "ready",
|
|
200
|
+
target,
|
|
201
|
+
source: { authority: "OpenSpec", owner, change, task: taskId },
|
|
202
|
+
capability,
|
|
203
|
+
goal,
|
|
204
|
+
reasons: [],
|
|
205
|
+
warnings: context.warnings,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function renderGoalProjection(result) {
|
|
210
|
+
const lines = [
|
|
211
|
+
`Keel native goal: ${result.status}`,
|
|
212
|
+
`Target: ${result.target}`,
|
|
213
|
+
];
|
|
214
|
+
if (result.source) lines.push(`Owner: ${result.source.owner}`);
|
|
215
|
+
if (result.goal) {
|
|
216
|
+
lines.push(`Fingerprint: ${result.goal.fingerprint.value}`);
|
|
217
|
+
lines.push(`Condition length: ${result.goal.conditionLength}`);
|
|
218
|
+
}
|
|
219
|
+
for (const reason of result.reasons || []) lines.push(`Reason: ${reason}`);
|
|
220
|
+
for (const warning of result.warnings || []) lines.push(`Warning: ${warning}`);
|
|
221
|
+
return `${lines.join("\n")}\n`;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
module.exports = {
|
|
225
|
+
GOAL_VERSION,
|
|
226
|
+
CLAUDE_CONDITION_LIMIT,
|
|
227
|
+
SUPPORTED_GOAL_TARGETS,
|
|
228
|
+
compileGoalProjection,
|
|
229
|
+
renderGoalProjection,
|
|
230
|
+
};
|