@agent-plan/core 0.2.25 → 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/display-status.d.ts +3 -3
- package/dist/display-status.d.ts.map +1 -1
- package/dist/display-status.js +5 -4
- package/dist/handoff-context.d.ts +222 -1
- package/dist/handoff-context.d.ts.map +1 -1
- package/dist/handoff-context.js +461 -11
- package/dist/index.d.ts +8 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -1
- package/dist/naming.d.ts +3 -0
- package/dist/naming.d.ts.map +1 -1
- package/dist/naming.js +7 -0
- 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 +38 -0
- package/dist/payload-fallback.d.ts.map +1 -0
- package/dist/payload-fallback.js +79 -0
- package/dist/plan-store.d.ts +192 -36
- package/dist/plan-store.d.ts.map +1 -1
- package/dist/plan-store.js +1048 -126
- package/dist/planner-rules.d.ts.map +1 -1
- package/dist/planner-rules.js +9 -3
- package/dist/planner-skill.d.ts +24 -0
- package/dist/planner-skill.d.ts.map +1 -0
- package/dist/planner-skill.js +113 -0
- package/dist/project-context-migration.d.ts +47 -0
- package/dist/project-context-migration.d.ts.map +1 -0
- package/dist/project-context-migration.js +168 -0
- package/dist/read-tracking.d.ts +47 -13
- package/dist/read-tracking.d.ts.map +1 -1
- package/dist/read-tracking.js +88 -33
- package/dist/recap.d.ts.map +1 -1
- package/dist/recap.js +34 -9
- package/dist/refs.d.ts +6 -1
- package/dist/refs.d.ts.map +1 -1
- package/dist/refs.js +25 -0
- package/dist/renderer.d.ts.map +1 -1
- package/dist/renderer.js +24 -2
- package/dist/requirement-macro-tasks.d.ts +18 -0
- package/dist/requirement-macro-tasks.d.ts.map +1 -0
- package/dist/requirement-macro-tasks.js +55 -0
- 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 +1575 -290
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +89 -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/task-start-outcome.d.ts +1 -1
- package/dist/task-start-outcome.d.ts.map +1 -1
- package/dist/task-start-outcome.js +1 -0
- 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 +3 -1
- package/planner-skill.md +226 -0
- package/skills/grill-me/SKILL.md +10 -0
package/dist/handoff-context.js
CHANGED
|
@@ -1,4 +1,79 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { buildPhaseWorkMap } from "./task-context.js";
|
|
1
3
|
export const COMPLETION_SUMMARY_HEADING = "**Completion summary:**";
|
|
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;
|
|
7
|
+
export const MAX_HANDOFF_CONTENT_CHARS = 24_000;
|
|
8
|
+
export const HANDOFF_AUDIT_START_MARKER = "<!-- agent-plan:handoff-audit:start -->";
|
|
9
|
+
export const HANDOFF_AUDIT_END_MARKER = "<!-- agent-plan:handoff-audit:end -->";
|
|
10
|
+
export const HANDOFF_COMPLETENESS_CATEGORIES = [
|
|
11
|
+
{ id: "exact-focus-resume-point", label: "Exact focus and resume point" },
|
|
12
|
+
{ id: "first-resume-action", label: "First resume action" },
|
|
13
|
+
{ id: "completed-work", label: "Completed work" },
|
|
14
|
+
{ id: "partial-work", label: "Partial work" },
|
|
15
|
+
{ id: "remaining-work", label: "Remaining work" },
|
|
16
|
+
{ id: "decisions-rationale", label: "Decisions and rationale" },
|
|
17
|
+
{ id: "rejected-alternatives", label: "Rejected alternatives" },
|
|
18
|
+
{ id: "files-symbols", label: "Files and symbols" },
|
|
19
|
+
{ id: "branch-worktree", label: "Branch and worktree" },
|
|
20
|
+
{ id: "commands-tools", label: "Commands and tools" },
|
|
21
|
+
{ id: "completed-verification", label: "Completed verification" },
|
|
22
|
+
{ id: "pending-verification", label: "Pending verification" },
|
|
23
|
+
{ id: "runtime-limitations-workarounds", label: "Runtime limitations and workarounds" },
|
|
24
|
+
{ id: "blockers-risks", label: "Blockers and risks" },
|
|
25
|
+
{ id: "user-visible-behavior", label: "User-visible behavior" },
|
|
26
|
+
{ id: "operator-actions", label: "Operator actions" },
|
|
27
|
+
{ id: "project-operating-notes", label: "Project-specific operating notes" },
|
|
28
|
+
{ id: "conversation-only-facts", label: "Conversation-only facts" },
|
|
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
|
+
];
|
|
67
|
+
export class HandoffContractError extends Error {
|
|
68
|
+
code;
|
|
69
|
+
details;
|
|
70
|
+
constructor(code, message, details = {}) {
|
|
71
|
+
super(message);
|
|
72
|
+
this.name = "HandoffContractError";
|
|
73
|
+
this.code = code;
|
|
74
|
+
this.details = details;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
2
77
|
export function hasTaskCompletionEvidence(task) {
|
|
3
78
|
if (task.status !== "done")
|
|
4
79
|
return true;
|
|
@@ -6,6 +81,37 @@ export function hasTaskCompletionEvidence(task) {
|
|
|
6
81
|
return true;
|
|
7
82
|
return task.statusLog.some((entry) => entry.toStatus === "done" && entry.description.trim().length > 0);
|
|
8
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
|
+
}
|
|
9
115
|
export function auditPhaseHandoff(phase, feature) {
|
|
10
116
|
const missingCompletionTasks = phase.tasks
|
|
11
117
|
.filter((task) => task.status === "done" && !hasTaskCompletionEvidence(task))
|
|
@@ -17,6 +123,21 @@ export function auditPhaseHandoff(phase, feature) {
|
|
|
17
123
|
handoffUpdatedAt: phase.handoffUpdatedAt,
|
|
18
124
|
missingCompletionTaskIds: missingCompletionTasks.map((task) => task.id),
|
|
19
125
|
missingCompletionTasks,
|
|
126
|
+
completenessVersion: HANDOFF_COMPLETENESS_AUDIT_VERSION,
|
|
127
|
+
targetContentChars: TARGET_HANDOFF_CONTENT_CHARS,
|
|
128
|
+
maxContentChars: MAX_HANDOFF_CONTENT_CHARS,
|
|
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,
|
|
139
|
+
existingCompletenessAudit: phase.handoffAudit,
|
|
140
|
+
phaseWorkMap: buildPhaseWorkMap(phase, feature.number),
|
|
20
141
|
};
|
|
21
142
|
}
|
|
22
143
|
function nonEmpty(value, field) {
|
|
@@ -34,6 +155,279 @@ function appendSection(existing, section) {
|
|
|
34
155
|
return existing;
|
|
35
156
|
return existing.trim() ? `${existing.trim()}\n\n---\n${normalized}` : normalized;
|
|
36
157
|
}
|
|
158
|
+
function isSubstantive(value) {
|
|
159
|
+
const normalized = value.trim();
|
|
160
|
+
if (normalized.length < 12)
|
|
161
|
+
return false;
|
|
162
|
+
return !/^(?:n\/?a|none|nothing|unknown|same as above|see (?:above|handoff|document)|not applicable|tbd)[.!]?$/i.test(normalized);
|
|
163
|
+
}
|
|
164
|
+
function stripRenderedCompletenessAudit(content) {
|
|
165
|
+
const start = content.indexOf(HANDOFF_AUDIT_START_MARKER);
|
|
166
|
+
if (start < 0)
|
|
167
|
+
return content.trim();
|
|
168
|
+
const end = content.indexOf(HANDOFF_AUDIT_END_MARKER, start);
|
|
169
|
+
if (end < 0)
|
|
170
|
+
return content.slice(0, start).trim();
|
|
171
|
+
return `${content.slice(0, start)}${content.slice(end + HANDOFF_AUDIT_END_MARKER.length)}`.trim();
|
|
172
|
+
}
|
|
173
|
+
export function handoffContentHash(content) {
|
|
174
|
+
return createHash("sha256").update(content).digest("hex");
|
|
175
|
+
}
|
|
176
|
+
export function validateHandoffCompletenessAudit(audit) {
|
|
177
|
+
const expectedIds = HANDOFF_COMPLETENESS_CATEGORIES.map((entry) => entry.id);
|
|
178
|
+
if (!audit || audit.version !== HANDOFF_COMPLETENESS_AUDIT_VERSION) {
|
|
179
|
+
throw new HandoffContractError("HANDOFF_COMPLETENESS_AUDIT_REQUIRED", `Handoff completeness audit version ${HANDOFF_COMPLETENESS_AUDIT_VERSION} is required.`, { requiredVersion: HANDOFF_COMPLETENESS_AUDIT_VERSION, missingCategories: expectedIds });
|
|
180
|
+
}
|
|
181
|
+
const byCategory = new Map();
|
|
182
|
+
const duplicates = [];
|
|
183
|
+
for (const entry of audit.entries) {
|
|
184
|
+
if (byCategory.has(entry.category))
|
|
185
|
+
duplicates.push(entry.category);
|
|
186
|
+
else
|
|
187
|
+
byCategory.set(entry.category, entry);
|
|
188
|
+
}
|
|
189
|
+
const missingCategories = expectedIds.filter((id) => !byCategory.has(id));
|
|
190
|
+
const unknownCategories = [...byCategory.keys()].filter((id) => !expectedIds.includes(id));
|
|
191
|
+
const invalidCategories = expectedIds.filter((id) => {
|
|
192
|
+
const entry = byCategory.get(id);
|
|
193
|
+
return entry ? !isSubstantive(entry.detail) : false;
|
|
194
|
+
});
|
|
195
|
+
if (missingCategories.length > 0 || unknownCategories.length > 0 || duplicates.length > 0 || invalidCategories.length > 0) {
|
|
196
|
+
throw new HandoffContractError("HANDOFF_COMPLETENESS_AUDIT_REQUIRED", "The handoff completeness audit is missing required categories or contains non-substantive entries.", { missingCategories, invalidCategories, unknownCategories, duplicateCategories: [...new Set(duplicates)] });
|
|
197
|
+
}
|
|
198
|
+
return expectedIds.map((id) => byCategory.get(id));
|
|
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
|
+
}
|
|
383
|
+
export function renderHandoffCompletenessAudit(audit) {
|
|
384
|
+
const entries = validateHandoffCompletenessAudit(audit);
|
|
385
|
+
const labels = new Map(HANDOFF_COMPLETENESS_CATEGORIES.map((entry) => [entry.id, entry.label]));
|
|
386
|
+
return [
|
|
387
|
+
HANDOFF_AUDIT_START_MARKER,
|
|
388
|
+
`## Operational completeness audit (v${HANDOFF_COMPLETENESS_AUDIT_VERSION})`,
|
|
389
|
+
"",
|
|
390
|
+
...entries.flatMap((entry) => [
|
|
391
|
+
`### ${labels.get(entry.category) ?? entry.category}`,
|
|
392
|
+
`**Status:** ${entry.status}`,
|
|
393
|
+
entry.detail.trim(),
|
|
394
|
+
"",
|
|
395
|
+
]),
|
|
396
|
+
HANDOFF_AUDIT_END_MARKER,
|
|
397
|
+
].join("\n").trim();
|
|
398
|
+
}
|
|
399
|
+
export function renderVerifiedHandoffContent(content, audit, coldStartInventory, supportingDocumentContents = []) {
|
|
400
|
+
const base = stripRenderedCompletenessAudit(content);
|
|
401
|
+
validateCanonicalHandoffContent(base);
|
|
402
|
+
validateHandoffCompletenessAudit(audit);
|
|
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);
|
|
425
|
+
}
|
|
426
|
+
else {
|
|
427
|
+
lines.unshift(...metadata, "");
|
|
428
|
+
}
|
|
429
|
+
return lines.join("\n").trim();
|
|
430
|
+
}
|
|
37
431
|
export function validateCanonicalHandoffContent(content) {
|
|
38
432
|
const body = nonEmpty(content, "Handoff content");
|
|
39
433
|
const required = [
|
|
@@ -41,21 +435,56 @@ export function validateCanonicalHandoffContent(content) {
|
|
|
41
435
|
{ label: "Updated at", pattern: /^Updated at:\s*\S+/im },
|
|
42
436
|
{ label: "Reason", pattern: /^Reason:\s*\S+/im },
|
|
43
437
|
{ label: "Current focus", pattern: /^##\s+Current focus\s*$/im },
|
|
44
|
-
{ 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 },
|
|
45
442
|
{ label: "How to resume", pattern: /^##\s+How to resume\s*$/im },
|
|
46
|
-
{ label: "Files touched", pattern: /^##\s+Files touched\s*$/im },
|
|
47
|
-
{ label: "Blockers", pattern: /^##\s+Blockers\s*$/im },
|
|
48
|
-
{ label: "Next steps", pattern: /^##\s+Next steps\s*$/im },
|
|
49
|
-
{ label: "Recent decisions", pattern: /^##\s+Recent decisions\s*$/im },
|
|
50
443
|
];
|
|
51
444
|
const missing = required.filter((entry) => !entry.pattern.test(body)).map((entry) => entry.label);
|
|
52
|
-
if (missing.length
|
|
53
|
-
|
|
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
|
+
}
|
|
54
464
|
}
|
|
55
465
|
export function validateHandoffContextSync(phase, feature, input) {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
466
|
+
const requestedDocuments = input.supportingDocuments ?? [];
|
|
467
|
+
const verifiedDocuments = input.verifiedSupportingDocuments ?? [];
|
|
468
|
+
if (requestedDocuments.length !== verifiedDocuments.length) {
|
|
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 });
|
|
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
|
+
}
|
|
475
|
+
for (let index = 0; index < requestedDocuments.length; index += 1) {
|
|
476
|
+
const requested = requestedDocuments[index];
|
|
477
|
+
const verified = verifiedDocuments[index];
|
|
478
|
+
if (requested.path !== verified.path || !isSubstantive(requested.description) || requested.description.trim() !== verified.description) {
|
|
479
|
+
throw new HandoffContractError("HANDOFF_SUPPORTING_DOCUMENT_INVALID", `Supporting document ${requested.path || `(index ${index})`} is not valid or lacks a substantive description.`, { index, path: requested.path });
|
|
480
|
+
}
|
|
481
|
+
if (!input.content.includes(requested.path)) {
|
|
482
|
+
throw new HandoffContractError("HANDOFF_SUPPORTING_DOCUMENT_INVALID", `Canonical handoff content must link supporting document ${requested.path}.`, { index, path: requested.path });
|
|
483
|
+
}
|
|
484
|
+
}
|
|
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.`);
|
|
59
488
|
}
|
|
60
489
|
if (phase.handoffUpdatedAt !== input.expectedHandoffUpdatedAt) {
|
|
61
490
|
throw new Error("Handoff changed after preparation. Run handoff_prepare again and reconcile the latest content before writing.");
|
|
@@ -100,6 +529,9 @@ export function validateHandoffContextSync(phase, feature, input) {
|
|
|
100
529
|
}
|
|
101
530
|
export function applyHandoffContextSync(phase, feature, input, timestamp) {
|
|
102
531
|
validateHandoffContextSync(phase, feature, input);
|
|
532
|
+
const handoffContent = renderVerifiedHandoffContent(input.content, input.completenessAudit, input.coldStartInventory, input.verifiedSupportingDocumentContents ?? []);
|
|
533
|
+
const auditEntries = validateHandoffCompletenessAudit(input.completenessAudit);
|
|
534
|
+
const coldStartInventoryEntries = validateHandoffColdStartInventory(input.coldStartInventory, stripRenderedCompletenessAudit(input.content), input.verifiedSupportingDocumentContents ?? []);
|
|
103
535
|
const nextPhase = structuredClone(phase);
|
|
104
536
|
const nextFeature = structuredClone(feature);
|
|
105
537
|
const updatedTaskIds = [];
|
|
@@ -141,8 +573,26 @@ export function applyHandoffContextSync(phase, feature, input, timestamp) {
|
|
|
141
573
|
nextFeature.workDone = appendSection(nextFeature.workDone, featureUpdate.workDone);
|
|
142
574
|
nextFeature.workRemaining = appendSection(nextFeature.workRemaining, featureUpdate.workRemaining);
|
|
143
575
|
}
|
|
144
|
-
nextPhase.handoff =
|
|
576
|
+
nextPhase.handoff = handoffContent;
|
|
145
577
|
nextPhase.handoffUpdatedAt = timestamp;
|
|
578
|
+
nextPhase.handoffAudit = {
|
|
579
|
+
version: HANDOFF_COMPLETENESS_AUDIT_VERSION,
|
|
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
|
+
},
|
|
589
|
+
supportingDocuments: input.verifiedSupportingDocuments ?? [],
|
|
590
|
+
contentHash: handoffContentHash(handoffContent),
|
|
591
|
+
contentLength: handoffContent.length,
|
|
592
|
+
verifiedAt: timestamp,
|
|
593
|
+
resumeReadyAt: "",
|
|
594
|
+
readBackSourceReviews: [],
|
|
595
|
+
};
|
|
146
596
|
nextPhase.handoffReadAt = "";
|
|
147
597
|
nextPhase.updatedAt = timestamp;
|
|
148
598
|
nextFeature.updatedAt = timestamp;
|
package/dist/index.d.ts
CHANGED
|
@@ -4,14 +4,21 @@ export * from "./schema.js";
|
|
|
4
4
|
export * from "./checklist.js";
|
|
5
5
|
export * from "./recap.js";
|
|
6
6
|
export * from "./planner-rules.js";
|
|
7
|
+
export * from "./planner-skill.js";
|
|
7
8
|
export * from "./display-status.js";
|
|
9
|
+
export * from "./description-freshness.js";
|
|
8
10
|
export * from "./task-context.js";
|
|
9
11
|
export * from "./task-selection.js";
|
|
10
12
|
export * from "./task-start-outcome.js";
|
|
11
13
|
export * from "./read-tracking.js";
|
|
14
|
+
export * from "./payload-fallback.js";
|
|
12
15
|
export * from "./handoff-context.js";
|
|
16
|
+
export * from "./project-context-migration.js";
|
|
17
|
+
export * from "./requirement-macro-tasks.js";
|
|
13
18
|
export * from "./package-version.js";
|
|
14
|
-
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";
|
|
15
22
|
export { PlanRenderer } from "./renderer.js";
|
|
16
23
|
export { ExportService } from "./export-service.js";
|
|
17
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,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,yBAAyB,CAAC;AACxC,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,cAAc,EAAE,uBAAuB,EAAE,eAAe,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
|
@@ -4,13 +4,20 @@ export * from "./schema.js";
|
|
|
4
4
|
export * from "./checklist.js";
|
|
5
5
|
export * from "./recap.js";
|
|
6
6
|
export * from "./planner-rules.js";
|
|
7
|
+
export * from "./planner-skill.js";
|
|
7
8
|
export * from "./display-status.js";
|
|
9
|
+
export * from "./description-freshness.js";
|
|
8
10
|
export * from "./task-context.js";
|
|
9
11
|
export * from "./task-selection.js";
|
|
10
12
|
export * from "./task-start-outcome.js";
|
|
11
13
|
export * from "./read-tracking.js";
|
|
14
|
+
export * from "./payload-fallback.js";
|
|
12
15
|
export * from "./handoff-context.js";
|
|
16
|
+
export * from "./project-context-migration.js";
|
|
17
|
+
export * from "./requirement-macro-tasks.js";
|
|
13
18
|
export * from "./package-version.js";
|
|
14
|
-
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";
|
|
15
22
|
export { PlanRenderer } from "./renderer.js";
|
|
16
23
|
export { ExportService } from "./export-service.js";
|
package/dist/naming.d.ts
CHANGED
|
@@ -39,6 +39,8 @@ export declare function formatThreeDigitNumber(value: number): string;
|
|
|
39
39
|
export declare function formatPhaseRef(phaseNumber: number, featureNumber?: number): string;
|
|
40
40
|
/** Human-readable feature ref: `F00x`. Harness-agnostic. */
|
|
41
41
|
export declare function formatFeatureRef(featureNumber: number): string;
|
|
42
|
+
/** Human-readable idea ref: `I00x`. Ideas use an independent global sequence. */
|
|
43
|
+
export declare function formatIdeaRef(ideaNumber: number): string;
|
|
42
44
|
/** Find the parent feature's number for a phase (for P00x(F00x) composite refs). */
|
|
43
45
|
export declare function featureNumberOfPhase(phase: {
|
|
44
46
|
featureId?: string | null | undefined;
|
|
@@ -53,6 +55,7 @@ export declare function isLegacyPhaseId(phaseId: string): boolean;
|
|
|
53
55
|
export declare function migratePhaseId(featureId: string, number: number, slug: string): string;
|
|
54
56
|
export declare function createTaskId(): string;
|
|
55
57
|
export declare function createRequirementId(): string;
|
|
58
|
+
export declare function createIdeaId(): string;
|
|
56
59
|
export declare function createMacroTaskId(): string;
|
|
57
60
|
export declare function createFeatureId(): string;
|
|
58
61
|
export declare function createChecklistItemId(taskId: string, number: number, title: string): string;
|
package/dist/naming.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"naming.d.ts","sourceRoot":"","sources":["../src/naming.ts"],"names":[],"mappings":"AAKA;iFACiF;AACjF,eAAO,MAAM,kBAAkB,qCAAqC,CAAC;AAErE,eAAO,MAAM,eAAe,IAAI,CAAC;AACjC,eAAO,MAAM,gBAAgB,QAAkB,CAAC;AAEhD,4EAA4E;AAC5E,eAAO,MAAM,YAAY,QAAoE,CAAC;AAE9F,wBAAgB,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAEtD;AAED;;gEAEgE;AAChE,wBAAsB,sBAAsB,CAAC,CAAC,SAAS;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,EACnE,IAAI,EAAE,SAAS,GAAG,OAAO,EACzB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,GACnC,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAStD;AAGD;;;;;qCAKqC;AACrC,wBAAgB,aAAa,CAAC,QAAQ,GAAE,GAAG,CAAC,MAAM,CAAa,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CA2BtF;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAOnD;AAED;;;;8BAI8B;AAC9B,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,SAAK,EAAE,QAAQ,SAAa,GAAG,MAAM,CAGnF;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE5D;AAED;;iBAEiB;AACjB,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,CAGlF;AAED,4DAA4D;AAC5D,wBAAgB,gBAAgB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAE9D;AAED,oFAAoF;AACpF,wBAAgB,oBAAoB,CAClC,KAAK,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAA;CAAE,EAChD,QAAQ,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EAAE,GACzC,MAAM,GAAG,SAAS,CAEpB;AAED,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED,mFAAmF;AACnF,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAExD;AAED,0FAA0F;AAC1F,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAGtF;AAED,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAED,wBAAgB,mBAAmB,IAAI,MAAM,CAE5C;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAE3F;AAED,wBAAgB,sBAAsB,IAAI,MAAM,CAE/C"}
|
|
1
|
+
{"version":3,"file":"naming.d.ts","sourceRoot":"","sources":["../src/naming.ts"],"names":[],"mappings":"AAKA;iFACiF;AACjF,eAAO,MAAM,kBAAkB,qCAAqC,CAAC;AAErE,eAAO,MAAM,eAAe,IAAI,CAAC;AACjC,eAAO,MAAM,gBAAgB,QAAkB,CAAC;AAEhD,4EAA4E;AAC5E,eAAO,MAAM,YAAY,QAAoE,CAAC;AAE9F,wBAAgB,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAEtD;AAED;;gEAEgE;AAChE,wBAAsB,sBAAsB,CAAC,CAAC,SAAS;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,EACnE,IAAI,EAAE,SAAS,GAAG,OAAO,EACzB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,GACnC,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAStD;AAGD;;;;;qCAKqC;AACrC,wBAAgB,aAAa,CAAC,QAAQ,GAAE,GAAG,CAAC,MAAM,CAAa,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CA2BtF;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAOnD;AAED;;;;8BAI8B;AAC9B,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,SAAK,EAAE,QAAQ,SAAa,GAAG,MAAM,CAGnF;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE5D;AAED;;iBAEiB;AACjB,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,CAGlF;AAED,4DAA4D;AAC5D,wBAAgB,gBAAgB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAE9D;AAED,iFAAiF;AACjF,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,oFAAoF;AACpF,wBAAgB,oBAAoB,CAClC,KAAK,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAA;CAAE,EAChD,QAAQ,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EAAE,GACzC,MAAM,GAAG,SAAS,CAEpB;AAED,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED,mFAAmF;AACnF,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAExD;AAED,0FAA0F;AAC1F,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAGtF;AAED,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAED,wBAAgB,mBAAmB,IAAI,MAAM,CAE5C;AAED,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAE3F;AAED,wBAAgB,sBAAsB,IAAI,MAAM,CAE/C"}
|
package/dist/naming.js
CHANGED
|
@@ -94,6 +94,10 @@ export function formatPhaseRef(phaseNumber, featureNumber) {
|
|
|
94
94
|
export function formatFeatureRef(featureNumber) {
|
|
95
95
|
return `F${formatThreeDigitNumber(featureNumber)}`;
|
|
96
96
|
}
|
|
97
|
+
/** Human-readable idea ref: `I00x`. Ideas use an independent global sequence. */
|
|
98
|
+
export function formatIdeaRef(ideaNumber) {
|
|
99
|
+
return `I${formatThreeDigitNumber(ideaNumber)}`;
|
|
100
|
+
}
|
|
97
101
|
/** Find the parent feature's number for a phase (for P00x(F00x) composite refs). */
|
|
98
102
|
export function featureNumberOfPhase(phase, features) {
|
|
99
103
|
return phase.featureId ? features.find((f) => f.id === phase.featureId)?.number : undefined;
|
|
@@ -116,6 +120,9 @@ export function createTaskId() {
|
|
|
116
120
|
export function createRequirementId() {
|
|
117
121
|
return randomUUID();
|
|
118
122
|
}
|
|
123
|
+
export function createIdeaId() {
|
|
124
|
+
return randomUUID();
|
|
125
|
+
}
|
|
119
126
|
export function createMacroTaskId() {
|
|
120
127
|
return randomUUID();
|
|
121
128
|
}
|
|
@@ -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"}
|