@agent-plan/core 0.2.26 → 0.2.28
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/dist/description-freshness.d.ts +37 -0
- package/dist/description-freshness.d.ts.map +1 -0
- package/dist/description-freshness.js +84 -0
- package/dist/handoff-context.d.ts +135 -3
- package/dist/handoff-context.d.ts.map +1 -1
- package/dist/handoff-context.js +336 -25
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/package-version.d.ts +2 -0
- package/dist/package-version.d.ts.map +1 -1
- package/dist/package-version.js +1 -1
- package/dist/payload-fallback.d.ts +1 -1
- package/dist/payload-fallback.d.ts.map +1 -1
- package/dist/payload-fallback.js +6 -0
- package/dist/plan-store.d.ts +129 -30
- package/dist/plan-store.d.ts.map +1 -1
- package/dist/plan-store.js +690 -115
- package/dist/planner-rules.d.ts.map +1 -1
- package/dist/planner-rules.js +6 -2
- package/dist/read-tracking.d.ts +27 -3
- package/dist/read-tracking.d.ts.map +1 -1
- package/dist/read-tracking.js +53 -4
- package/dist/recap.d.ts.map +1 -1
- package/dist/recap.js +33 -9
- package/dist/renderer.d.ts.map +1 -1
- package/dist/renderer.js +0 -1
- package/dist/requirement-macro-tasks.d.ts +2 -2
- package/dist/requirement-macro-tasks.d.ts.map +1 -1
- package/dist/requirement-macro-tasks.js +2 -2
- package/dist/runtime-diagnostics.d.ts +34 -0
- package/dist/runtime-diagnostics.d.ts.map +1 -0
- package/dist/runtime-diagnostics.js +39 -0
- package/dist/schema.d.ts +487 -17
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +24 -4
- package/dist/task-context.d.ts +41 -2
- package/dist/task-context.d.ts.map +1 -1
- package/dist/task-context.js +102 -4
- package/dist/task-selection.d.ts +44 -1
- package/dist/task-selection.d.ts.map +1 -1
- package/dist/task-selection.js +158 -7
- package/dist/write-coordination.d.ts +27 -0
- package/dist/write-coordination.d.ts.map +1 -0
- package/dist/write-coordination.js +223 -0
- package/package.json +1 -1
- package/planner-skill.md +36 -20
package/dist/handoff-context.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
+
import { buildPhaseWorkMap } from "./task-context.js";
|
|
2
3
|
export const COMPLETION_SUMMARY_HEADING = "**Completion summary:**";
|
|
3
4
|
export const HANDOFF_COMPLETENESS_AUDIT_VERSION = 1;
|
|
5
|
+
export const HANDOFF_COLD_START_INVENTORY_VERSION = 1;
|
|
6
|
+
export const TARGET_HANDOFF_CONTENT_CHARS = 8_000;
|
|
4
7
|
export const MAX_HANDOFF_CONTENT_CHARS = 24_000;
|
|
5
8
|
export const HANDOFF_AUDIT_START_MARKER = "<!-- agent-plan:handoff-audit:start -->";
|
|
6
9
|
export const HANDOFF_AUDIT_END_MARKER = "<!-- agent-plan:handoff-audit:end -->";
|
|
@@ -24,6 +27,37 @@ export const HANDOFF_COMPLETENESS_CATEGORIES = [
|
|
|
24
27
|
{ id: "project-operating-notes", label: "Project-specific operating notes" },
|
|
25
28
|
{ id: "conversation-only-facts", label: "Conversation-only facts" },
|
|
26
29
|
];
|
|
30
|
+
export const HANDOFF_COLD_START_SOURCE_REVIEWS = [
|
|
31
|
+
{ id: "conversation", label: "Conversation, user corrections, and authorization state" },
|
|
32
|
+
{ id: "planner-entities", label: "Task, sibling tasks, phase, feature, requirements, and prior handoff" },
|
|
33
|
+
{ id: "working-tree", label: "Changed files, diffs, implementation state, and ownership" },
|
|
34
|
+
{ id: "verification-runtime", label: "Commands, test results, runtime observations, and limitations" },
|
|
35
|
+
{ id: "peer-agent-output", label: "Peer-agent messages and delegated-work results, or confirmation that none exist" },
|
|
36
|
+
];
|
|
37
|
+
export const HANDOFF_COLD_START_INVENTORY_CATEGORIES = [
|
|
38
|
+
{ id: "files", label: "Exact files" },
|
|
39
|
+
{ id: "symbols", label: "Exact symbols and identifiers" },
|
|
40
|
+
{ id: "working-tree-ownership", label: "Working-tree state, work ownership, and commit/discard authorization" },
|
|
41
|
+
{ id: "negative-state", label: "Work not started, removals not performed, and intentionally untouched state" },
|
|
42
|
+
{ id: "commands-tools", label: "Commands and tool paths" },
|
|
43
|
+
{ id: "runtime-wiring", label: "Runtime wiring, call sites, and data flow" },
|
|
44
|
+
{ id: "preservation-constraints", label: "Behavior and code paths that must survive" },
|
|
45
|
+
{ id: "verification-evidence", label: "Concrete observations proving completed, live, dead, or inert state" },
|
|
46
|
+
{ id: "related-planned-work", label: "Sibling tasks and related planned capabilities" },
|
|
47
|
+
{ id: "user-visible-behavior", label: "User-visible behavior" },
|
|
48
|
+
{ id: "operator-actions", label: "Operator actions" },
|
|
49
|
+
{ id: "blockers-risks", label: "Blockers and risks" },
|
|
50
|
+
{ id: "remaining-work", label: "Remaining work" },
|
|
51
|
+
{ id: "ordered-resume-steps", label: "Ordered resume steps" },
|
|
52
|
+
];
|
|
53
|
+
export const HANDOFF_CANONICAL_SECTIONS = [
|
|
54
|
+
"Current focus",
|
|
55
|
+
"Current and partial state",
|
|
56
|
+
"Preservation constraints",
|
|
57
|
+
"Supporting documents",
|
|
58
|
+
"Blockers and risks",
|
|
59
|
+
"How to resume",
|
|
60
|
+
];
|
|
27
61
|
export class HandoffContractError extends Error {
|
|
28
62
|
code;
|
|
29
63
|
details;
|
|
@@ -41,6 +75,37 @@ export function hasTaskCompletionEvidence(task) {
|
|
|
41
75
|
return true;
|
|
42
76
|
return task.statusLog.some((entry) => entry.toStatus === "done" && entry.description.trim().length > 0);
|
|
43
77
|
}
|
|
78
|
+
export function buildHandoffDraftTemplate(phase, feature) {
|
|
79
|
+
const featureRef = `F${String(feature.number).padStart(3, "0")}`;
|
|
80
|
+
const phaseRef = `P${String(phase.number).padStart(3, "0")}(${featureRef})`;
|
|
81
|
+
return [
|
|
82
|
+
`# ${phaseRef} — {{REQUIRED: meaningful handoff title}}`,
|
|
83
|
+
"",
|
|
84
|
+
"<!-- Planner-generated: Created at, Updated at, and structured Reason. Do not write these lines in the draft. -->",
|
|
85
|
+
"",
|
|
86
|
+
"## Current focus",
|
|
87
|
+
`- Feature: ${featureRef} — ${feature.name}`,
|
|
88
|
+
`- Phase: ${phaseRef} — ${phase.title}`,
|
|
89
|
+
"- Task: {{REQUIRED: exact composite task ref and title}}",
|
|
90
|
+
"- Exact resume point: {{REQUIRED: file, symbol, command, or state boundary}}",
|
|
91
|
+
"",
|
|
92
|
+
"## Current and partial state",
|
|
93
|
+
"{{REQUIRED: concise completed/current/partial state only; keep extended detail in supporting documents}}",
|
|
94
|
+
"",
|
|
95
|
+
"## Preservation constraints",
|
|
96
|
+
"{{REQUIRED: minimal behaviors and boundaries that must survive}}",
|
|
97
|
+
"",
|
|
98
|
+
"## Supporting documents",
|
|
99
|
+
"- {{REQUIRED: ordered .planner/docs/*.md links with why each is needed, or an explicit verified statement that none are needed}}",
|
|
100
|
+
"",
|
|
101
|
+
"## Blockers and risks",
|
|
102
|
+
"- {{REQUIRED: blocker/risk, or a substantive verified statement that none apply}}",
|
|
103
|
+
"",
|
|
104
|
+
"## How to resume",
|
|
105
|
+
"1. {{REQUIRED: first exact action, including file/symbol/command}}",
|
|
106
|
+
"2. {{REQUIRED: subsequent ordered actions and verification}}",
|
|
107
|
+
].join("\n");
|
|
108
|
+
}
|
|
44
109
|
export function auditPhaseHandoff(phase, feature) {
|
|
45
110
|
const missingCompletionTasks = phase.tasks
|
|
46
111
|
.filter((task) => task.status === "done" && !hasTaskCompletionEvidence(task))
|
|
@@ -53,9 +118,20 @@ export function auditPhaseHandoff(phase, feature) {
|
|
|
53
118
|
missingCompletionTaskIds: missingCompletionTasks.map((task) => task.id),
|
|
54
119
|
missingCompletionTasks,
|
|
55
120
|
completenessVersion: HANDOFF_COMPLETENESS_AUDIT_VERSION,
|
|
121
|
+
targetContentChars: TARGET_HANDOFF_CONTENT_CHARS,
|
|
56
122
|
maxContentChars: MAX_HANDOFF_CONTENT_CHARS,
|
|
57
123
|
completenessCategories: HANDOFF_COMPLETENESS_CATEGORIES,
|
|
124
|
+
canonicalSections: HANDOFF_CANONICAL_SECTIONS,
|
|
125
|
+
requiredHumanInputs: [
|
|
126
|
+
{ id: "title", label: "Meaningful title", description: "A concise handoff title; the planner renders it as the H1." },
|
|
127
|
+
{ id: "reason", label: "Reason", description: "Why work is stopping and why a cold agent needs this handoff; the planner renders it as metadata." },
|
|
128
|
+
],
|
|
129
|
+
draftTemplate: buildHandoffDraftTemplate(phase, feature),
|
|
130
|
+
coldStartInventoryVersion: HANDOFF_COLD_START_INVENTORY_VERSION,
|
|
131
|
+
coldStartSourceReviews: HANDOFF_COLD_START_SOURCE_REVIEWS,
|
|
132
|
+
coldStartInventoryCategories: HANDOFF_COLD_START_INVENTORY_CATEGORIES,
|
|
58
133
|
existingCompletenessAudit: phase.handoffAudit,
|
|
134
|
+
phaseWorkMap: buildPhaseWorkMap(phase, feature.number),
|
|
59
135
|
};
|
|
60
136
|
}
|
|
61
137
|
function nonEmpty(value, field) {
|
|
@@ -93,7 +169,14 @@ export function handoffContentHash(content) {
|
|
|
93
169
|
}
|
|
94
170
|
export function validateHandoffCompletenessAudit(audit) {
|
|
95
171
|
const expectedIds = HANDOFF_COMPLETENESS_CATEGORIES.map((entry) => entry.id);
|
|
96
|
-
if (!audit
|
|
172
|
+
if (!audit) {
|
|
173
|
+
return expectedIds.map((category) => ({
|
|
174
|
+
category,
|
|
175
|
+
status: "captured",
|
|
176
|
+
detail: "Derived from the compact handoff and persisted planner state; no duplicate prose audit was supplied.",
|
|
177
|
+
}));
|
|
178
|
+
}
|
|
179
|
+
if (audit.version !== HANDOFF_COMPLETENESS_AUDIT_VERSION) {
|
|
97
180
|
throw new HandoffContractError("HANDOFF_COMPLETENESS_AUDIT_REQUIRED", `Handoff completeness audit version ${HANDOFF_COMPLETENESS_AUDIT_VERSION} is required.`, { requiredVersion: HANDOFF_COMPLETENESS_AUDIT_VERSION, missingCategories: expectedIds });
|
|
98
181
|
}
|
|
99
182
|
const byCategory = new Map();
|
|
@@ -115,6 +198,202 @@ export function validateHandoffCompletenessAudit(audit) {
|
|
|
115
198
|
}
|
|
116
199
|
return expectedIds.map((id) => byCategory.get(id));
|
|
117
200
|
}
|
|
201
|
+
export function validateHandoffColdStartInventory(inventory, content, supportingDocumentContents = []) {
|
|
202
|
+
const expectedIds = HANDOFF_COLD_START_INVENTORY_CATEGORIES.map((entry) => entry.id);
|
|
203
|
+
if (!inventory) {
|
|
204
|
+
return expectedIds.map((category) => ({
|
|
205
|
+
category,
|
|
206
|
+
items: ["Derived from the compact handoff and persisted planner state."],
|
|
207
|
+
}));
|
|
208
|
+
}
|
|
209
|
+
if (inventory.version !== HANDOFF_COLD_START_INVENTORY_VERSION) {
|
|
210
|
+
throw new HandoffContractError("HANDOFF_COLD_START_INVENTORY_REQUIRED", `Cold-start inventory version ${HANDOFF_COLD_START_INVENTORY_VERSION} is required when legacy inventory evidence is supplied.`, { requiredVersion: HANDOFF_COLD_START_INVENTORY_VERSION, missingCategories: expectedIds });
|
|
211
|
+
}
|
|
212
|
+
const requiredSources = HANDOFF_COLD_START_SOURCE_REVIEWS.map((entry) => entry.id);
|
|
213
|
+
const requiredSourceIds = new Set(requiredSources);
|
|
214
|
+
const sourceReviews = new Map();
|
|
215
|
+
const duplicateSources = [];
|
|
216
|
+
for (const review of inventory.sourceReviews) {
|
|
217
|
+
if (sourceReviews.has(review.source))
|
|
218
|
+
duplicateSources.push(review.source);
|
|
219
|
+
else
|
|
220
|
+
sourceReviews.set(review.source, review.detail);
|
|
221
|
+
}
|
|
222
|
+
const missingSources = requiredSources.filter((source) => !sourceReviews.has(source));
|
|
223
|
+
const unknownSources = [...sourceReviews.keys()].filter((source) => !requiredSourceIds.has(source));
|
|
224
|
+
const invalidSources = requiredSources.filter((source) => {
|
|
225
|
+
const detail = sourceReviews.get(source);
|
|
226
|
+
return detail !== undefined && !isSubstantive(detail);
|
|
227
|
+
});
|
|
228
|
+
const byCategory = new Map();
|
|
229
|
+
const duplicates = [];
|
|
230
|
+
for (const entry of inventory.entries) {
|
|
231
|
+
if (byCategory.has(entry.category))
|
|
232
|
+
duplicates.push(entry.category);
|
|
233
|
+
else
|
|
234
|
+
byCategory.set(entry.category, entry);
|
|
235
|
+
}
|
|
236
|
+
const missingCategories = expectedIds.filter((id) => !byCategory.has(id));
|
|
237
|
+
const unknownCategories = [...byCategory.keys()].filter((id) => !expectedIds.includes(id));
|
|
238
|
+
const invalidCategories = [];
|
|
239
|
+
for (const id of expectedIds) {
|
|
240
|
+
const entry = byCategory.get(id);
|
|
241
|
+
if (!entry)
|
|
242
|
+
continue;
|
|
243
|
+
const items = uniqueStrings(entry.items);
|
|
244
|
+
const notApplicableReason = entry.notApplicableReason?.trim() ?? "";
|
|
245
|
+
if (items.length === 0)
|
|
246
|
+
invalidCategories.push(id);
|
|
247
|
+
if (notApplicableReason && !isSubstantive(notApplicableReason))
|
|
248
|
+
invalidCategories.push(id);
|
|
249
|
+
if (items.some((item) => /^(?:n\/?a|none|nothing|unknown|tbd|see above)$/i.test(item.trim())))
|
|
250
|
+
invalidCategories.push(id);
|
|
251
|
+
}
|
|
252
|
+
if (missingSources.length > 0 || unknownSources.length > 0 || duplicateSources.length > 0 || invalidSources.length > 0
|
|
253
|
+
|| missingCategories.length > 0 || unknownCategories.length > 0 || duplicates.length > 0 || invalidCategories.length > 0) {
|
|
254
|
+
throw new HandoffContractError("HANDOFF_COLD_START_INVENTORY_REQUIRED", "The cold-start inventory is incomplete. Review every prepared source before drafting; every category needs at least one concrete item. When nothing exists, the item must explicitly state the verified absence and why it matters (for example, 'No deletion has started; all original files remain intact').", {
|
|
255
|
+
missingSources,
|
|
256
|
+
invalidSources,
|
|
257
|
+
unknownSources,
|
|
258
|
+
duplicateSources: [...new Set(duplicateSources)],
|
|
259
|
+
missingCategories,
|
|
260
|
+
invalidCategories: [...new Set(invalidCategories)],
|
|
261
|
+
unknownCategories,
|
|
262
|
+
duplicateCategories: [...new Set(duplicates)],
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
const normalize = (value) => value.normalize("NFKC").replace(/[`*_]/g, "").replace(/\s+/g, " ").toLowerCase();
|
|
266
|
+
const corpus = normalize([content, ...supportingDocumentContents].join("\n"));
|
|
267
|
+
const uncoveredItems = expectedIds.flatMap((id) => {
|
|
268
|
+
const entry = byCategory.get(id);
|
|
269
|
+
return uniqueStrings(entry.items)
|
|
270
|
+
.filter((item) => !corpus.includes(normalize(item)))
|
|
271
|
+
.map((item) => ({ category: id, item }));
|
|
272
|
+
});
|
|
273
|
+
if (uncoveredItems.length > 0) {
|
|
274
|
+
throw new HandoffContractError("HANDOFF_COLD_START_INVENTORY_UNCOVERED", "Resume-critical inventory items are missing from the canonical handoff and validated supporting documents. Add each exact item before retrying; do not certify from generic prose.", { uncoveredItems });
|
|
275
|
+
}
|
|
276
|
+
return expectedIds.map((id) => {
|
|
277
|
+
const entry = byCategory.get(id);
|
|
278
|
+
return {
|
|
279
|
+
category: entry.category,
|
|
280
|
+
items: uniqueStrings(entry.items),
|
|
281
|
+
...(entry.notApplicableReason?.trim() ? { notApplicableReason: entry.notApplicableReason.trim() } : {}),
|
|
282
|
+
};
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
export function validateHandoffReadBackVerification(phase, input) {
|
|
286
|
+
const audit = phase.handoffAudit;
|
|
287
|
+
const actualContentHash = phase.handoff.trim() ? handoffContentHash(phase.handoff) : "";
|
|
288
|
+
if (!phase.handoff.trim() || !audit || audit.contentHash !== actualContentHash || audit.contentLength !== phase.handoff.length) {
|
|
289
|
+
throw new HandoffContractError("HANDOFF_READBACK_VERIFICATION_REQUIRED", "The persisted handoff or its audit metadata is missing or inconsistent. Run handoff_prepare, rewrite the handoff, then read it back before verification.", { expectedContentHash: input.expectedContentHash, actualContentHash, auditContentHash: audit?.contentHash ?? "" });
|
|
290
|
+
}
|
|
291
|
+
if (!input.expectedContentHash.trim() || input.expectedContentHash !== actualContentHash) {
|
|
292
|
+
throw new HandoffContractError("HANDOFF_READBACK_VERIFICATION_REQUIRED", "The handoff changed after the read-back candidate was selected. Call handoff_show again and verify the returned contentHash.", { expectedContentHash: input.expectedContentHash, actualContentHash });
|
|
293
|
+
}
|
|
294
|
+
const requiredSources = HANDOFF_COLD_START_SOURCE_REVIEWS.map((entry) => entry.id);
|
|
295
|
+
const suppliedReviews = input.sourceReviews ?? [];
|
|
296
|
+
if (suppliedReviews.length === 0 && (input.omissionsFound ?? []).length === 0) {
|
|
297
|
+
return requiredSources.map((source) => ({
|
|
298
|
+
source,
|
|
299
|
+
detail: "Derived from persisted handoff content, planner entities, and read-back state; no duplicate source inventory was supplied.",
|
|
300
|
+
}));
|
|
301
|
+
}
|
|
302
|
+
const bySource = new Map();
|
|
303
|
+
const duplicateSources = [];
|
|
304
|
+
for (const review of suppliedReviews) {
|
|
305
|
+
if (bySource.has(review.source))
|
|
306
|
+
duplicateSources.push(review.source);
|
|
307
|
+
else
|
|
308
|
+
bySource.set(review.source, review.detail.trim());
|
|
309
|
+
}
|
|
310
|
+
const missingSources = requiredSources.filter((source) => !bySource.has(source));
|
|
311
|
+
const unknownSources = [...bySource.keys()].filter((source) => !requiredSources.includes(source));
|
|
312
|
+
const invalidSources = requiredSources.filter((source) => {
|
|
313
|
+
const detail = bySource.get(source);
|
|
314
|
+
return detail !== undefined && !isSubstantive(detail);
|
|
315
|
+
});
|
|
316
|
+
if (missingSources.length > 0 || unknownSources.length > 0 || duplicateSources.length > 0 || invalidSources.length > 0) {
|
|
317
|
+
throw new HandoffContractError("HANDOFF_READBACK_VERIFICATION_REQUIRED", "Legacy read-back source evidence is incomplete. For compact handoffs omit sourceReviews and omissionsFound so the planner can derive evidence from persisted state.", { missingSources, unknownSources, duplicateSources: [...new Set(duplicateSources)], invalidSources });
|
|
318
|
+
}
|
|
319
|
+
const omissionsFound = uniqueStrings(input.omissionsFound ?? []);
|
|
320
|
+
if (omissionsFound.length > 0) {
|
|
321
|
+
throw new HandoffContractError("HANDOFF_READBACK_GAPS_FOUND", "The persisted handoff is not resume-ready because the read-back found omissions. Run handoff_prepare again, reconcile every listed gap, and rewrite before retrying verification.", { omissionsFound });
|
|
322
|
+
}
|
|
323
|
+
return requiredSources.map((source) => ({ source, detail: bySource.get(source) }));
|
|
324
|
+
}
|
|
325
|
+
function sectionBody(content, headings) {
|
|
326
|
+
const lines = content.split(/\r?\n/);
|
|
327
|
+
const normalized = new Set(headings.map((heading) => heading.toLowerCase()));
|
|
328
|
+
const start = lines.findIndex((line) => {
|
|
329
|
+
const match = line.match(/^##\s+(.+?)\s*$/);
|
|
330
|
+
return Boolean(match && normalized.has(match[1].toLowerCase()));
|
|
331
|
+
});
|
|
332
|
+
if (start < 0)
|
|
333
|
+
return "";
|
|
334
|
+
const body = [];
|
|
335
|
+
for (let index = start + 1; index < lines.length; index += 1) {
|
|
336
|
+
if (/^##\s+/.test(lines[index]))
|
|
337
|
+
break;
|
|
338
|
+
body.push(lines[index]);
|
|
339
|
+
}
|
|
340
|
+
return body.join("\n").trim();
|
|
341
|
+
}
|
|
342
|
+
function boundedSection(value, fallback, maxChars) {
|
|
343
|
+
const normalized = value.trim() || fallback;
|
|
344
|
+
if (normalized.length <= maxChars)
|
|
345
|
+
return normalized;
|
|
346
|
+
return `${normalized.slice(0, Math.max(0, maxChars - 86)).trimEnd()}\n\n[Extended detail continues in the linked planner document.]`;
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Mechanically compact oversized legacy or verbose handoffs while retaining the
|
|
350
|
+
* complete submitted body in one planner-owned Markdown document.
|
|
351
|
+
*/
|
|
352
|
+
export function externalizeOversizedHandoffContent(content, supportingDocumentPath) {
|
|
353
|
+
const base = stripRenderedCompletenessAudit(content);
|
|
354
|
+
if (base.length <= TARGET_HANDOFF_CONTENT_CHARS) {
|
|
355
|
+
return { content: base, externalized: false, extendedContent: "" };
|
|
356
|
+
}
|
|
357
|
+
const firstHeading = base.split(/\r?\n/).find((line) => /^#\s+/.test(line.trim()))?.trim() ?? "# Handoff resume capsule";
|
|
358
|
+
const metadata = ["Created at", "Updated at", "Reason"].map((label) => base.split(/\r?\n/).find((line) => line.toLowerCase().startsWith(`${label.toLowerCase()}:`))?.trim() ?? `${label}: See supporting document.`);
|
|
359
|
+
const currentFocus = boundedSection(sectionBody(base, ["Current focus"]), "Exact focus and resume point are preserved in the supporting document.", 1_200);
|
|
360
|
+
const currentState = boundedSection(sectionBody(base, ["Current and partial state", "What was being done"]), "Current and partial work details are preserved in the supporting document.", 1_500);
|
|
361
|
+
const preservation = boundedSection(sectionBody(base, ["Preservation constraints"]), "Preservation constraints are recorded in the supporting document.", 900);
|
|
362
|
+
const blockers = boundedSection(sectionBody(base, ["Blockers and risks", "Blockers"]), "Blockers and risks are recorded in the supporting document.", 900);
|
|
363
|
+
const resume = boundedSection(sectionBody(base, ["How to resume", "Next steps"]), "1. Read the linked supporting document completely, then resume from its first ordered action.", 1_500);
|
|
364
|
+
const compact = [
|
|
365
|
+
firstHeading,
|
|
366
|
+
"",
|
|
367
|
+
...metadata,
|
|
368
|
+
"",
|
|
369
|
+
"## Current focus",
|
|
370
|
+
currentFocus,
|
|
371
|
+
"",
|
|
372
|
+
"## Current and partial state",
|
|
373
|
+
currentState,
|
|
374
|
+
"",
|
|
375
|
+
"## Preservation constraints",
|
|
376
|
+
preservation,
|
|
377
|
+
"",
|
|
378
|
+
"## Supporting documents",
|
|
379
|
+
`- ${supportingDocumentPath} — Full submitted handoff detail externalized automatically because the inline resume capsule exceeded ${TARGET_HANDOFF_CONTENT_CHARS} characters. Read this document before resuming.`,
|
|
380
|
+
"",
|
|
381
|
+
"## Blockers and risks",
|
|
382
|
+
blockers,
|
|
383
|
+
"",
|
|
384
|
+
"## How to resume",
|
|
385
|
+
resume,
|
|
386
|
+
].join("\n").trim();
|
|
387
|
+
return {
|
|
388
|
+
content: compact,
|
|
389
|
+
externalized: true,
|
|
390
|
+
extendedContent: `${firstHeading} — extended detail\n\n${base}\n`,
|
|
391
|
+
supportingDocument: {
|
|
392
|
+
path: supportingDocumentPath,
|
|
393
|
+
description: "Full submitted handoff detail externalized automatically; required for cold resume and reconciliation.",
|
|
394
|
+
},
|
|
395
|
+
};
|
|
396
|
+
}
|
|
118
397
|
export function renderHandoffCompletenessAudit(audit) {
|
|
119
398
|
const entries = validateHandoffCompletenessAudit(audit);
|
|
120
399
|
const labels = new Map(HANDOFF_COMPLETENESS_CATEGORIES.map((entry) => [entry.id, entry.label]));
|
|
@@ -131,41 +410,58 @@ export function renderHandoffCompletenessAudit(audit) {
|
|
|
131
410
|
HANDOFF_AUDIT_END_MARKER,
|
|
132
411
|
].join("\n").trim();
|
|
133
412
|
}
|
|
134
|
-
export function renderVerifiedHandoffContent(content, audit) {
|
|
413
|
+
export function renderVerifiedHandoffContent(content, audit, coldStartInventory, supportingDocumentContents = []) {
|
|
135
414
|
const base = stripRenderedCompletenessAudit(content);
|
|
136
415
|
validateCanonicalHandoffContent(base);
|
|
137
416
|
validateHandoffCompletenessAudit(audit);
|
|
138
|
-
|
|
139
|
-
if (
|
|
140
|
-
throw new HandoffContractError("HANDOFF_CONTENT_LIMIT_EXCEEDED", `
|
|
417
|
+
validateHandoffColdStartInventory(coldStartInventory, base, supportingDocumentContents);
|
|
418
|
+
if (base.length > MAX_HANDOFF_CONTENT_CHARS) {
|
|
419
|
+
throw new HandoffContractError("HANDOFF_CONTENT_LIMIT_EXCEEDED", `Inline handoff content is ${base.length} characters after compaction; the absolute compatibility ceiling is ${MAX_HANDOFF_CONTENT_CHARS}. Retry through PlanStore so extended detail can be externalized automatically.`, { contentLength: base.length, targetContentChars: TARGET_HANDOFF_CONTENT_CHARS, maxContentChars: MAX_HANDOFF_CONTENT_CHARS, continuation: "externalize-and-retry" });
|
|
141
420
|
}
|
|
142
|
-
return
|
|
421
|
+
return base;
|
|
422
|
+
}
|
|
423
|
+
export function materializeHandoffMetadata(content, reason, createdAt, updatedAt) {
|
|
424
|
+
const normalizedReason = reason.trim();
|
|
425
|
+
if (!normalizedReason) {
|
|
426
|
+
throw new HandoffContractError("HANDOFF_REASON_REQUIRED", "A structured handoff reason is required before drafting or persistence. Run handoff_prepare and provide reason; timestamps are planner-generated.", { requiredInputs: ["title", "reason"], generatedMetadata: ["Created at", "Updated at"] });
|
|
427
|
+
}
|
|
428
|
+
const existingCreatedAt = content.match(/^Created at:\s*(\S+)\s*$/im)?.[1] ?? createdAt;
|
|
429
|
+
const withoutMetadata = content
|
|
430
|
+
.split(/\r?\n/)
|
|
431
|
+
.filter((line) => !/^(?:Created at|Updated at|Reason):\s*/i.test(line.trim()))
|
|
432
|
+
.join("\n")
|
|
433
|
+
.trim();
|
|
434
|
+
const lines = withoutMetadata.split("\n");
|
|
435
|
+
const firstContent = lines.findIndex((line) => line.trim().length > 0);
|
|
436
|
+
const metadata = [`Created at: ${existingCreatedAt}`, `Updated at: ${updatedAt}`, `Reason: ${normalizedReason}`];
|
|
437
|
+
if (firstContent >= 0 && /^#\s+/.test(lines[firstContent] ?? "")) {
|
|
438
|
+
lines.splice(firstContent + 1, 0, "", ...metadata);
|
|
439
|
+
}
|
|
440
|
+
else {
|
|
441
|
+
lines.unshift(...metadata, "");
|
|
442
|
+
}
|
|
443
|
+
return lines.join("\n").trim();
|
|
143
444
|
}
|
|
144
445
|
export function validateCanonicalHandoffContent(content) {
|
|
446
|
+
// A handoff is a resume capsule, not a form. The prepared scaffold documents
|
|
447
|
+
// useful headings, but requiring agents to reproduce those headings creates a
|
|
448
|
+
// late write-time failure and makes them draft the same handoff twice.
|
|
145
449
|
const body = nonEmpty(content, "Handoff content");
|
|
146
|
-
const
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
{ label: "Current focus", pattern: /^##\s+Current focus\s*$/im },
|
|
151
|
-
{ label: "What was being done", pattern: /^##\s+What was being done\s*$/im },
|
|
152
|
-
{ label: "How to resume", pattern: /^##\s+How to resume\s*$/im },
|
|
153
|
-
{ label: "Files touched", pattern: /^##\s+Files touched\s*$/im },
|
|
154
|
-
{ label: "Blockers", pattern: /^##\s+Blockers\s*$/im },
|
|
155
|
-
{ label: "Next steps", pattern: /^##\s+Next steps\s*$/im },
|
|
156
|
-
{ label: "Recent decisions", pattern: /^##\s+Recent decisions\s*$/im },
|
|
157
|
-
];
|
|
158
|
-
const missing = required.filter((entry) => !entry.pattern.test(body)).map((entry) => entry.label);
|
|
159
|
-
if (missing.length > 0)
|
|
160
|
-
throw new Error(`Canonical handoff is missing required sections: ${missing.join(", ")}.`);
|
|
450
|
+
const unresolvedPlaceholders = [...body.matchAll(/\{\{REQUIRED:[^}]+\}\}/g)].map((match) => match[0]);
|
|
451
|
+
if (unresolvedPlaceholders.length > 0) {
|
|
452
|
+
throw new HandoffContractError("HANDOFF_CANONICAL_SECTIONS_REQUIRED", "The handoff still contains unresolved placeholders. Replace every placeholder before writing.", { missingSections: [], unresolvedPlaceholders: [...new Set(unresolvedPlaceholders)] });
|
|
453
|
+
}
|
|
161
454
|
}
|
|
162
455
|
export function validateHandoffContextSync(phase, feature, input) {
|
|
163
|
-
renderVerifiedHandoffContent(input.content, input.completenessAudit);
|
|
164
456
|
const requestedDocuments = input.supportingDocuments ?? [];
|
|
165
457
|
const verifiedDocuments = input.verifiedSupportingDocuments ?? [];
|
|
166
458
|
if (requestedDocuments.length !== verifiedDocuments.length) {
|
|
167
459
|
throw new HandoffContractError("HANDOFF_SUPPORTING_DOCUMENT_INVALID", "Every supporting document must be validated by PlanStore before the handoff is written.", { requestedCount: requestedDocuments.length, verifiedCount: verifiedDocuments.length });
|
|
168
460
|
}
|
|
461
|
+
const verifiedContents = input.verifiedSupportingDocumentContents ?? [];
|
|
462
|
+
if (verifiedContents.length !== verifiedDocuments.length) {
|
|
463
|
+
throw new HandoffContractError("HANDOFF_SUPPORTING_DOCUMENT_INVALID", "Validated supporting-document content is required for cold-start inventory coverage checks.", { verifiedDocumentCount: verifiedDocuments.length, verifiedContentCount: verifiedContents.length });
|
|
464
|
+
}
|
|
169
465
|
for (let index = 0; index < requestedDocuments.length; index += 1) {
|
|
170
466
|
const requested = requestedDocuments[index];
|
|
171
467
|
const verified = verifiedDocuments[index];
|
|
@@ -176,8 +472,9 @@ export function validateHandoffContextSync(phase, feature, input) {
|
|
|
176
472
|
throw new HandoffContractError("HANDOFF_SUPPORTING_DOCUMENT_INVALID", `Canonical handoff content must link supporting document ${requested.path}.`, { index, path: requested.path });
|
|
177
473
|
}
|
|
178
474
|
}
|
|
179
|
-
|
|
180
|
-
|
|
475
|
+
renderVerifiedHandoffContent(input.content, input.completenessAudit, input.coldStartInventory, verifiedContents);
|
|
476
|
+
if (phase.status === "done" || phase.status === "rejected" || phase.status === "canceled") {
|
|
477
|
+
throw new Error(`Cannot write a handoff on ${phase.status} phase ${phase.id}; terminal phases have no pending handoff.`);
|
|
181
478
|
}
|
|
182
479
|
if (phase.handoffUpdatedAt !== input.expectedHandoffUpdatedAt) {
|
|
183
480
|
throw new Error("Handoff changed after preparation. Run handoff_prepare again and reconcile the latest content before writing.");
|
|
@@ -222,8 +519,9 @@ export function validateHandoffContextSync(phase, feature, input) {
|
|
|
222
519
|
}
|
|
223
520
|
export function applyHandoffContextSync(phase, feature, input, timestamp) {
|
|
224
521
|
validateHandoffContextSync(phase, feature, input);
|
|
225
|
-
const handoffContent = renderVerifiedHandoffContent(input.content, input.completenessAudit);
|
|
522
|
+
const handoffContent = renderVerifiedHandoffContent(input.content, input.completenessAudit, input.coldStartInventory, input.verifiedSupportingDocumentContents ?? []);
|
|
226
523
|
const auditEntries = validateHandoffCompletenessAudit(input.completenessAudit);
|
|
524
|
+
const coldStartInventoryEntries = validateHandoffColdStartInventory(input.coldStartInventory, stripRenderedCompletenessAudit(input.content), input.verifiedSupportingDocumentContents ?? []);
|
|
227
525
|
const nextPhase = structuredClone(phase);
|
|
228
526
|
const nextFeature = structuredClone(feature);
|
|
229
527
|
const updatedTaskIds = [];
|
|
@@ -270,10 +568,23 @@ export function applyHandoffContextSync(phase, feature, input, timestamp) {
|
|
|
270
568
|
nextPhase.handoffAudit = {
|
|
271
569
|
version: HANDOFF_COMPLETENESS_AUDIT_VERSION,
|
|
272
570
|
entries: auditEntries,
|
|
571
|
+
coldStartInventory: {
|
|
572
|
+
version: HANDOFF_COLD_START_INVENTORY_VERSION,
|
|
573
|
+
sourceReviews: HANDOFF_COLD_START_SOURCE_REVIEWS.map(({ id }) => {
|
|
574
|
+
const review = input.coldStartInventory?.sourceReviews.find((entry) => entry.source === id);
|
|
575
|
+
return {
|
|
576
|
+
source: id,
|
|
577
|
+
detail: review?.detail.trim() || "Derived from persisted handoff content, planner entities, and read-back state; no duplicate source inventory was supplied.",
|
|
578
|
+
};
|
|
579
|
+
}),
|
|
580
|
+
entries: coldStartInventoryEntries,
|
|
581
|
+
},
|
|
273
582
|
supportingDocuments: input.verifiedSupportingDocuments ?? [],
|
|
274
583
|
contentHash: handoffContentHash(handoffContent),
|
|
275
584
|
contentLength: handoffContent.length,
|
|
276
585
|
verifiedAt: timestamp,
|
|
586
|
+
resumeReadyAt: "",
|
|
587
|
+
readBackSourceReviews: [],
|
|
277
588
|
};
|
|
278
589
|
nextPhase.handoffReadAt = "";
|
|
279
590
|
nextPhase.updatedAt = timestamp;
|
package/dist/index.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ export * from "./recap.js";
|
|
|
6
6
|
export * from "./planner-rules.js";
|
|
7
7
|
export * from "./planner-skill.js";
|
|
8
8
|
export * from "./display-status.js";
|
|
9
|
+
export * from "./description-freshness.js";
|
|
9
10
|
export * from "./task-context.js";
|
|
10
11
|
export * from "./task-selection.js";
|
|
11
12
|
export * from "./task-start-outcome.js";
|
|
@@ -15,7 +16,9 @@ export * from "./handoff-context.js";
|
|
|
15
16
|
export * from "./project-context-migration.js";
|
|
16
17
|
export * from "./requirement-macro-tasks.js";
|
|
17
18
|
export * from "./package-version.js";
|
|
18
|
-
export
|
|
19
|
+
export * from "./runtime-diagnostics.js";
|
|
20
|
+
export * from "./write-coordination.js";
|
|
21
|
+
export { PlanStore, PlanStoreError, PlanStaleWriteError, PlanUnsupportedAllocationKindError, assertPlannerRevision, setWriteBusyHook, setWriteNotifyHook, migrateToUuids, migrateToGlobalSequence, withFeatureLock, type IdeaCreateInput, type IdeaUpdateInput, type IdeaPromotionTargetInput, type AcceptedDecisionOwner, type AcceptedDecisionCreateInput, type AcceptedDecisionUpdateInput, type PhaseHandoffSummary, type OrphanPhaseSummary } from "./plan-store.js";
|
|
19
22
|
export { PlanRenderer } from "./renderer.js";
|
|
20
23
|
export { ExportService } from "./export-service.js";
|
|
21
24
|
export type { CodebaseProfile, ResumeFocus, ActivityEntry, ActivityLog, AmbientFacts } from "./schema.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,yBAAyB,CAAC;AACxC,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC;AACtC,cAAc,sBAAsB,CAAC;AACrC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,sBAAsB,CAAC;AACrC,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,cAAc,EAAE,uBAAuB,EAAE,eAAe,EAAE,KAAK,eAAe,EAAE,KAAK,eAAe,EAAE,KAAK,wBAAwB,EAAE,KAAK,mBAAmB,EAAE,KAAK,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,yBAAyB,CAAC;AACxC,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC;AACtC,cAAc,sBAAsB,CAAC;AACrC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,sBAAsB,CAAC;AACrC,cAAc,0BAA0B,CAAC;AACzC,cAAc,yBAAyB,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,mBAAmB,EAAE,kCAAkC,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,cAAc,EAAE,uBAAuB,EAAE,eAAe,EAAE,KAAK,eAAe,EAAE,KAAK,eAAe,EAAE,KAAK,wBAAwB,EAAE,KAAK,qBAAqB,EAAE,KAAK,2BAA2B,EAAE,KAAK,2BAA2B,EAAE,KAAK,mBAAmB,EAAE,KAAK,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAC1c,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,YAAY,EAAE,eAAe,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -6,6 +6,7 @@ export * from "./recap.js";
|
|
|
6
6
|
export * from "./planner-rules.js";
|
|
7
7
|
export * from "./planner-skill.js";
|
|
8
8
|
export * from "./display-status.js";
|
|
9
|
+
export * from "./description-freshness.js";
|
|
9
10
|
export * from "./task-context.js";
|
|
10
11
|
export * from "./task-selection.js";
|
|
11
12
|
export * from "./task-start-outcome.js";
|
|
@@ -15,6 +16,8 @@ export * from "./handoff-context.js";
|
|
|
15
16
|
export * from "./project-context-migration.js";
|
|
16
17
|
export * from "./requirement-macro-tasks.js";
|
|
17
18
|
export * from "./package-version.js";
|
|
18
|
-
export
|
|
19
|
+
export * from "./runtime-diagnostics.js";
|
|
20
|
+
export * from "./write-coordination.js";
|
|
21
|
+
export { PlanStore, PlanStoreError, PlanStaleWriteError, PlanUnsupportedAllocationKindError, assertPlannerRevision, setWriteBusyHook, setWriteNotifyHook, migrateToUuids, migrateToGlobalSequence, withFeatureLock } from "./plan-store.js";
|
|
19
22
|
export { PlanRenderer } from "./renderer.js";
|
|
20
23
|
export { ExportService } from "./export-service.js";
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export interface RuntimePackageVersion {
|
|
2
2
|
name: string;
|
|
3
3
|
version: string;
|
|
4
|
+
/** Absolute path to the loaded package manifest used as the version source. */
|
|
5
|
+
packageJsonPath?: string;
|
|
4
6
|
}
|
|
5
7
|
/** Find the nearest matching package manifest above a loaded module. */
|
|
6
8
|
export declare function packageVersionFromModule(moduleUrlOrPath: string, expectedName: string): RuntimePackageVersion;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"package-version.d.ts","sourceRoot":"","sources":["../src/package-version.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"package-version.d.ts","sourceRoot":"","sources":["../src/package-version.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,+EAA+E;IAC/E,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAaD,wEAAwE;AACxE,wBAAgB,wBAAwB,CACtC,eAAe,EAAE,MAAM,EACvB,YAAY,EAAE,MAAM,GACnB,qBAAqB,CAqBvB;AAED,oFAAoF;AACpF,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,MAAM,GACpB,qBAAqB,CAGvB"}
|
package/dist/package-version.js
CHANGED
|
@@ -18,7 +18,7 @@ export function packageVersionFromModule(moduleUrlOrPath, expectedName) {
|
|
|
18
18
|
if (typeof manifest.version !== "string" || !manifest.version.trim()) {
|
|
19
19
|
throw new Error(`Package ${expectedName} has no valid version in ${manifestPath}.`);
|
|
20
20
|
}
|
|
21
|
-
return { name: expectedName, version: manifest.version };
|
|
21
|
+
return { name: expectedName, version: manifest.version, packageJsonPath: manifestPath };
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
const parent = dirname(current);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type PlannerPayloadEntity = "project" | "feature" | "phase" | "task";
|
|
1
|
+
export type PlannerPayloadEntity = "project" | "feature" | "phase" | "task" | "requirement" | "idea" | "acceptedDecision";
|
|
2
2
|
export type PlannerPayloadOperation = "update" | "discuss";
|
|
3
3
|
export type PlannerPayloadFailureCode = "NO_MUTABLE_FIELDS_RECEIVED" | "DESCRIPTION_MARKDOWN_FALLBACK_REQUIRED";
|
|
4
4
|
export interface DescriptionFallbackInput {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"payload-fallback.d.ts","sourceRoot":"","sources":["../src/payload-fallback.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,oBAAoB,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"payload-fallback.d.ts","sourceRoot":"","sources":["../src/payload-fallback.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,oBAAoB,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,aAAa,GAAG,MAAM,GAAG,kBAAkB,CAAC;AAC1H,MAAM,MAAM,uBAAuB,GAAG,QAAQ,GAAG,SAAS,CAAC;AAC3D,MAAM,MAAM,yBAAyB,GAAG,4BAA4B,GAAG,wCAAwC,CAAC;AAEhH,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,4BAA4B;IAC3C,MAAM,EAAE,oBAAoB,CAAC;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,uBAAuB,CAAC;IACnC,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mBAAmB,CAAC,EAAE,wBAAwB,CAAC;CAChD;AAED,MAAM,WAAW,6BAA6B;IAC5C,OAAO,EAAE,KAAK,CAAC;IACf,SAAS,CAAC,EAAE,KAAK,CAAC;IAClB,MAAM,EAAE,mBAAmB,CAAC;IAC5B,SAAS,EAAE,yBAAyB,CAAC;IACrC,4BAA4B,EAAE,OAAO,CAAC;IACtC,MAAM,EAAE,oBAAoB,CAAC;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,uBAAuB,CAAC;IACnC,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,2BAA2B,CAAC,EAAE,MAAM,CAAC;IACrC,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAGrF;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAO9D;AAED,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,oBAAoB,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAiBnG;AAED,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,4BAA4B,GAClC,6BAA6B,CAgD/B"}
|
package/dist/payload-fallback.js
CHANGED
|
@@ -24,6 +24,12 @@ export function suggestedDescriptionRefPath(entity, entityId) {
|
|
|
24
24
|
return `.planner/docs/phases/${entityId ?? "phase"}.md`;
|
|
25
25
|
case "task":
|
|
26
26
|
return `.planner/docs/tasks/${entityId ?? "task"}.md`;
|
|
27
|
+
case "requirement":
|
|
28
|
+
return `.planner/docs/requirements/${entityId ?? "requirement"}.md`;
|
|
29
|
+
case "idea":
|
|
30
|
+
return `.planner/docs/ideas/${entityId ?? "idea"}.md`;
|
|
31
|
+
case "acceptedDecision":
|
|
32
|
+
return `.planner/docs/accepted-decisions/${entityId ?? "accepted-decision"}.md`;
|
|
27
33
|
}
|
|
28
34
|
}
|
|
29
35
|
export function noMutableFieldsReceived(input) {
|