@agent-plan/core 0.2.19-next.24 → 0.2.19-next.25
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/plan-store.d.ts +22 -13
- package/dist/plan-store.d.ts.map +1 -1
- package/dist/plan-store.js +102 -64
- package/package.json +1 -1
package/dist/plan-store.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
1
2
|
import { type Feature, type FeaturesDocument, type Manifest, type Phase, type PlanWorkspace, type Project, type RequirementsDocument, type Requirement, type ActivityEntry, type ActivityLog, type CodebaseProfile, type ResumeFocus, type WorkDeviation } from "./schema.js";
|
|
2
3
|
import { type ParentDisplay } from "./display-status.js";
|
|
3
4
|
export interface PlanStoreErrorDetails {
|
|
@@ -24,6 +25,11 @@ export declare class PlanStoreError extends Error {
|
|
|
24
25
|
}
|
|
25
26
|
export declare function setWriteBusyHook(hook: ((busy: boolean) => void) | undefined): void;
|
|
26
27
|
export declare function setWriteNotifyHook(hook: (() => void) | undefined): void;
|
|
28
|
+
/** Allocation registry is deliberately outside the versioned plan. Git worktrees
|
|
29
|
+
* share their common git dir, so reservations are serialized across branches
|
|
30
|
+
* without rewriting project.json or unrelated planner entities. */
|
|
31
|
+
declare const AllocationKindSchema: z.ZodEnum<["feature", "phase", "task"]>;
|
|
32
|
+
type AllocationKind = z.infer<typeof AllocationKindSchema>;
|
|
27
33
|
export declare function withFeatureLock<T>(featureId: string, fn: () => Promise<T>): Promise<T>;
|
|
28
34
|
/** Summary of a phase that has a non-empty pending handoff, for the
|
|
29
35
|
* listHandoffs() API. Completed/canceled phases are never returned here. */
|
|
@@ -165,26 +171,28 @@ export declare class PlanStore {
|
|
|
165
171
|
* Returns true if the file was (re)written. Safe to call on every load. */
|
|
166
172
|
ensureGitignore(): Promise<boolean>;
|
|
167
173
|
exists(): Promise<boolean>;
|
|
174
|
+
/** Read-only manifest load. Upgrading legacy `.local` state is explicit
|
|
175
|
+
* maintenance (`repair`), never an incidental side effect of opening a plan. */
|
|
168
176
|
loadManifest(): Promise<Manifest>;
|
|
169
177
|
loadProject(): Promise<Project>;
|
|
170
178
|
/**
|
|
171
|
-
*
|
|
172
|
-
*
|
|
173
|
-
*
|
|
174
|
-
*
|
|
175
|
-
*
|
|
176
|
-
*
|
|
179
|
+
* Reserve immutable human identifiers without touching tracked `project.json`.
|
|
180
|
+
* Worktrees in the same clone share `.git/agent-plan/allocations`, guarded by
|
|
181
|
+
* the same cross-process lock used for atomic writes. The registry reserves
|
|
182
|
+
* numbers/short IDs before an entity file is written, so parallel branches
|
|
183
|
+
* cannot allocate the same F/P/T or shortId. Existing entities are never
|
|
184
|
+
* rewritten; cross-clone coordination requires a shared allocator service.
|
|
177
185
|
*/
|
|
186
|
+
allocateEntityIdentity(kind: AllocationKind, entityId: string): Promise<{
|
|
187
|
+
number: number;
|
|
188
|
+
shortId: string;
|
|
189
|
+
}>;
|
|
190
|
+
/** Compatibility helpers. New callers should allocate the number and shortId
|
|
191
|
+
* together with allocateEntityIdentity so reservation cannot be split. */
|
|
178
192
|
allocFeatureNumber(): Promise<number>;
|
|
179
193
|
allocPhaseNumber(): Promise<number>;
|
|
180
194
|
allocTaskNumber(): Promise<number>;
|
|
181
|
-
|
|
182
|
-
* serializes concurrent calls via `withWriteLock` on the project file, so
|
|
183
|
-
* the read-modify-write is race-free in-process. The collision guard
|
|
184
|
-
* additionally skips any candidate that already exists in the data (safety
|
|
185
|
-
* net for cross-process races or manual edits) and persists the corrected
|
|
186
|
-
* counter. */
|
|
187
|
-
private allocSeqNumber;
|
|
195
|
+
private allocateLegacyNumber;
|
|
188
196
|
loadPhase(phaseId: string): Promise<Phase>;
|
|
189
197
|
/** Read raw feature files WITHOUT the derived `status` field. Used
|
|
190
198
|
* internally so loadFeatures/loadAll can derive status from phases without
|
|
@@ -385,4 +393,5 @@ export declare class PlanStore {
|
|
|
385
393
|
/** Update .local/timestamp.json to reflect a change. */
|
|
386
394
|
private touchTimestamp;
|
|
387
395
|
}
|
|
396
|
+
export {};
|
|
388
397
|
//# sourceMappingURL=plan-store.d.ts.map
|
package/dist/plan-store.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plan-store.d.ts","sourceRoot":"","sources":["../src/plan-store.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"plan-store.d.ts","sourceRoot":"","sources":["../src/plan-store.ts"],"names":[],"mappings":"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"}
|
package/dist/plan-store.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { access, copyFile, mkdir, readdir, readFile, rename, rm, stat, unlink, writeFile } from "node:fs/promises";
|
|
2
|
-
import { basename, dirname, join } from "node:path";
|
|
2
|
+
import { basename, dirname, join, resolve } from "node:path";
|
|
3
|
+
import { randomUUID } from "node:crypto";
|
|
3
4
|
import { z, ZodError } from "zod";
|
|
4
5
|
/** Canonical `.planner/.gitignore` content (P042 spec): ignore the `.local/`
|
|
5
6
|
* transient root, legacy `*.bak` crash backups, `*.tmp.*` atomic-write temp
|
|
@@ -71,8 +72,53 @@ export function setWriteNotifyHook(hook) {
|
|
|
71
72
|
}
|
|
72
73
|
const CROSS_PROCESS_LOCK_STALE_MS = 30_000;
|
|
73
74
|
const CROSS_PROCESS_LOCK_RETRY_MS = 10;
|
|
75
|
+
/** Allocation registry is deliberately outside the versioned plan. Git worktrees
|
|
76
|
+
* share their common git dir, so reservations are serialized across branches
|
|
77
|
+
* without rewriting project.json or unrelated planner entities. */
|
|
78
|
+
const AllocationKindSchema = z.enum(["feature", "phase", "task"]);
|
|
79
|
+
const AllocationRegistrySchema = z.object({
|
|
80
|
+
version: z.literal(1),
|
|
81
|
+
projectId: z.string().min(1),
|
|
82
|
+
allocations: z.array(z.object({
|
|
83
|
+
kind: AllocationKindSchema,
|
|
84
|
+
entityId: z.string().min(1),
|
|
85
|
+
number: z.number().int().positive(),
|
|
86
|
+
shortId: z.string().regex(/^[A-Z2-9]{5}$/),
|
|
87
|
+
})).default([]),
|
|
88
|
+
});
|
|
89
|
+
async function gitCommonDirFor(planRoot) {
|
|
90
|
+
let current = resolve(planRoot);
|
|
91
|
+
for (;;) {
|
|
92
|
+
const dotGit = join(current, ".git");
|
|
93
|
+
try {
|
|
94
|
+
const info = await stat(dotGit);
|
|
95
|
+
if (info.isDirectory())
|
|
96
|
+
return dotGit;
|
|
97
|
+
const pointer = await readFile(dotGit, "utf8");
|
|
98
|
+
const match = pointer.match(/^gitdir:\s*(.+)\s*$/m);
|
|
99
|
+
if (!match?.[1])
|
|
100
|
+
return undefined;
|
|
101
|
+
const worktreeGitDir = resolve(current, match[1]);
|
|
102
|
+
const commonDirRef = await readFile(join(worktreeGitDir, "commondir"), "utf8").catch(() => "");
|
|
103
|
+
return commonDirRef.trim() ? resolve(worktreeGitDir, commonDirRef.trim()) : worktreeGitDir;
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
const parent = dirname(current);
|
|
107
|
+
if (parent === current)
|
|
108
|
+
return undefined;
|
|
109
|
+
current = parent;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
async function writeRegistry(path, registry) {
|
|
114
|
+
await mkdir(dirname(path), { recursive: true });
|
|
115
|
+
const tmp = `${path}.tmp.${process.pid}.${Date.now()}`;
|
|
116
|
+
await writeFile(tmp, JSON.stringify(registry, null, 2), "utf8");
|
|
117
|
+
await rename(tmp, path);
|
|
118
|
+
}
|
|
74
119
|
async function acquireCrossProcessLock(path) {
|
|
75
120
|
const lockPath = `${path}.lock`;
|
|
121
|
+
await mkdir(dirname(lockPath), { recursive: true });
|
|
76
122
|
for (;;) {
|
|
77
123
|
try {
|
|
78
124
|
await mkdir(lockPath);
|
|
@@ -850,79 +896,71 @@ export class PlanStore {
|
|
|
850
896
|
}
|
|
851
897
|
}
|
|
852
898
|
// ── Loaders ──────────────────────────────────────────────────────────
|
|
899
|
+
/** Read-only manifest load. Upgrading legacy `.local` state is explicit
|
|
900
|
+
* maintenance (`repair`), never an incidental side effect of opening a plan. */
|
|
853
901
|
async loadManifest() {
|
|
854
|
-
// Ensure the .planner/.gitignore ignores transients (P042). Idempotent;
|
|
855
|
-
// upgrades plans initialized before the .local/ move. Runs on every load.
|
|
856
|
-
await this.ensureGitignore().catch(() => { });
|
|
857
902
|
const manifest = await readJson(this.manifestPath(), ManifestSchema);
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
manifest.updatedAt = timestamp.updatedAt;
|
|
861
|
-
}
|
|
862
|
-
catch {
|
|
863
|
-
// Legacy plan without .local/timestamp.json: bootstrap from manifest.updatedAt
|
|
864
|
-
// and create the timestamp file so subsequent writes stay in .local/.
|
|
865
|
-
try {
|
|
866
|
-
await mkdir(this.localRoot(), { recursive: true });
|
|
867
|
-
await atomicWriteJson(this.timestampPath(), { updatedAt: manifest.updatedAt }, this.root);
|
|
868
|
-
}
|
|
869
|
-
catch {
|
|
870
|
-
// ignore bootstrap failure
|
|
871
|
-
}
|
|
872
|
-
}
|
|
873
|
-
return manifest;
|
|
903
|
+
const timestamp = await readJson(this.timestampPath(), z.object({ updatedAt: TimestampSchema })).catch(() => undefined);
|
|
904
|
+
return timestamp ? { ...manifest, updatedAt: timestamp.updatedAt } : manifest;
|
|
874
905
|
}
|
|
875
906
|
async loadProject() {
|
|
876
907
|
return readJson(this.projectPath(), ProjectSchema);
|
|
877
908
|
}
|
|
878
909
|
/**
|
|
879
|
-
*
|
|
880
|
-
*
|
|
881
|
-
*
|
|
882
|
-
*
|
|
883
|
-
*
|
|
884
|
-
*
|
|
910
|
+
* Reserve immutable human identifiers without touching tracked `project.json`.
|
|
911
|
+
* Worktrees in the same clone share `.git/agent-plan/allocations`, guarded by
|
|
912
|
+
* the same cross-process lock used for atomic writes. The registry reserves
|
|
913
|
+
* numbers/short IDs before an entity file is written, so parallel branches
|
|
914
|
+
* cannot allocate the same F/P/T or shortId. Existing entities are never
|
|
915
|
+
* rewritten; cross-clone coordination requires a shared allocator service.
|
|
885
916
|
*/
|
|
886
|
-
async
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
for (const t of p.tasks)
|
|
903
|
-
used.add(t.number);
|
|
904
|
-
}
|
|
905
|
-
else if (kind === "phase") {
|
|
917
|
+
async allocateEntityIdentity(kind, entityId) {
|
|
918
|
+
const project = await this.loadProject();
|
|
919
|
+
const manifest = await this.loadManifest();
|
|
920
|
+
const commonGitDir = await gitCommonDirFor(this.root);
|
|
921
|
+
const registryPath = commonGitDir
|
|
922
|
+
? join(commonGitDir, "agent-plan", "allocations", `${manifest.projectId}.json`)
|
|
923
|
+
: join(this.localRoot(), "allocations", `${manifest.projectId}.json`);
|
|
924
|
+
return withWriteLock(registryPath, async () => {
|
|
925
|
+
const registry = AllocationRegistrySchema.parse(await readFile(registryPath, "utf8")
|
|
926
|
+
.then(JSON.parse)
|
|
927
|
+
.catch(() => ({ version: 1, projectId: manifest.projectId, allocations: [] })));
|
|
928
|
+
if (registry.projectId !== manifest.projectId)
|
|
929
|
+
throw new PlanStoreError(`allocation registry project mismatch: ${registryPath}`);
|
|
930
|
+
const prior = registry.allocations.find((entry) => entry.kind === kind && entry.entityId === entityId);
|
|
931
|
+
if (prior)
|
|
932
|
+
return { number: prior.number, shortId: prior.shortId };
|
|
906
933
|
const phases = await this.loadAllPhases();
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
934
|
+
const features = (await this.loadFeatures()).features;
|
|
935
|
+
const canonical = kind === "feature"
|
|
936
|
+
? features.map((feature) => ({ number: feature.number, shortId: feature.shortId }))
|
|
937
|
+
: kind === "phase"
|
|
938
|
+
? phases.map((phase) => ({ number: phase.number, shortId: phase.shortId }))
|
|
939
|
+
: phases.flatMap((phase) => phase.tasks.map((task) => ({ number: task.number, shortId: task.shortId })));
|
|
940
|
+
const usedNumbers = new Set([...canonical.map((entry) => entry.number), ...registry.allocations.filter((entry) => entry.kind === kind).map((entry) => entry.number)]);
|
|
941
|
+
const counter = kind === "feature" ? project.nextFeatureNumber : kind === "phase" ? project.nextPhaseNumber : project.nextTaskNumber;
|
|
942
|
+
let number = Math.max(1, counter);
|
|
943
|
+
while (usedNumbers.has(number))
|
|
944
|
+
number += 1;
|
|
945
|
+
const allShortIds = new Set([
|
|
946
|
+
...features.map((feature) => feature.shortId),
|
|
947
|
+
...phases.flatMap((phase) => [phase.shortId, ...phase.tasks.map((task) => task.shortId)]),
|
|
948
|
+
...registry.allocations.map((entry) => entry.shortId),
|
|
949
|
+
].filter(Boolean));
|
|
950
|
+
const allocation = { kind, entityId, number, shortId: createShortId(allShortIds, `${kind}:${entityId}`) };
|
|
951
|
+
registry.allocations.push(allocation);
|
|
952
|
+
await writeRegistry(registryPath, registry);
|
|
953
|
+
return { number: allocation.number, shortId: allocation.shortId };
|
|
924
954
|
});
|
|
925
|
-
|
|
955
|
+
}
|
|
956
|
+
/** Compatibility helpers. New callers should allocate the number and shortId
|
|
957
|
+
* together with allocateEntityIdentity so reservation cannot be split. */
|
|
958
|
+
async allocFeatureNumber() { return this.allocateLegacyNumber("feature"); }
|
|
959
|
+
async allocPhaseNumber() { return this.allocateLegacyNumber("phase"); }
|
|
960
|
+
async allocTaskNumber() { return this.allocateLegacyNumber("task"); }
|
|
961
|
+
async allocateLegacyNumber(kind) {
|
|
962
|
+
const id = `legacy-${kind}-${randomUUID()}`;
|
|
963
|
+
return (await this.allocateEntityIdentity(kind, id)).number;
|
|
926
964
|
}
|
|
927
965
|
async loadPhase(phaseId) {
|
|
928
966
|
const raw = await readJson(this.phasePath(phaseId), PhaseSchema);
|
package/package.json
CHANGED