agent-dealer 1.2.7 → 1.2.8
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/bundle/server/dist/adapters/agent-deck-bind.js +16 -6
- package/bundle/server/dist/adapters/agent-deck-bind.test.js +74 -0
- package/bundle/server/dist/adapters/agent-health.js +14 -3
- package/bundle/server/dist/adapters/github.js +4 -1
- package/bundle/server/dist/adapters/muse-capability.js +443 -24
- package/bundle/server/dist/adapters/muse-capability.test.js +469 -25
- package/bundle/server/dist/adapters/muse-visual-qa.js +114 -0
- package/bundle/server/dist/adapters/muse-visual-qa.test.js +68 -0
- package/bundle/server/dist/capacity/muse-probe.js +56 -9
- package/bundle/server/dist/capacity/muse-probe.test.js +188 -1
- package/bundle/server/dist/coordinator/admission.js +14 -1
- package/bundle/server/dist/coordinator/admission.test.js +197 -4
- package/bundle/server/dist/coordinator/auto-merge.integration.test.js +276 -0
- package/bundle/server/dist/coordinator/auto-merge.js +39 -3
- package/bundle/server/dist/coordinator/commands.js +90 -5
- package/bundle/server/dist/coordinator/deck-outage.integration.test.js +4 -2
- package/bundle/server/dist/coordinator/developer-effect.js +79 -1
- package/bundle/server/dist/coordinator/execution-report.js +6 -0
- package/bundle/server/dist/coordinator/execution-report.test.js +7 -0
- package/bundle/server/dist/coordinator/failure-cause.js +64 -1
- package/bundle/server/dist/coordinator/failure-cause.test.js +92 -1
- package/bundle/server/dist/coordinator/failure-reason.js +7 -0
- package/bundle/server/dist/coordinator/failure-reason.test.js +14 -0
- package/bundle/server/dist/coordinator/human-resolution.js +17 -1
- package/bundle/server/dist/coordinator/merge-conflict-sync.js +647 -0
- package/bundle/server/dist/coordinator/merge-conflict-sync.test.js +121 -0
- package/bundle/server/dist/coordinator/muse-developer.integration.test.js +9 -4
- package/bundle/server/dist/coordinator/muse-spawn.js +122 -29
- package/bundle/server/dist/coordinator/muse-spawn.test.js +210 -0
- package/bundle/server/dist/coordinator/playbook-feedback.js +690 -0
- package/bundle/server/dist/coordinator/playbook-feedback.test.js +702 -0
- package/bundle/server/dist/coordinator/prompts-execution-contract.test.js +107 -0
- package/bundle/server/dist/coordinator/prompts.js +78 -2
- package/bundle/server/dist/coordinator/prompts.test.js +83 -0
- package/bundle/server/dist/coordinator/reflect-trigger.js +33 -169
- package/bundle/server/dist/coordinator/reflect-trigger.test.js +149 -200
- package/bundle/server/dist/coordinator/reviewer-effect.js +10 -1
- package/bundle/server/dist/coordinator/reviewer-result.js +6 -0
- package/bundle/server/dist/coordinator/routing.test.js +14 -0
- package/bundle/server/dist/coordinator/session-timeouts.js +30 -0
- package/bundle/server/dist/coordinator/usage-cap.integration.test.js +1 -1
- package/bundle/server/dist/coordinator/worker-loop.js +18 -4
- package/bundle/server/dist/db/index.js +5 -0
- package/bundle/server/dist/db/schema.sql +3 -0
- package/bundle/server/dist/docs-execution-analysis.test.js +1 -0
- package/bundle/server/dist/repository/artifacts-for-issue.js +3 -3
- package/bundle/server/dist/repository/human-actions.js +14 -0
- package/bundle/server/dist/repository/issues.js +29 -4
- package/bundle/server/dist/repository/worker-sessions.js +51 -2
- package/bundle/server/dist/routes/human-actions.js +10 -8
- package/bundle/server/dist/routes/issues-execution-contract.test.js +321 -0
- package/bundle/server/dist/routes/issues.js +19 -2
- package/bundle/server/dist/runners/muse-code-jsonl.js +106 -11
- package/bundle/server/dist/runners/muse-config-core.js +18 -1
- package/bundle/server/dist/runners/muse-config.test.js +37 -0
- package/bundle/server/dist/runners/muse-serve-session.js +5 -0
- package/bundle/server/dist/runners/spawn-cli.js +116 -0
- package/bundle/server/dist/runners/spawn-cli.test.js +124 -0
- package/bundle/server/package.json +2 -2
- package/bundle/server/static-ui/assets/{index-yLyxRd-7.js → index-B6SVCzMR.js} +14 -14
- package/bundle/server/static-ui/assets/{index-DyAJNyfV.css → index-K_YcYkQU.css} +1 -1
- package/bundle/server/static-ui/index.html +2 -2
- package/bundle/shared/dist/execution-analysis.d.ts +22 -22
- package/bundle/shared/dist/execution-contract.d.ts +117 -0
- package/bundle/shared/dist/execution-contract.js +307 -0
- package/bundle/shared/dist/execution-contract.test.d.ts +2 -0
- package/bundle/shared/dist/execution-contract.test.js +499 -0
- package/bundle/shared/dist/execution-report.d.ts +9 -9
- package/bundle/shared/dist/execution-report.js +2 -0
- package/bundle/shared/dist/failure-cause.d.ts +4 -4
- package/bundle/shared/dist/failure-cause.js +2 -0
- package/bundle/shared/dist/human-actions.d.ts +4 -4
- package/bundle/shared/dist/human-actions.js +9 -0
- package/bundle/shared/dist/index.d.ts +85 -84
- package/bundle/shared/dist/index.js +3 -0
- package/bundle/shared/dist/issues.d.ts +146 -16
- package/bundle/shared/dist/issues.js +8 -0
- package/bundle/shared/dist/issues.test.js +1 -0
- package/bundle/shared/dist/outbound-draft.d.ts +12 -12
- package/bundle/shared/dist/worker-sessions.d.ts +10 -0
- package/bundle/shared/dist/worker-sessions.js +8 -0
- package/bundle/shared/dist/worker-sessions.test.js +1 -0
- package/bundle/shared/dist/workflow.d.ts +4 -4
- package/bundle/shared/dist/workflow.js +10 -0
- package/bundle/shared/dist/workflow.test.js +2 -0
- package/bundle/shared/package.json +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* NOT-306: frozen Dealer execution contracts compiled from Planner-authored
|
|
4
|
+
* Linear tickets.
|
|
5
|
+
*
|
|
6
|
+
* A Planner brief carries its execution semantics as canonical Markdown
|
|
7
|
+
* headings in the ticket description. This module is the ONE implementation
|
|
8
|
+
* that parses, validates, and structures those headings — shared by the web
|
|
9
|
+
* intake, the API routes, and (through the API) the CLI, so no intake path
|
|
10
|
+
* keeps its own regex or drifts from the others.
|
|
11
|
+
*
|
|
12
|
+
* The ticket description stays the untouched source of truth; the contract is
|
|
13
|
+
* derived data. A description with none of the canonical headings is a
|
|
14
|
+
* compatible legacy issue (contract `null`) and existing/manual issues are
|
|
15
|
+
* never blocked.
|
|
16
|
+
*/
|
|
17
|
+
export const EXECUTION_CONTRACT_VERSION = "v1";
|
|
18
|
+
export const ExecutionMode = z.enum(["feature", "bug fix", "refactor", "investigation", "split child"]);
|
|
19
|
+
const EXECUTION_MODES = ExecutionMode.options;
|
|
20
|
+
export const ExecutionContractCriterion = z.object({
|
|
21
|
+
text: z.string(),
|
|
22
|
+
/** Raw `Evidence:` line body (where to look, action/command, expected result). */
|
|
23
|
+
evidence: z.string().nullable(),
|
|
24
|
+
});
|
|
25
|
+
export const ExecutionContractV1 = z.object({
|
|
26
|
+
version: z.literal(EXECUTION_CONTRACT_VERSION),
|
|
27
|
+
executionMode: ExecutionMode,
|
|
28
|
+
nonGoals: z.array(z.string()),
|
|
29
|
+
exitPredicate: z.string(),
|
|
30
|
+
onePrStoppingPoint: z.string(),
|
|
31
|
+
acceptanceCriteria: z.array(ExecutionContractCriterion),
|
|
32
|
+
});
|
|
33
|
+
/** Display names of the canonical `##` headings, in ticket order. */
|
|
34
|
+
export const EXECUTION_CONTRACT_HEADINGS = {
|
|
35
|
+
executionMode: "Builder execution mode",
|
|
36
|
+
nonGoals: "Non-goals",
|
|
37
|
+
exitPredicate: "Exit predicate",
|
|
38
|
+
onePrStoppingPoint: "One-PR stopping point",
|
|
39
|
+
acceptanceCriteria: "Acceptance criteria",
|
|
40
|
+
};
|
|
41
|
+
/** Actionable validation failure — callers map this to a 400, never a silent drop. */
|
|
42
|
+
export class ExecutionContractError extends Error {
|
|
43
|
+
constructor(message) {
|
|
44
|
+
super(`Execution contract: ${message}`);
|
|
45
|
+
this.name = "ExecutionContractError";
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const SECTION_KEYS = Object.keys(EXECUTION_CONTRACT_HEADINGS);
|
|
49
|
+
/** Canonical key for a raw `##` heading: lowercase, hyphens→spaces, collapsed. */
|
|
50
|
+
function normalizeHeadingName(raw) {
|
|
51
|
+
return raw.trim().toLowerCase().replace(/[-_]+/g, " ").replace(/\s+/g, " ").trim();
|
|
52
|
+
}
|
|
53
|
+
const CANONICAL_BY_NORMALIZED = Object.fromEntries(SECTION_KEYS.map((key) => [
|
|
54
|
+
normalizeHeadingName(EXECUTION_CONTRACT_HEADINGS[key]),
|
|
55
|
+
key,
|
|
56
|
+
]));
|
|
57
|
+
/** The four Planner-signal headings. `Acceptance criteria` alone stays legacy. */
|
|
58
|
+
const SIGNAL_KEYS = [
|
|
59
|
+
"executionMode",
|
|
60
|
+
"nonGoals",
|
|
61
|
+
"exitPredicate",
|
|
62
|
+
"onePrStoppingPoint",
|
|
63
|
+
];
|
|
64
|
+
/** Split Markdown into `##`-level sections (only `##`, never `#` or `###`). */
|
|
65
|
+
function splitSections(description) {
|
|
66
|
+
const lines = description.split("\n");
|
|
67
|
+
const sections = [];
|
|
68
|
+
let current = null;
|
|
69
|
+
for (const line of lines) {
|
|
70
|
+
const heading = line.match(/^##\s+(.+?)\s*$/);
|
|
71
|
+
if (heading?.[1] !== undefined) {
|
|
72
|
+
if (current)
|
|
73
|
+
sections.push({ name: current.name, body: current.body.join("\n") });
|
|
74
|
+
current = { name: heading[1].trim(), body: [] };
|
|
75
|
+
}
|
|
76
|
+
else if (current) {
|
|
77
|
+
current.body.push(line);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (current)
|
|
81
|
+
sections.push({ name: current.name, body: current.body.join("\n") });
|
|
82
|
+
return sections;
|
|
83
|
+
}
|
|
84
|
+
/** Normalize an execution-mode value (`Bug-Fix` → `bug fix`); null when unknown. */
|
|
85
|
+
function normalizeExecutionMode(raw) {
|
|
86
|
+
const collapsed = raw
|
|
87
|
+
.trim()
|
|
88
|
+
.toLowerCase()
|
|
89
|
+
.replace(/[-_]+/g, " ")
|
|
90
|
+
.replace(/\s+/g, " ")
|
|
91
|
+
.trim()
|
|
92
|
+
// Planner tickets punctuate the value as prose (`Feature.`); trailing
|
|
93
|
+
// sentence punctuation is not part of the mode.
|
|
94
|
+
.replace(/[.\u3002!?;:,'"\)\]]+$/g, "")
|
|
95
|
+
.trim();
|
|
96
|
+
if (collapsed === "bugfix")
|
|
97
|
+
return "bug fix";
|
|
98
|
+
const found = EXECUTION_MODES.find((m) => m === collapsed);
|
|
99
|
+
return (found ?? null);
|
|
100
|
+
}
|
|
101
|
+
function requireSection(sections, key) {
|
|
102
|
+
const section = sections.get(key);
|
|
103
|
+
if (!section) {
|
|
104
|
+
throw new ExecutionContractError(`missing \`## ${EXECUTION_CONTRACT_HEADINGS[key]}\` — a ticket with any execution-contract heading must carry all five contract sections`);
|
|
105
|
+
}
|
|
106
|
+
return section;
|
|
107
|
+
}
|
|
108
|
+
function parseNonGoals(body) {
|
|
109
|
+
const items = [];
|
|
110
|
+
for (const line of body.split("\n")) {
|
|
111
|
+
if (!line.trim())
|
|
112
|
+
continue;
|
|
113
|
+
const item = line.match(/^\s*(?:[-*+]|\d+[.)])\s+(.+?)\s*$/);
|
|
114
|
+
if (!item?.[1]) {
|
|
115
|
+
throw new ExecutionContractError(`\`## Non-goals\` must be a Markdown list — got a non-list line: "${line.trim()}"`);
|
|
116
|
+
}
|
|
117
|
+
items.push(item[1]);
|
|
118
|
+
}
|
|
119
|
+
if (items.length === 0) {
|
|
120
|
+
throw new ExecutionContractError("`## Non-goals` must list at least one non-goal");
|
|
121
|
+
}
|
|
122
|
+
return items;
|
|
123
|
+
}
|
|
124
|
+
function parseExitPredicate(body) {
|
|
125
|
+
const text = body.trim();
|
|
126
|
+
if (!text) {
|
|
127
|
+
throw new ExecutionContractError("`## Exit predicate` must be one observable completion statement (got empty)");
|
|
128
|
+
}
|
|
129
|
+
if (/\n\s*\n/.test(text)) {
|
|
130
|
+
throw new ExecutionContractError("`## Exit predicate` must be one observable completion statement (a single paragraph)");
|
|
131
|
+
}
|
|
132
|
+
return text;
|
|
133
|
+
}
|
|
134
|
+
function parseOnePrStoppingPoint(body) {
|
|
135
|
+
const text = body.trim();
|
|
136
|
+
if (!text) {
|
|
137
|
+
throw new ExecutionContractError("`## One-PR stopping point` must state the boundary where this worker must stop (got empty)");
|
|
138
|
+
}
|
|
139
|
+
return text;
|
|
140
|
+
}
|
|
141
|
+
const CHECKBOX_RE = /^\s*[-*]\s+\[([ xX])\]\s+(.*)$/;
|
|
142
|
+
// Planner/Linear evidence lines carry the ticket's nested bullet (` * Evidence: ...`);
|
|
143
|
+
// accept an optional bullet marker so that canonical shape parses as evidence.
|
|
144
|
+
const EVIDENCE_RE = /^\s*(?:[-*+]\s+)?Evidence\s*:\s*(.*)$/i;
|
|
145
|
+
const BULLET_RE = /^\s*(?:[-*+]|\d+[.)])\s+/;
|
|
146
|
+
function normalizeCriterionText(raw) {
|
|
147
|
+
return raw.trim().replace(/\s+/g, " ").toLowerCase();
|
|
148
|
+
}
|
|
149
|
+
function parseAcceptanceCriteria(body) {
|
|
150
|
+
const criteria = [];
|
|
151
|
+
const seen = new Map();
|
|
152
|
+
let current = null;
|
|
153
|
+
let hasEvidence = false;
|
|
154
|
+
const flush = () => {
|
|
155
|
+
if (current)
|
|
156
|
+
criteria.push(current);
|
|
157
|
+
current = null;
|
|
158
|
+
hasEvidence = false;
|
|
159
|
+
};
|
|
160
|
+
for (const line of body.split("\n")) {
|
|
161
|
+
if (!line.trim())
|
|
162
|
+
continue;
|
|
163
|
+
const checkbox = line.match(CHECKBOX_RE);
|
|
164
|
+
if (checkbox) {
|
|
165
|
+
flush();
|
|
166
|
+
const text = (checkbox[2] ?? "").trim();
|
|
167
|
+
if (!text) {
|
|
168
|
+
throw new ExecutionContractError("`## Acceptance criteria` has a checkbox item with no text");
|
|
169
|
+
}
|
|
170
|
+
const key = normalizeCriterionText(text);
|
|
171
|
+
const first = seen.get(key);
|
|
172
|
+
if (first !== undefined) {
|
|
173
|
+
throw new ExecutionContractError(`\`## Acceptance criteria\` has a duplicate criterion: "${first}" — keep each criterion exactly once`);
|
|
174
|
+
}
|
|
175
|
+
seen.set(key, text);
|
|
176
|
+
current = { text, evidence: null };
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
const evidence = line.match(EVIDENCE_RE);
|
|
180
|
+
if (evidence) {
|
|
181
|
+
if (!current) {
|
|
182
|
+
throw new ExecutionContractError("`## Acceptance criteria` has an `Evidence:` line before any criterion — indent it under the criterion it belongs to");
|
|
183
|
+
}
|
|
184
|
+
if (hasEvidence) {
|
|
185
|
+
throw new ExecutionContractError(`\`## Acceptance criteria\` has two \`Evidence:\` lines for one criterion ("${current.text}") — keep exactly one`);
|
|
186
|
+
}
|
|
187
|
+
const value = (evidence[1] ?? "").trim();
|
|
188
|
+
if (!value) {
|
|
189
|
+
throw new ExecutionContractError(`\`## Acceptance criteria\` has an empty \`Evidence:\` line for "${current.text}" — state where to look, the action/command, and the expected result`);
|
|
190
|
+
}
|
|
191
|
+
current.evidence = value;
|
|
192
|
+
hasEvidence = true;
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
if (BULLET_RE.test(line)) {
|
|
196
|
+
throw new ExecutionContractError(`\`## Acceptance criteria\` items must be checkboxes (\`- [ ] text\`) with an optional indented \`Evidence:\` line — got a plain bullet: "${line.trim()}"`);
|
|
197
|
+
}
|
|
198
|
+
throw new ExecutionContractError(`\`## Acceptance criteria\` lines must be \`- [ ]\` checkbox items or indented \`Evidence:\` lines — got: "${line.trim()}"`);
|
|
199
|
+
}
|
|
200
|
+
flush();
|
|
201
|
+
if (criteria.length === 0) {
|
|
202
|
+
throw new ExecutionContractError("`## Acceptance criteria` must contain at least one `- [ ]` checkbox item");
|
|
203
|
+
}
|
|
204
|
+
return criteria;
|
|
205
|
+
}
|
|
206
|
+
/** Re-serialize contract criteria as the legacy acceptance-criteria text column. */
|
|
207
|
+
export function renderContractAcceptanceCriteria(contract) {
|
|
208
|
+
const lines = [];
|
|
209
|
+
for (const criterion of contract.acceptanceCriteria) {
|
|
210
|
+
lines.push(`- [ ] ${criterion.text}`);
|
|
211
|
+
if (criterion.evidence)
|
|
212
|
+
lines.push(` Evidence: ${criterion.evidence}`);
|
|
213
|
+
}
|
|
214
|
+
return lines.join("\n");
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Compile a ticket description into its execution contract. Throws
|
|
218
|
+
* {@link ExecutionContractError} with an actionable message once any
|
|
219
|
+
* contract-signal heading is present and the brief is ambiguous or malformed.
|
|
220
|
+
* Never throws for a completely contract-free (legacy) description.
|
|
221
|
+
*/
|
|
222
|
+
export function compileExecutionContract(description) {
|
|
223
|
+
if (!description?.trim()) {
|
|
224
|
+
return { contract: null, acceptanceCriteria: null, hasContractHeadings: false };
|
|
225
|
+
}
|
|
226
|
+
const found = new Map();
|
|
227
|
+
for (const section of splitSections(description)) {
|
|
228
|
+
const key = CANONICAL_BY_NORMALIZED[normalizeHeadingName(section.name)];
|
|
229
|
+
if (!key)
|
|
230
|
+
continue;
|
|
231
|
+
const prev = found.get(key);
|
|
232
|
+
if (prev) {
|
|
233
|
+
throw new ExecutionContractError(`duplicate \`## ${EXECUTION_CONTRACT_HEADINGS[key]}\` section — keep exactly one and move the extra content into it`);
|
|
234
|
+
}
|
|
235
|
+
found.set(key, { name: section.name, key, body: section.body });
|
|
236
|
+
}
|
|
237
|
+
if (found.size === 0) {
|
|
238
|
+
return { contract: null, acceptanceCriteria: null, hasContractHeadings: false };
|
|
239
|
+
}
|
|
240
|
+
const hasSignal = SIGNAL_KEYS.some((key) => found.has(key));
|
|
241
|
+
if (!hasSignal) {
|
|
242
|
+
// `## Acceptance criteria` alone is the pre-contract Linear intake shape:
|
|
243
|
+
// keep extracting its raw text exactly as before, without validation.
|
|
244
|
+
const raw = found.get("acceptanceCriteria").body.trim();
|
|
245
|
+
return { contract: null, acceptanceCriteria: raw || null, hasContractHeadings: true };
|
|
246
|
+
}
|
|
247
|
+
const modeSection = requireSection(found, "executionMode");
|
|
248
|
+
const modeLines = modeSection.body.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
249
|
+
if (modeLines.length !== 1) {
|
|
250
|
+
throw new ExecutionContractError(`\`## Builder execution mode\` must state exactly one execution mode (got ${modeLines.length} non-empty lines)`);
|
|
251
|
+
}
|
|
252
|
+
const executionMode = normalizeExecutionMode(modeLines[0]);
|
|
253
|
+
if (!executionMode) {
|
|
254
|
+
throw new ExecutionContractError(`unknown execution mode "${modeLines[0]}" — use exactly one of: ${EXECUTION_MODES.join(" | ")}`);
|
|
255
|
+
}
|
|
256
|
+
const nonGoals = parseNonGoals(requireSection(found, "nonGoals").body);
|
|
257
|
+
const exitPredicate = parseExitPredicate(requireSection(found, "exitPredicate").body);
|
|
258
|
+
const onePrStoppingPoint = parseOnePrStoppingPoint(requireSection(found, "onePrStoppingPoint").body);
|
|
259
|
+
const acceptanceCriteria = parseAcceptanceCriteria(requireSection(found, "acceptanceCriteria").body);
|
|
260
|
+
const contract = {
|
|
261
|
+
version: EXECUTION_CONTRACT_VERSION,
|
|
262
|
+
executionMode,
|
|
263
|
+
nonGoals,
|
|
264
|
+
exitPredicate,
|
|
265
|
+
onePrStoppingPoint,
|
|
266
|
+
acceptanceCriteria,
|
|
267
|
+
};
|
|
268
|
+
return {
|
|
269
|
+
contract,
|
|
270
|
+
acceptanceCriteria: renderContractAcceptanceCriteria(contract),
|
|
271
|
+
hasContractHeadings: true,
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
/** True when the description carries any Planner-signal heading (a contract ticket). */
|
|
275
|
+
export function hasExecutionContractSignal(description) {
|
|
276
|
+
if (!description?.trim())
|
|
277
|
+
return false;
|
|
278
|
+
for (const section of splitSections(description)) {
|
|
279
|
+
const key = CANONICAL_BY_NORMALIZED[normalizeHeadingName(section.name)];
|
|
280
|
+
if (key && SIGNAL_KEYS.includes(key))
|
|
281
|
+
return true;
|
|
282
|
+
}
|
|
283
|
+
return false;
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Resolve the issue-row fields derived from a ticket description: validate the
|
|
287
|
+
* contract when signal headings are present (throws) and fill a missing
|
|
288
|
+
* legacy acceptance-criteria value from the description. An explicitly
|
|
289
|
+
* provided non-empty value always wins — the contract never silently drops it.
|
|
290
|
+
*/
|
|
291
|
+
export function resolveIssueContractFields(description, explicitAcceptanceCriteria) {
|
|
292
|
+
const compiled = compileExecutionContract(description ?? null);
|
|
293
|
+
const explicit = explicitAcceptanceCriteria?.trim() ? explicitAcceptanceCriteria : null;
|
|
294
|
+
return {
|
|
295
|
+
executionContract: compiled.contract,
|
|
296
|
+
acceptanceCriteria: explicit ?? compiled.acceptanceCriteria ?? null,
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
/** Best-effort contract for read paths — legacy/unparseable descriptions read as null. */
|
|
300
|
+
export function tryCompileContract(description) {
|
|
301
|
+
try {
|
|
302
|
+
return compileExecutionContract(description).contract;
|
|
303
|
+
}
|
|
304
|
+
catch {
|
|
305
|
+
return null;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/** Planner-authored Linear ticket carrying every contract-v1 heading. */
|
|
2
|
+
export declare const FULL_CONTRACT_DESCRIPTION = "Ship the frozen execution contract for Planner tickets.\n\n## Builder execution mode\nfeature\n\n## Non-goals\n- Selecting a playbook or reasoning about implementation architecture\n- Adding a generic workflow/schema editor\n\n## Exit predicate\nImporting a Planner-authored Linear ticket yields a versioned frozen execution contract visible in Dealer and both worker prompts.\n\n## One-PR stopping point\nStop after the contract compiles from ticket Markdown, persists/freezes with the Issue workflow, appears read-only in Issue Detail, and renders in developer/reviewer prompts.\n\n## Acceptance criteria\n- [ ] Full ticket imports into the exact structured schema\n Evidence: shared compiler fixtures | run the compiler test | every field asserted\n- [ ] Legacy issues without headings still start\n Evidence: migration tests | run the legacy suite | green\n";
|