@agent-plan/core 0.2.26 → 0.2.27
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 +134 -3
- package/dist/handoff-context.d.ts.map +1 -1
- package/dist/handoff-context.js +334 -16
- 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 +685 -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,43 @@ 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
|
+
];
|
|
61
|
+
const LEGACY_HANDOFF_CANONICAL_SECTIONS = [
|
|
62
|
+
"Created at", "Updated at", "Reason", "Current focus", "What was being done",
|
|
63
|
+
"Working tree and ownership", "Work not started or intentionally untouched", "Runtime wiring",
|
|
64
|
+
"Preservation constraints", "Verification evidence", "Related planned work", "How to resume",
|
|
65
|
+
"Files touched", "Blockers", "Next steps", "Recent decisions",
|
|
66
|
+
];
|
|
27
67
|
export class HandoffContractError extends Error {
|
|
28
68
|
code;
|
|
29
69
|
details;
|
|
@@ -41,6 +81,37 @@ export function hasTaskCompletionEvidence(task) {
|
|
|
41
81
|
return true;
|
|
42
82
|
return task.statusLog.some((entry) => entry.toStatus === "done" && entry.description.trim().length > 0);
|
|
43
83
|
}
|
|
84
|
+
export function buildHandoffDraftTemplate(phase, feature) {
|
|
85
|
+
const featureRef = `F${String(feature.number).padStart(3, "0")}`;
|
|
86
|
+
const phaseRef = `P${String(phase.number).padStart(3, "0")}(${featureRef})`;
|
|
87
|
+
return [
|
|
88
|
+
`# ${phaseRef} — {{REQUIRED: meaningful handoff title}}`,
|
|
89
|
+
"",
|
|
90
|
+
"<!-- Planner-generated: Created at, Updated at, and structured Reason. Do not write these lines in the draft. -->",
|
|
91
|
+
"",
|
|
92
|
+
"## Current focus",
|
|
93
|
+
`- Feature: ${featureRef} — ${feature.name}`,
|
|
94
|
+
`- Phase: ${phaseRef} — ${phase.title}`,
|
|
95
|
+
"- Task: {{REQUIRED: exact composite task ref and title}}",
|
|
96
|
+
"- Exact resume point: {{REQUIRED: file, symbol, command, or state boundary}}",
|
|
97
|
+
"",
|
|
98
|
+
"## Current and partial state",
|
|
99
|
+
"{{REQUIRED: concise completed/current/partial state only; keep extended detail in supporting documents}}",
|
|
100
|
+
"",
|
|
101
|
+
"## Preservation constraints",
|
|
102
|
+
"{{REQUIRED: minimal behaviors and boundaries that must survive}}",
|
|
103
|
+
"",
|
|
104
|
+
"## Supporting documents",
|
|
105
|
+
"- {{REQUIRED: ordered .planner/docs/*.md links with why each is needed, or an explicit verified statement that none are needed}}",
|
|
106
|
+
"",
|
|
107
|
+
"## Blockers and risks",
|
|
108
|
+
"- {{REQUIRED: blocker/risk, or a substantive verified statement that none apply}}",
|
|
109
|
+
"",
|
|
110
|
+
"## How to resume",
|
|
111
|
+
"1. {{REQUIRED: first exact action, including file/symbol/command}}",
|
|
112
|
+
"2. {{REQUIRED: subsequent ordered actions and verification}}",
|
|
113
|
+
].join("\n");
|
|
114
|
+
}
|
|
44
115
|
export function auditPhaseHandoff(phase, feature) {
|
|
45
116
|
const missingCompletionTasks = phase.tasks
|
|
46
117
|
.filter((task) => task.status === "done" && !hasTaskCompletionEvidence(task))
|
|
@@ -53,9 +124,20 @@ export function auditPhaseHandoff(phase, feature) {
|
|
|
53
124
|
missingCompletionTaskIds: missingCompletionTasks.map((task) => task.id),
|
|
54
125
|
missingCompletionTasks,
|
|
55
126
|
completenessVersion: HANDOFF_COMPLETENESS_AUDIT_VERSION,
|
|
127
|
+
targetContentChars: TARGET_HANDOFF_CONTENT_CHARS,
|
|
56
128
|
maxContentChars: MAX_HANDOFF_CONTENT_CHARS,
|
|
57
129
|
completenessCategories: HANDOFF_COMPLETENESS_CATEGORIES,
|
|
130
|
+
canonicalSections: HANDOFF_CANONICAL_SECTIONS,
|
|
131
|
+
requiredHumanInputs: [
|
|
132
|
+
{ id: "title", label: "Meaningful title", description: "A concise handoff title; the planner renders it as the H1." },
|
|
133
|
+
{ id: "reason", label: "Reason", description: "Why work is stopping and why a cold agent needs this handoff; the planner renders it as metadata." },
|
|
134
|
+
],
|
|
135
|
+
draftTemplate: buildHandoffDraftTemplate(phase, feature),
|
|
136
|
+
coldStartInventoryVersion: HANDOFF_COLD_START_INVENTORY_VERSION,
|
|
137
|
+
coldStartSourceReviews: HANDOFF_COLD_START_SOURCE_REVIEWS,
|
|
138
|
+
coldStartInventoryCategories: HANDOFF_COLD_START_INVENTORY_CATEGORIES,
|
|
58
139
|
existingCompletenessAudit: phase.handoffAudit,
|
|
140
|
+
phaseWorkMap: buildPhaseWorkMap(phase, feature.number),
|
|
59
141
|
};
|
|
60
142
|
}
|
|
61
143
|
function nonEmpty(value, field) {
|
|
@@ -115,6 +197,189 @@ export function validateHandoffCompletenessAudit(audit) {
|
|
|
115
197
|
}
|
|
116
198
|
return expectedIds.map((id) => byCategory.get(id));
|
|
117
199
|
}
|
|
200
|
+
export function validateHandoffColdStartInventory(inventory, content, supportingDocumentContents = []) {
|
|
201
|
+
const expectedIds = HANDOFF_COLD_START_INVENTORY_CATEGORIES.map((entry) => entry.id);
|
|
202
|
+
if (!inventory || inventory.version !== HANDOFF_COLD_START_INVENTORY_VERSION) {
|
|
203
|
+
throw new HandoffContractError("HANDOFF_COLD_START_INVENTORY_REQUIRED", `Cold-start inventory version ${HANDOFF_COLD_START_INVENTORY_VERSION} is required before handoff persistence. Populate it from the scaffold returned by handoff_prepare.`, { requiredVersion: HANDOFF_COLD_START_INVENTORY_VERSION, missingCategories: expectedIds });
|
|
204
|
+
}
|
|
205
|
+
const requiredSources = HANDOFF_COLD_START_SOURCE_REVIEWS.map((entry) => entry.id);
|
|
206
|
+
const requiredSourceIds = new Set(requiredSources);
|
|
207
|
+
const sourceReviews = new Map();
|
|
208
|
+
const duplicateSources = [];
|
|
209
|
+
for (const review of inventory.sourceReviews) {
|
|
210
|
+
if (sourceReviews.has(review.source))
|
|
211
|
+
duplicateSources.push(review.source);
|
|
212
|
+
else
|
|
213
|
+
sourceReviews.set(review.source, review.detail);
|
|
214
|
+
}
|
|
215
|
+
const missingSources = requiredSources.filter((source) => !sourceReviews.has(source));
|
|
216
|
+
const unknownSources = [...sourceReviews.keys()].filter((source) => !requiredSourceIds.has(source));
|
|
217
|
+
const invalidSources = requiredSources.filter((source) => {
|
|
218
|
+
const detail = sourceReviews.get(source);
|
|
219
|
+
return detail !== undefined && !isSubstantive(detail);
|
|
220
|
+
});
|
|
221
|
+
const byCategory = new Map();
|
|
222
|
+
const duplicates = [];
|
|
223
|
+
for (const entry of inventory.entries) {
|
|
224
|
+
if (byCategory.has(entry.category))
|
|
225
|
+
duplicates.push(entry.category);
|
|
226
|
+
else
|
|
227
|
+
byCategory.set(entry.category, entry);
|
|
228
|
+
}
|
|
229
|
+
const missingCategories = expectedIds.filter((id) => !byCategory.has(id));
|
|
230
|
+
const unknownCategories = [...byCategory.keys()].filter((id) => !expectedIds.includes(id));
|
|
231
|
+
const invalidCategories = [];
|
|
232
|
+
for (const id of expectedIds) {
|
|
233
|
+
const entry = byCategory.get(id);
|
|
234
|
+
if (!entry)
|
|
235
|
+
continue;
|
|
236
|
+
const items = uniqueStrings(entry.items);
|
|
237
|
+
const notApplicableReason = entry.notApplicableReason?.trim() ?? "";
|
|
238
|
+
if (items.length === 0)
|
|
239
|
+
invalidCategories.push(id);
|
|
240
|
+
if (notApplicableReason && !isSubstantive(notApplicableReason))
|
|
241
|
+
invalidCategories.push(id);
|
|
242
|
+
if (items.some((item) => /^(?:n\/?a|none|nothing|unknown|tbd|see above)$/i.test(item.trim())))
|
|
243
|
+
invalidCategories.push(id);
|
|
244
|
+
}
|
|
245
|
+
if (missingSources.length > 0 || unknownSources.length > 0 || duplicateSources.length > 0 || invalidSources.length > 0
|
|
246
|
+
|| missingCategories.length > 0 || unknownCategories.length > 0 || duplicates.length > 0 || invalidCategories.length > 0) {
|
|
247
|
+
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').", {
|
|
248
|
+
missingSources,
|
|
249
|
+
invalidSources,
|
|
250
|
+
unknownSources,
|
|
251
|
+
duplicateSources: [...new Set(duplicateSources)],
|
|
252
|
+
missingCategories,
|
|
253
|
+
invalidCategories: [...new Set(invalidCategories)],
|
|
254
|
+
unknownCategories,
|
|
255
|
+
duplicateCategories: [...new Set(duplicates)],
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
const normalize = (value) => value.normalize("NFKC").replace(/[`*_]/g, "").replace(/\s+/g, " ").toLowerCase();
|
|
259
|
+
const corpus = normalize([content, ...supportingDocumentContents].join("\n"));
|
|
260
|
+
const uncoveredItems = expectedIds.flatMap((id) => {
|
|
261
|
+
const entry = byCategory.get(id);
|
|
262
|
+
return uniqueStrings(entry.items)
|
|
263
|
+
.filter((item) => !corpus.includes(normalize(item)))
|
|
264
|
+
.map((item) => ({ category: id, item }));
|
|
265
|
+
});
|
|
266
|
+
if (uncoveredItems.length > 0) {
|
|
267
|
+
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 });
|
|
268
|
+
}
|
|
269
|
+
return expectedIds.map((id) => {
|
|
270
|
+
const entry = byCategory.get(id);
|
|
271
|
+
return {
|
|
272
|
+
category: entry.category,
|
|
273
|
+
items: uniqueStrings(entry.items),
|
|
274
|
+
...(entry.notApplicableReason?.trim() ? { notApplicableReason: entry.notApplicableReason.trim() } : {}),
|
|
275
|
+
};
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
export function validateHandoffReadBackVerification(phase, input) {
|
|
279
|
+
const audit = phase.handoffAudit;
|
|
280
|
+
const actualContentHash = phase.handoff.trim() ? handoffContentHash(phase.handoff) : "";
|
|
281
|
+
if (!phase.handoff.trim() || !audit || audit.contentHash !== actualContentHash || audit.contentLength !== phase.handoff.length) {
|
|
282
|
+
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 ?? "" });
|
|
283
|
+
}
|
|
284
|
+
if (!input.expectedContentHash.trim() || input.expectedContentHash !== actualContentHash) {
|
|
285
|
+
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 });
|
|
286
|
+
}
|
|
287
|
+
const requiredSources = HANDOFF_COLD_START_SOURCE_REVIEWS.map((entry) => entry.id);
|
|
288
|
+
const bySource = new Map();
|
|
289
|
+
const duplicateSources = [];
|
|
290
|
+
for (const review of input.sourceReviews) {
|
|
291
|
+
if (bySource.has(review.source))
|
|
292
|
+
duplicateSources.push(review.source);
|
|
293
|
+
else
|
|
294
|
+
bySource.set(review.source, review.detail.trim());
|
|
295
|
+
}
|
|
296
|
+
const missingSources = requiredSources.filter((source) => !bySource.has(source));
|
|
297
|
+
const unknownSources = [...bySource.keys()].filter((source) => !requiredSources.includes(source));
|
|
298
|
+
const invalidSources = requiredSources.filter((source) => {
|
|
299
|
+
const detail = bySource.get(source);
|
|
300
|
+
return detail !== undefined && !isSubstantive(detail);
|
|
301
|
+
});
|
|
302
|
+
if (missingSources.length > 0 || unknownSources.length > 0 || duplicateSources.length > 0 || invalidSources.length > 0) {
|
|
303
|
+
throw new HandoffContractError("HANDOFF_READBACK_VERIFICATION_REQUIRED", "Read-back verification must re-check every required source against the persisted handoff with substantive findings.", { missingSources, unknownSources, duplicateSources: [...new Set(duplicateSources)], invalidSources });
|
|
304
|
+
}
|
|
305
|
+
const omissionsFound = uniqueStrings(input.omissionsFound);
|
|
306
|
+
if (omissionsFound.length > 0) {
|
|
307
|
+
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 });
|
|
308
|
+
}
|
|
309
|
+
return requiredSources.map((source) => ({ source, detail: bySource.get(source) }));
|
|
310
|
+
}
|
|
311
|
+
function sectionBody(content, headings) {
|
|
312
|
+
const lines = content.split(/\r?\n/);
|
|
313
|
+
const normalized = new Set(headings.map((heading) => heading.toLowerCase()));
|
|
314
|
+
const start = lines.findIndex((line) => {
|
|
315
|
+
const match = line.match(/^##\s+(.+?)\s*$/);
|
|
316
|
+
return Boolean(match && normalized.has(match[1].toLowerCase()));
|
|
317
|
+
});
|
|
318
|
+
if (start < 0)
|
|
319
|
+
return "";
|
|
320
|
+
const body = [];
|
|
321
|
+
for (let index = start + 1; index < lines.length; index += 1) {
|
|
322
|
+
if (/^##\s+/.test(lines[index]))
|
|
323
|
+
break;
|
|
324
|
+
body.push(lines[index]);
|
|
325
|
+
}
|
|
326
|
+
return body.join("\n").trim();
|
|
327
|
+
}
|
|
328
|
+
function boundedSection(value, fallback, maxChars) {
|
|
329
|
+
const normalized = value.trim() || fallback;
|
|
330
|
+
if (normalized.length <= maxChars)
|
|
331
|
+
return normalized;
|
|
332
|
+
return `${normalized.slice(0, Math.max(0, maxChars - 86)).trimEnd()}\n\n[Extended detail continues in the linked planner document.]`;
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Mechanically compact oversized legacy or verbose handoffs while retaining the
|
|
336
|
+
* complete submitted body in one planner-owned Markdown document.
|
|
337
|
+
*/
|
|
338
|
+
export function externalizeOversizedHandoffContent(content, supportingDocumentPath) {
|
|
339
|
+
const base = stripRenderedCompletenessAudit(content);
|
|
340
|
+
if (base.length <= TARGET_HANDOFF_CONTENT_CHARS) {
|
|
341
|
+
return { content: base, externalized: false, extendedContent: "" };
|
|
342
|
+
}
|
|
343
|
+
const firstHeading = base.split(/\r?\n/).find((line) => /^#\s+/.test(line.trim()))?.trim() ?? "# Handoff resume capsule";
|
|
344
|
+
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.`);
|
|
345
|
+
const currentFocus = boundedSection(sectionBody(base, ["Current focus"]), "Exact focus and resume point are preserved in the supporting document.", 1_200);
|
|
346
|
+
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);
|
|
347
|
+
const preservation = boundedSection(sectionBody(base, ["Preservation constraints"]), "Preservation constraints are recorded in the supporting document.", 900);
|
|
348
|
+
const blockers = boundedSection(sectionBody(base, ["Blockers and risks", "Blockers"]), "Blockers and risks are recorded in the supporting document.", 900);
|
|
349
|
+
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);
|
|
350
|
+
const compact = [
|
|
351
|
+
firstHeading,
|
|
352
|
+
"",
|
|
353
|
+
...metadata,
|
|
354
|
+
"",
|
|
355
|
+
"## Current focus",
|
|
356
|
+
currentFocus,
|
|
357
|
+
"",
|
|
358
|
+
"## Current and partial state",
|
|
359
|
+
currentState,
|
|
360
|
+
"",
|
|
361
|
+
"## Preservation constraints",
|
|
362
|
+
preservation,
|
|
363
|
+
"",
|
|
364
|
+
"## Supporting documents",
|
|
365
|
+
`- ${supportingDocumentPath} — Full submitted handoff detail externalized automatically because the inline resume capsule exceeded ${TARGET_HANDOFF_CONTENT_CHARS} characters. Read this document before resuming.`,
|
|
366
|
+
"",
|
|
367
|
+
"## Blockers and risks",
|
|
368
|
+
blockers,
|
|
369
|
+
"",
|
|
370
|
+
"## How to resume",
|
|
371
|
+
resume,
|
|
372
|
+
].join("\n").trim();
|
|
373
|
+
return {
|
|
374
|
+
content: compact,
|
|
375
|
+
externalized: true,
|
|
376
|
+
extendedContent: `${firstHeading} — extended detail\n\n${base}\n`,
|
|
377
|
+
supportingDocument: {
|
|
378
|
+
path: supportingDocumentPath,
|
|
379
|
+
description: "Full submitted handoff detail externalized automatically; required for cold resume and reconciliation.",
|
|
380
|
+
},
|
|
381
|
+
};
|
|
382
|
+
}
|
|
118
383
|
export function renderHandoffCompletenessAudit(audit) {
|
|
119
384
|
const entries = validateHandoffCompletenessAudit(audit);
|
|
120
385
|
const labels = new Map(HANDOFF_COMPLETENESS_CATEGORIES.map((entry) => [entry.id, entry.label]));
|
|
@@ -131,15 +396,37 @@ export function renderHandoffCompletenessAudit(audit) {
|
|
|
131
396
|
HANDOFF_AUDIT_END_MARKER,
|
|
132
397
|
].join("\n").trim();
|
|
133
398
|
}
|
|
134
|
-
export function renderVerifiedHandoffContent(content, audit) {
|
|
399
|
+
export function renderVerifiedHandoffContent(content, audit, coldStartInventory, supportingDocumentContents = []) {
|
|
135
400
|
const base = stripRenderedCompletenessAudit(content);
|
|
136
401
|
validateCanonicalHandoffContent(base);
|
|
137
402
|
validateHandoffCompletenessAudit(audit);
|
|
138
|
-
|
|
139
|
-
if (
|
|
140
|
-
throw new HandoffContractError("HANDOFF_CONTENT_LIMIT_EXCEEDED", `
|
|
403
|
+
validateHandoffColdStartInventory(coldStartInventory, base, supportingDocumentContents);
|
|
404
|
+
if (base.length > MAX_HANDOFF_CONTENT_CHARS) {
|
|
405
|
+
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" });
|
|
406
|
+
}
|
|
407
|
+
return base;
|
|
408
|
+
}
|
|
409
|
+
export function materializeHandoffMetadata(content, reason, createdAt, updatedAt) {
|
|
410
|
+
const normalizedReason = reason.trim();
|
|
411
|
+
if (!normalizedReason) {
|
|
412
|
+
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"] });
|
|
413
|
+
}
|
|
414
|
+
const existingCreatedAt = content.match(/^Created at:\s*(\S+)\s*$/im)?.[1] ?? createdAt;
|
|
415
|
+
const withoutMetadata = content
|
|
416
|
+
.split(/\r?\n/)
|
|
417
|
+
.filter((line) => !/^(?:Created at|Updated at|Reason):\s*/i.test(line.trim()))
|
|
418
|
+
.join("\n")
|
|
419
|
+
.trim();
|
|
420
|
+
const lines = withoutMetadata.split("\n");
|
|
421
|
+
const firstContent = lines.findIndex((line) => line.trim().length > 0);
|
|
422
|
+
const metadata = [`Created at: ${existingCreatedAt}`, `Updated at: ${updatedAt}`, `Reason: ${normalizedReason}`];
|
|
423
|
+
if (firstContent >= 0 && /^#\s+/.test(lines[firstContent] ?? "")) {
|
|
424
|
+
lines.splice(firstContent + 1, 0, "", ...metadata);
|
|
141
425
|
}
|
|
142
|
-
|
|
426
|
+
else {
|
|
427
|
+
lines.unshift(...metadata, "");
|
|
428
|
+
}
|
|
429
|
+
return lines.join("\n").trim();
|
|
143
430
|
}
|
|
144
431
|
export function validateCanonicalHandoffContent(content) {
|
|
145
432
|
const body = nonEmpty(content, "Handoff content");
|
|
@@ -148,24 +435,43 @@ export function validateCanonicalHandoffContent(content) {
|
|
|
148
435
|
{ label: "Updated at", pattern: /^Updated at:\s*\S+/im },
|
|
149
436
|
{ label: "Reason", pattern: /^Reason:\s*\S+/im },
|
|
150
437
|
{ label: "Current focus", pattern: /^##\s+Current focus\s*$/im },
|
|
151
|
-
{ label: "
|
|
438
|
+
{ label: "Current and partial state", pattern: /^##\s+Current and partial state\s*$/im },
|
|
439
|
+
{ label: "Preservation constraints", pattern: /^##\s+Preservation constraints\s*$/im },
|
|
440
|
+
{ label: "Supporting documents", pattern: /^##\s+Supporting documents\s*$/im },
|
|
441
|
+
{ label: "Blockers and risks", pattern: /^##\s+Blockers and risks\s*$/im },
|
|
152
442
|
{ 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
443
|
];
|
|
158
444
|
const missing = required.filter((entry) => !entry.pattern.test(body)).map((entry) => entry.label);
|
|
159
|
-
if (missing.length
|
|
160
|
-
|
|
445
|
+
if (missing.length === 0) {
|
|
446
|
+
const unresolvedPlaceholders = [...body.matchAll(/\{\{REQUIRED:[^}]+\}\}/g)].map((match) => match[0]);
|
|
447
|
+
if (unresolvedPlaceholders.length > 0) {
|
|
448
|
+
throw new HandoffContractError("HANDOFF_CANONICAL_SECTIONS_REQUIRED", "The canonical handoff still contains unresolved placeholders. Replace every placeholder before writing.", { missingSections: [], unresolvedPlaceholders: [...new Set(unresolvedPlaceholders)] });
|
|
449
|
+
}
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
const legacyMissing = LEGACY_HANDOFF_CANONICAL_SECTIONS.filter((label) => {
|
|
453
|
+
if (label === "Created at")
|
|
454
|
+
return !/^Created at:\s*\S+/im.test(body);
|
|
455
|
+
if (label === "Updated at")
|
|
456
|
+
return !/^Updated at:\s*\S+/im.test(body);
|
|
457
|
+
if (label === "Reason")
|
|
458
|
+
return !/^Reason:\s*\S+/im.test(body);
|
|
459
|
+
return !new RegExp(`^##\\s+${label.replace(/[.*+?^${}()|[\\]\\]/g, "\\$&")}\\s*$`, "im").test(body);
|
|
460
|
+
});
|
|
461
|
+
if (legacyMissing.length > 0) {
|
|
462
|
+
throw new HandoffContractError("HANDOFF_CANONICAL_SECTIONS_REQUIRED", "The canonical handoff does not match the prepared scaffold. Fill every required heading and replace every placeholder before writing.", { missingSections: missing, legacyMissingSections: legacyMissing, unresolvedPlaceholders: [...new Set([...body.matchAll(/\{\{REQUIRED:[^}]+\}\}/g)].map((m) => m[0]))] });
|
|
463
|
+
}
|
|
161
464
|
}
|
|
162
465
|
export function validateHandoffContextSync(phase, feature, input) {
|
|
163
|
-
renderVerifiedHandoffContent(input.content, input.completenessAudit);
|
|
164
466
|
const requestedDocuments = input.supportingDocuments ?? [];
|
|
165
467
|
const verifiedDocuments = input.verifiedSupportingDocuments ?? [];
|
|
166
468
|
if (requestedDocuments.length !== verifiedDocuments.length) {
|
|
167
469
|
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
470
|
}
|
|
471
|
+
const verifiedContents = input.verifiedSupportingDocumentContents ?? [];
|
|
472
|
+
if (verifiedContents.length !== verifiedDocuments.length) {
|
|
473
|
+
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 });
|
|
474
|
+
}
|
|
169
475
|
for (let index = 0; index < requestedDocuments.length; index += 1) {
|
|
170
476
|
const requested = requestedDocuments[index];
|
|
171
477
|
const verified = verifiedDocuments[index];
|
|
@@ -176,8 +482,9 @@ export function validateHandoffContextSync(phase, feature, input) {
|
|
|
176
482
|
throw new HandoffContractError("HANDOFF_SUPPORTING_DOCUMENT_INVALID", `Canonical handoff content must link supporting document ${requested.path}.`, { index, path: requested.path });
|
|
177
483
|
}
|
|
178
484
|
}
|
|
179
|
-
|
|
180
|
-
|
|
485
|
+
renderVerifiedHandoffContent(input.content, input.completenessAudit, input.coldStartInventory, verifiedContents);
|
|
486
|
+
if (phase.status === "done" || phase.status === "rejected" || phase.status === "canceled") {
|
|
487
|
+
throw new Error(`Cannot write a handoff on ${phase.status} phase ${phase.id}; terminal phases have no pending handoff.`);
|
|
181
488
|
}
|
|
182
489
|
if (phase.handoffUpdatedAt !== input.expectedHandoffUpdatedAt) {
|
|
183
490
|
throw new Error("Handoff changed after preparation. Run handoff_prepare again and reconcile the latest content before writing.");
|
|
@@ -222,8 +529,9 @@ export function validateHandoffContextSync(phase, feature, input) {
|
|
|
222
529
|
}
|
|
223
530
|
export function applyHandoffContextSync(phase, feature, input, timestamp) {
|
|
224
531
|
validateHandoffContextSync(phase, feature, input);
|
|
225
|
-
const handoffContent = renderVerifiedHandoffContent(input.content, input.completenessAudit);
|
|
532
|
+
const handoffContent = renderVerifiedHandoffContent(input.content, input.completenessAudit, input.coldStartInventory, input.verifiedSupportingDocumentContents ?? []);
|
|
226
533
|
const auditEntries = validateHandoffCompletenessAudit(input.completenessAudit);
|
|
534
|
+
const coldStartInventoryEntries = validateHandoffColdStartInventory(input.coldStartInventory, stripRenderedCompletenessAudit(input.content), input.verifiedSupportingDocumentContents ?? []);
|
|
227
535
|
const nextPhase = structuredClone(phase);
|
|
228
536
|
const nextFeature = structuredClone(feature);
|
|
229
537
|
const updatedTaskIds = [];
|
|
@@ -270,10 +578,20 @@ export function applyHandoffContextSync(phase, feature, input, timestamp) {
|
|
|
270
578
|
nextPhase.handoffAudit = {
|
|
271
579
|
version: HANDOFF_COMPLETENESS_AUDIT_VERSION,
|
|
272
580
|
entries: auditEntries,
|
|
581
|
+
coldStartInventory: {
|
|
582
|
+
version: HANDOFF_COLD_START_INVENTORY_VERSION,
|
|
583
|
+
sourceReviews: HANDOFF_COLD_START_SOURCE_REVIEWS.map(({ id }) => {
|
|
584
|
+
const review = input.coldStartInventory.sourceReviews.find((entry) => entry.source === id);
|
|
585
|
+
return { source: id, detail: review.detail.trim() };
|
|
586
|
+
}),
|
|
587
|
+
entries: coldStartInventoryEntries,
|
|
588
|
+
},
|
|
273
589
|
supportingDocuments: input.verifiedSupportingDocuments ?? [],
|
|
274
590
|
contentHash: handoffContentHash(handoffContent),
|
|
275
591
|
contentLength: handoffContent.length,
|
|
276
592
|
verifiedAt: timestamp,
|
|
593
|
+
resumeReadyAt: "",
|
|
594
|
+
readBackSourceReviews: [],
|
|
277
595
|
};
|
|
278
596
|
nextPhase.handoffReadAt = "";
|
|
279
597
|
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) {
|