@agent-plan/core 0.2.18 → 0.2.19-next.0

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/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export * from "./naming.js";
2
+ export * from "./refs.js";
2
3
  export * from "./schema.js";
3
- export { PlanStore, PlanStoreError, setWriteBusyHook, setWriteNotifyHook, migrateToUuids, withFeatureLock } from "./plan-store.js";
4
+ export { PlanStore, PlanStoreError, setWriteBusyHook, setWriteNotifyHook, migrateToUuids, withFeatureLock, type PhaseHandoffSummary } from "./plan-store.js";
4
5
  export { PlanRenderer } from "./renderer.js";
5
6
  export { ExportService } from "./export-service.js";
6
7
  export type { CodebaseProfile, ResumeFocus, ActivityEntry, ActivityLog, AmbientFacts } from "./schema.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACnI,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"}
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,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,cAAc,EAAE,eAAe,EAAE,KAAK,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAC7J,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
@@ -1,4 +1,5 @@
1
1
  export * from "./naming.js";
2
+ export * from "./refs.js";
2
3
  export * from "./schema.js";
3
4
  export { PlanStore, PlanStoreError, setWriteBusyHook, setWriteNotifyHook, migrateToUuids, withFeatureLock } from "./plan-store.js";
4
5
  export { PlanRenderer } from "./renderer.js";
package/dist/naming.d.ts CHANGED
@@ -1,3 +1,12 @@
1
+ /** Crockford Base32 alphabet — excludes ambiguous characters 0/O/1/I/L
2
+ * to minimize human transcription errors. 32 symbols → 5 chars = 33M combos. */
3
+ export declare const CROCKFORD_ALPHABET = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
4
+ export declare const SHORT_ID_LENGTH = 5;
5
+ export declare const SHORT_ID_PATTERN: RegExp;
6
+ /** Generate a globally-unique short id (5 chars, Crockford Base32, e.g. `UUXD1`-style
7
+ * but without 0/1/I/O). Retries until the id is not in `existing` (project-scoped
8
+ * collision guard). Throws only in the impossible saturation case (~50 retries). */
9
+ export declare function createShortId(existing?: Set<string>): string;
1
10
  export declare function normalizeSlug(input: string): string;
2
11
  /** Normalize, truncate to maxLen, and strip dangling dashes so the result
3
12
  * always satisfies SlugSchema (/^[a-z0-9]+(?:-[a-z0-9]+)*$/). Returns fallback
@@ -7,6 +16,10 @@ export declare function normalizeSlug(input: string): string;
7
16
  export declare function clampSlug(input: string, maxLen?: number, fallback?: string): string;
8
17
  export declare function formatTwoDigitNumber(value: number): string;
9
18
  export declare function formatThreeDigitNumber(value: number): string;
19
+ /** Human-readable phase composite ref: `P00x` or `P00x(F00x)` when the parent
20
+ * feature number is known. Harness-agnostic (used by core handoff listing +
21
+ * adapters). */
22
+ export declare function formatPhaseRef(phaseNumber: number, featureNumber?: number): string;
10
23
  export declare function createPhaseId(): string;
11
24
  /** True for legacy phase ids that are NOT feature-scoped (e.g. `phase-01-...`). */
12
25
  export declare function isLegacyPhaseId(phaseId: string): boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"naming.d.ts","sourceRoot":"","sources":["../src/naming.ts"],"names":[],"mappings":"AAKA,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,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"}
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;;qFAEqF;AACrF,wBAAgB,aAAa,CAAC,QAAQ,GAAE,GAAG,CAAC,MAAM,CAAa,GAAG,MAAM,CAUvE;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,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"}
package/dist/naming.js CHANGED
@@ -1,6 +1,26 @@
1
- import { randomUUID } from "node:crypto";
1
+ import { randomInt, randomUUID } from "node:crypto";
2
2
  const SLUG_PATTERN = /[^a-z0-9]+/g;
3
3
  const MULTI_DASH_PATTERN = /-+/g;
4
+ /** Crockford Base32 alphabet — excludes ambiguous characters 0/O/1/I/L
5
+ * to minimize human transcription errors. 32 symbols → 5 chars = 33M combos. */
6
+ export const CROCKFORD_ALPHABET = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
7
+ export const SHORT_ID_LENGTH = 5;
8
+ export const SHORT_ID_PATTERN = /^[A-Z2-9]{5}$/;
9
+ /** Generate a globally-unique short id (5 chars, Crockford Base32, e.g. `UUXD1`-style
10
+ * but without 0/1/I/O). Retries until the id is not in `existing` (project-scoped
11
+ * collision guard). Throws only in the impossible saturation case (~50 retries). */
12
+ export function createShortId(existing = new Set()) {
13
+ const max = CROCKFORD_ALPHABET.length;
14
+ for (let attempt = 0; attempt < 64; attempt += 1) {
15
+ let id = "";
16
+ for (let i = 0; i < SHORT_ID_LENGTH; i += 1) {
17
+ id += CROCKFORD_ALPHABET[randomInt(0, max)];
18
+ }
19
+ if (!existing.has(id))
20
+ return id;
21
+ }
22
+ throw new Error("createShortId: could not generate a unique id after 64 attempts");
23
+ }
4
24
  export function normalizeSlug(input) {
5
25
  return input
6
26
  .trim()
@@ -24,6 +44,13 @@ export function formatTwoDigitNumber(value) {
24
44
  export function formatThreeDigitNumber(value) {
25
45
  return String(value).padStart(3, "0");
26
46
  }
47
+ /** Human-readable phase composite ref: `P00x` or `P00x(F00x)` when the parent
48
+ * feature number is known. Harness-agnostic (used by core handoff listing +
49
+ * adapters). */
50
+ export function formatPhaseRef(phaseNumber, featureNumber) {
51
+ const p = `P${formatThreeDigitNumber(phaseNumber)}`;
52
+ return featureNumber != null ? `${p}(F${formatThreeDigitNumber(featureNumber)})` : p;
53
+ }
27
54
  export function createPhaseId() {
28
55
  return randomUUID();
29
56
  }
@@ -1,4 +1,4 @@
1
- import { type FeaturesDocument, type Manifest, type Phase, type PlanWorkspace, type Project, type RequirementsDocument, type ActivityEntry, type ActivityLog, type CodebaseProfile, type ResumeFocus } from "./schema.js";
1
+ import { type Feature, type FeaturesDocument, type Manifest, type Phase, type PlanWorkspace, type Project, type RequirementsDocument, type ActivityEntry, type ActivityLog, type CodebaseProfile, type ResumeFocus } from "./schema.js";
2
2
  export declare class PlanStoreError extends Error {
3
3
  readonly cause?: unknown | undefined;
4
4
  constructor(message: string, cause?: unknown | undefined);
@@ -6,6 +6,16 @@ export declare class PlanStoreError extends Error {
6
6
  export declare function setWriteBusyHook(hook: ((busy: boolean) => void) | undefined): void;
7
7
  export declare function setWriteNotifyHook(hook: (() => void) | undefined): void;
8
8
  export declare function withFeatureLock<T>(featureId: string, fn: () => Promise<T>): Promise<T>;
9
+ /** Summary of a phase that has a non-empty handoff, for the listHandoffs() API. */
10
+ export interface PhaseHandoffSummary {
11
+ phaseId: string;
12
+ /** Human-readable composite ref, e.g. `P003` or `P003(F002)`. */
13
+ compositeRef: string;
14
+ /** handoffUpdatedAt (falls back to phase.updatedAt if unset). */
15
+ updatedAt: string;
16
+ /** First non-empty line of the handoff, leading markdown headers stripped, ~80 chars. */
17
+ firstLine: string;
18
+ }
9
19
  export declare function migrateToUuids(store: PlanStore): Promise<void>;
10
20
  export declare class PlanStore {
11
21
  readonly root: string;
@@ -36,6 +46,13 @@ export declare class PlanStore {
36
46
  private projectPath;
37
47
  private requirementsPath;
38
48
  private featuresPath;
49
+ private featuresDir;
50
+ private featurePath;
51
+ private withFeaturesLock;
52
+ /** Idempotent one-time migration: if a legacy features.json exists, split it
53
+ * into features/<id>.json (one per feature) and remove the legacy file.
54
+ * Must be called under withFeaturesLock. Crash-safe: re-run overwrites. */
55
+ private migrateLegacy;
39
56
  private phasesDir;
40
57
  private phasePath;
41
58
  private generatedDir;
@@ -95,6 +112,22 @@ export declare class PlanStore {
95
112
  cleanupOrphanBackups(): Promise<{
96
113
  removed: number;
97
114
  }>;
115
+ /** All non-empty shortIds currently assigned in the project (features + phases + tasks).
116
+ * Read-only; used by createShortId collision guard during entity creation. */
117
+ assignedShortIds(): Promise<Set<string>>;
118
+ /** Next priority (>=1) for a new entity within its scope.
119
+ * - feature: max priority among features + 1
120
+ * - phase: max priority among phases of parentId (featureId) + 1
121
+ * - task: max priority among tasks of parentId (phaseId) + 1 */
122
+ nextPriority(kind: "feature" | "phase" | "task", parentId?: string): Promise<number>;
123
+ /** Idempotent backfill of shortId (globally-unique 5-char Crockford) and priority
124
+ * (per-scope display order). Assigns missing shortIds and priorities; never overwrites
125
+ * existing non-empty shortIds or non-zero priorities. Safe to run at startup. */
126
+ ensureShortIdsAndPriority(): Promise<{
127
+ shortIdsAssigned: number;
128
+ prioritiesAssigned: number;
129
+ duplicateShortIds: string[];
130
+ }>;
98
131
  /** Repair dangling references and report integrity. One-shot maintenance op. */
99
132
  repair(): Promise<{
100
133
  migrated: {
@@ -102,33 +135,57 @@ export declare class PlanStore {
102
135
  repaired: number;
103
136
  inferred: number;
104
137
  };
138
+ backfill: {
139
+ shortIdsAssigned: number;
140
+ prioritiesAssigned: number;
141
+ duplicateShortIds: string[];
142
+ };
105
143
  integrity: {
106
144
  duplicatePhaseIds: string[];
107
145
  danglingPhaseIds: string[];
146
+ duplicateShortIds: string[];
108
147
  };
109
148
  }>;
110
149
  /** Validate plan integrity: globally unique phase ids and resolvable feature.phaseIds. */
111
150
  validateIntegrity(): Promise<{
112
151
  duplicatePhaseIds: string[];
113
152
  danglingPhaseIds: string[];
153
+ duplicateShortIds: string[];
114
154
  }>;
115
155
  private derivePhaseStatus;
116
156
  private deriveFeatureStatus;
117
- syncStatuses(): Promise<void>;
157
+ syncStatuses(): Promise<string[]>;
118
158
  /** Optimized rollup: syncs only the affected phase and its parent feature.
119
- * Drastically reduces write operations and 'busy' window for task updates. */
120
- syncTaskStatusRollup(phaseId: string): Promise<void>;
159
+ * Drastically reduces write operations and 'busy' window for task updates.
160
+ * Returns the composite ref of the phase if its handoff was auto-cleared
161
+ * (phase transitioned to done), else null. */
162
+ syncTaskStatusRollup(phaseId: string): Promise<string | null>;
121
163
  updateProject(updater: (p: Project) => Project): Promise<Project>;
122
164
  updateFeatures(updater: (f: FeaturesDocument) => FeaturesDocument): Promise<FeaturesDocument>;
123
165
  updateRequirements(updater: (r: RequirementsDocument) => RequirementsDocument): Promise<RequirementsDocument>;
124
166
  saveProject(project: Project): Promise<void>;
125
167
  saveFeatures(features: FeaturesDocument): Promise<void>;
168
+ /** Per-file write of all features + orphan reconcile. No lock (caller holds withFeaturesLock). */
169
+ private saveFeaturesRaw;
170
+ /** Granular single-feature write (per-file lock; parallel-safe across features). */
171
+ saveFeature(feature: Feature): Promise<void>;
126
172
  saveRequirements(reqs: RequirementsDocument): Promise<void>;
127
173
  savePhase(phase: Phase): Promise<void>;
128
174
  /** Atomic read-modify-write on a single phase file. Serializes concurrent
129
175
  * task_create / phase_update calls on the SAME phaseId so batch operations
130
176
  * don't lose tasks (last-write-wins race condition). */
131
177
  updatePhase(phaseId: string, updater: (phase: Phase) => Phase): Promise<Phase>;
178
+ /** Get the handoff text for a phase ("" if none). Throws if phase missing. */
179
+ getPhaseHandoff(phaseId: string): Promise<string>;
180
+ /** Set the handoff text for a phase + stamp handoffUpdatedAt. Atomic per-file
181
+ * update via updatePhase (so the web UI refreshes via maybeAutoSync). */
182
+ setPhaseHandoff(phaseId: string, text: string): Promise<void>;
183
+ /** Clear the handoff text for a phase (handoff=""). handoffUpdatedAt is left
184
+ * unchanged as an audit trail (when a handoff last existed). */
185
+ clearPhaseHandoff(phaseId: string): Promise<void>;
186
+ /** List all phases that have a non-empty handoff, newest first, with a
187
+ * human-readable composite ref (P00x or P00x(F00x)) and a first-line excerpt. */
188
+ listHandoffs(): Promise<PhaseHandoffSummary[]>;
132
189
  deletePhase(phaseId: string): Promise<void>;
133
190
  /** Load the full workspace (manifest + phases + project + requirements + features) */
134
191
  loadWorkspace(): Promise<PlanWorkspace>;
@@ -1 +1 @@
1
- {"version":3,"file":"plan-store.d.ts","sourceRoot":"","sources":["../src/plan-store.ts"],"names":[],"mappings":"AAEA,OAAO,EAIL,KAAK,gBAAgB,EAGrB,KAAK,QAAQ,EAEb,KAAK,KAAK,EAEV,KAAK,aAAa,EAElB,KAAK,OAAO,EAEZ,KAAK,oBAAoB,EAIzB,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,WAAW,EACjB,MAAM,aAAa,CAAC;AAOrB,qBAAa,cAAe,SAAQ,KAAK;aAGrB,KAAK,CAAC,EAAE,OAAO;gBAD/B,OAAO,EAAE,MAAM,EACC,KAAK,CAAC,EAAE,OAAO,YAAA;CAKlC;AAeD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC,GAAG,SAAS,GAAG,IAAI,CAElF;AAKD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,GAAG,IAAI,CAEvE;AAaD,wBAAgB,eAAe,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAStF;AAmDD,wBAAsB,cAAc,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAsEpE;AAqBD,qBAAa,SAAS;IACpB,SAAgB,IAAI,EAAE,MAAM,CAAC;IAC7B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,SAAS,CAAS;IAO1B,OAAO,CAAC,eAAe,CAAS;gBAEpB,IAAI,EAAE,MAAM;IAIxB;;4EAEwE;IACxE,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAEpC;;;2BAGuB;YACT,UAAU;IAUxB,2EAA2E;IACrE,oBAAoB,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;YAIjD,aAAa;IAU3B,OAAO,CAAC,cAAc;IAUtB,OAAO,CAAC,yBAAyB;IAUjC,OAAO,CAAC,sBAAsB;IAc9B,OAAO,CAAC,0BAA0B;IAuE5B,uBAAuB,IAAI,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC;IAgB9D,OAAO,CAAC,YAAY;IAGpB,OAAO,CAAC,WAAW;IAGnB,OAAO,CAAC,gBAAgB;IAGxB,OAAO,CAAC,YAAY;IAGpB,OAAO,CAAC,SAAS;IAGjB,OAAO,CAAC,SAAS;IAGjB,OAAO,CAAC,YAAY;IAGpB,OAAO,CAAC,YAAY;IAGpB,OAAO,CAAC,UAAU;IAGlB,OAAO,CAAC,YAAY;IAGpB,OAAO,CAAC,WAAW;IAMb,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAqFxC,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC;IAW1B,YAAY,IAAI,OAAO,CAAC,QAAQ,CAAC;IAIjC,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAM/B,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;IAK1C,YAAY,IAAI,OAAO,CAAC,gBAAgB,CAAC;IASzC,mBAAmB,IAAI,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;IAQtD,mBAAmB,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAM5D,UAAU,IAAI,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAQzC,UAAU,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAMpD;;;;;OAKG;IACG,oBAAoB,CAAC,eAAe,SAAK,GAAG,OAAO,CAAC,MAAM,CAAC;IAkBjE,qCAAqC;IAC/B,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC;IAQvC,kEAAkE;IAC5D,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC;IAQnC,eAAe,IAAI,OAAO,CAAC,WAAW,CAAC;IAQvC,aAAa,IAAI,OAAO,CAAC,OAAO,CAAC;IASjC,WAAW,IAAI,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAkBxF,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAK3C,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IAO9B,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAaxF,0EAA0E;IACpE,aAAa,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,kBAAkB,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAoBhF,gBAAgB,IAAI,OAAO,CAAC,oBAAoB,CAAC;IAQjD,aAAa,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;IA0BjC,OAAO,IAAI,OAAO,CAAC,aAAa,CAAC;IAWvC;4DACwD;IAClD,eAAe,IAAI,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAmEzF;;;;;OAKG;IACG,oBAAoB,IAAI,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IA0B1D,gFAAgF;IAC1E,MAAM,IAAI,OAAO,CAAC;QACtB,QAAQ,EAAE;YAAE,OAAO,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,MAAM,CAAA;SAAE,CAAC;QAClE,SAAS,EAAE;YAAE,iBAAiB,EAAE,MAAM,EAAE,CAAC;YAAC,gBAAgB,EAAE,MAAM,EAAE,CAAA;SAAE,CAAC;KACxE,CAAC;IASF,0FAA0F;IACpF,iBAAiB,IAAI,OAAO,CAAC;QAAE,iBAAiB,EAAE,MAAM,EAAE,CAAC;QAAC,gBAAgB,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IAkB/F,OAAO,CAAC,iBAAiB;IAsBzB,OAAO,CAAC,mBAAmB;IAuBrB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IA6BnC;mFAC+E;IACzE,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAoBpD,aAAa,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAMjE,cAAc,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,gBAAgB,KAAK,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAM7F,kBAAkB,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,oBAAoB,KAAK,oBAAoB,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAM7G,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAO5C,YAAY,CAAC,QAAQ,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAOvD,gBAAgB,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC;IAMzD,SAAS,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ9C;;6DAEyD;IACnD,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;IAM9E,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAWjD,sFAAsF;IAChF,aAAa,IAAI,OAAO,CAAC,aAAa,CAAC;IAW7C,iEAAiE;IAC3D,cAAc,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IA0BzC,qDAAqD;YACvC,aAAa;CAS5B"}
1
+ {"version":3,"file":"plan-store.d.ts","sourceRoot":"","sources":["../src/plan-store.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,7 @@
1
- import { access, copyFile, mkdir, readFile, rename, stat, unlink, writeFile } from "node:fs/promises";
1
+ import { access, copyFile, mkdir, readdir, readFile, rename, stat, unlink, writeFile } from "node:fs/promises";
2
2
  import { join } from "node:path";
3
- import { CodebaseProfileSchema, FeaturesDocumentSchema, ManifestSchema, PhaseSchema, PlanWorkspaceSchema, ProjectSchema, RequirementsDocumentSchema, ResumeFocusSchema, ActivityLogSchema, } from "./schema.js";
4
- import { createFeatureId, createPhaseId, createRequirementId, createTaskId, isLegacyPhaseId } from "./naming.js";
3
+ import { CodebaseProfileSchema, FeatureSchema, FeaturesDocumentSchema, ManifestSchema, PhaseSchema, PlanWorkspaceSchema, ProjectSchema, RequirementsDocumentSchema, ResumeFocusSchema, ActivityLogSchema, } from "./schema.js";
4
+ import { createFeatureId, createPhaseId, createRequirementId, createShortId, createTaskId, formatPhaseRef, isLegacyPhaseId } from "./naming.js";
5
5
  function nowISO() {
6
6
  return new Date().toISOString();
7
7
  }
@@ -108,6 +108,12 @@ async function atomicUpdateJson(path, schema, updater) {
108
108
  return parsed;
109
109
  });
110
110
  }
111
+ /** Extract the first meaningful line of a handoff: skip blank lines, strip a
112
+ * leading markdown header (#), trim, and truncate to ~80 chars. */
113
+ function handoffFirstLine(text) {
114
+ const line = text.trim().split(/\r?\n/).find((l) => l.trim().length > 0) ?? "";
115
+ return line.replace(/^#+\s*/, "").trim().slice(0, 80);
116
+ }
111
117
  export async function migrateToUuids(store) {
112
118
  // Run as a batch so internal saveFeatures/savePhase calls do not
113
119
  // re-trigger syncStatuses (O(N^2) on large planners). Idempotent: if there
@@ -335,7 +341,7 @@ export class PlanStore {
335
341
  }
336
342
  async ensureStructureOrdering() {
337
343
  return this.runAsBatch(async () => {
338
- const featuresDoc = await readJson(this.featuresPath(), FeaturesDocumentSchema).catch(() => ({ features: [] }));
344
+ const featuresDoc = await this.loadFeatures();
339
345
  const phases = await this.loadAllPhases();
340
346
  const normalized = this.normalizeStructureSnapshot(featuresDoc, phases);
341
347
  if (!normalized.changed)
@@ -360,6 +366,35 @@ export class PlanStore {
360
366
  featuresPath() {
361
367
  return join(this.root, "features.json");
362
368
  }
369
+ featuresDir() {
370
+ return join(this.root, "features");
371
+ }
372
+ featurePath(featureId) {
373
+ return join(this.featuresDir(), `${featureId}.json`);
374
+ }
375
+ withFeaturesLock(fn) {
376
+ // Sentinel-keyed mutex (the features dir path) serializing all feature
377
+ // mutations so read-modify-write via updateFeatures is race-free and the
378
+ // one-time legacy→per-file migration never interleaves with a writer.
379
+ return withWriteLock(this.featuresDir(), fn);
380
+ }
381
+ /** Idempotent one-time migration: if a legacy features.json exists, split it
382
+ * into features/<id>.json (one per feature) and remove the legacy file.
383
+ * Must be called under withFeaturesLock. Crash-safe: re-run overwrites. */
384
+ async migrateLegacy() {
385
+ let legacy;
386
+ try {
387
+ legacy = await readJson(this.featuresPath(), FeaturesDocumentSchema);
388
+ }
389
+ catch {
390
+ return; // no legacy features.json (already migrated or fresh project)
391
+ }
392
+ await mkdir(this.featuresDir(), { recursive: true });
393
+ for (const feat of legacy.features) {
394
+ await atomicWriteJson(this.featurePath(feat.id), feat);
395
+ }
396
+ await unlink(this.featuresPath()).catch(() => { });
397
+ }
363
398
  phasesDir() {
364
399
  return join(this.root, "phases");
365
400
  }
@@ -388,6 +423,7 @@ export class PlanStore {
388
423
  }
389
424
  await mkdir(this.root, { recursive: true });
390
425
  await mkdir(this.phasesDir(), { recursive: true });
426
+ await mkdir(this.featuresDir(), { recursive: true });
391
427
  await mkdir(join(this.generatedDir(), "phases"), { recursive: true });
392
428
  await mkdir(join(this.root, "schema"), { recursive: true });
393
429
  await mkdir(join(this.root, "adapters"), { recursive: true });
@@ -478,9 +514,33 @@ export class PlanStore {
478
514
  return this.normalizePhaseDocument(phase).phase;
479
515
  }
480
516
  async loadFeatures() {
517
+ // Per-file layout: read features/<id>.json (one file per feature).
518
+ let jsonFiles = [];
481
519
  try {
482
- const features = await readJson(this.featuresPath(), FeaturesDocumentSchema);
483
- return this.normalizeFeaturesDocument(features).doc;
520
+ const all = await readdir(this.featuresDir());
521
+ jsonFiles = all.filter((f) => f.endsWith(".json"));
522
+ }
523
+ catch {
524
+ // features/ absent → fall through to legacy single-file layout
525
+ }
526
+ if (jsonFiles.length > 0) {
527
+ const features = [];
528
+ for (const f of jsonFiles) {
529
+ const id = f.replace(/\.json$/, "");
530
+ try {
531
+ features.push(await readJson(this.featurePath(id), FeatureSchema));
532
+ }
533
+ catch (err) {
534
+ // Skip an invalid feature file rather than failing the whole load.
535
+ console.warn(`[plan-store] skipping invalid feature file ${f}:`, err);
536
+ }
537
+ }
538
+ return this.normalizeFeaturesDocument({ features }).doc;
539
+ }
540
+ // Legacy: single features.json (pre-migration read; migration writes on first write op).
541
+ try {
542
+ const legacy = await readJson(this.featuresPath(), FeaturesDocumentSchema);
543
+ return this.normalizeFeaturesDocument(legacy).doc;
484
544
  }
485
545
  catch {
486
546
  return { features: [] };
@@ -795,13 +855,156 @@ export class PlanStore {
795
855
  catch { /* best-effort */ }
796
856
  return { removed };
797
857
  }
858
+ /** All non-empty shortIds currently assigned in the project (features + phases + tasks).
859
+ * Read-only; used by createShortId collision guard during entity creation. */
860
+ async assignedShortIds() {
861
+ const features = await this.loadFeatures();
862
+ const phases = await this.loadAllPhases();
863
+ const ids = new Set();
864
+ for (const f of features.features)
865
+ if (f.shortId)
866
+ ids.add(f.shortId);
867
+ for (const p of phases) {
868
+ if (p.shortId)
869
+ ids.add(p.shortId);
870
+ for (const t of p.tasks)
871
+ if (t.shortId)
872
+ ids.add(t.shortId);
873
+ }
874
+ return ids;
875
+ }
876
+ /** Next priority (>=1) for a new entity within its scope.
877
+ * - feature: max priority among features + 1
878
+ * - phase: max priority among phases of parentId (featureId) + 1
879
+ * - task: max priority among tasks of parentId (phaseId) + 1 */
880
+ async nextPriority(kind, parentId) {
881
+ if (kind === "feature") {
882
+ const features = await this.loadFeatures();
883
+ const max = features.features.reduce((m, f) => Math.max(m, f.priority ?? 0), 0);
884
+ return max + 1;
885
+ }
886
+ if (kind === "phase") {
887
+ const phases = await this.loadAllPhases();
888
+ const siblings = phases.filter((p) => p.featureId === parentId);
889
+ const max = siblings.reduce((m, p) => Math.max(m, p.priority ?? 0), 0);
890
+ return max + 1;
891
+ }
892
+ // task
893
+ const phase = parentId ? await this.loadPhase(parentId).catch(() => undefined) : undefined;
894
+ const tasks = phase?.tasks ?? [];
895
+ const max = tasks.reduce((m, t) => Math.max(m, t.priority ?? 0), 0);
896
+ return max + 1;
897
+ }
898
+ /** Idempotent backfill of shortId (globally-unique 5-char Crockford) and priority
899
+ * (per-scope display order). Assigns missing shortIds and priorities; never overwrites
900
+ * existing non-empty shortIds or non-zero priorities. Safe to run at startup. */
901
+ async ensureShortIdsAndPriority() {
902
+ return this.runAsBatch(async () => {
903
+ const featuresDoc = await this.loadFeatures();
904
+ const phases = await this.loadAllPhases();
905
+ const existing = new Set();
906
+ for (const f of featuresDoc.features)
907
+ if (f.shortId)
908
+ existing.add(f.shortId);
909
+ for (const p of phases) {
910
+ if (p.shortId)
911
+ existing.add(p.shortId);
912
+ for (const t of p.tasks)
913
+ if (t.shortId)
914
+ existing.add(t.shortId);
915
+ }
916
+ let shortIdsAssigned = 0;
917
+ let prioritiesAssigned = 0;
918
+ let featuresDirty = false;
919
+ const assignPriority = (current, index) => {
920
+ if (current === 0) {
921
+ prioritiesAssigned += 1;
922
+ return index + 1;
923
+ }
924
+ return current;
925
+ };
926
+ // Features: shortId + priority (project scope)
927
+ const sortedFeatures = [...featuresDoc.features].sort((a, b) => a.number - b.number || a.createdAt.localeCompare(b.createdAt));
928
+ sortedFeatures.forEach((f, index) => {
929
+ if (!f.shortId) {
930
+ f.shortId = createShortId(existing);
931
+ existing.add(f.shortId);
932
+ shortIdsAssigned += 1;
933
+ featuresDirty = true;
934
+ }
935
+ const nextP = assignPriority(f.priority ?? 0, index);
936
+ if (nextP !== f.priority) {
937
+ f.priority = nextP;
938
+ featuresDirty = true;
939
+ }
940
+ });
941
+ if (featuresDirty) {
942
+ featuresDoc.features.sort((a, b) => a.priority - b.priority || a.number - b.number);
943
+ await this.saveFeatures(featuresDoc);
944
+ }
945
+ // Phases + tasks
946
+ for (const phase of phases) {
947
+ let phaseDirty = false;
948
+ if (!phase.shortId) {
949
+ phase.shortId = createShortId(existing);
950
+ existing.add(phase.shortId);
951
+ shortIdsAssigned += 1;
952
+ phaseDirty = true;
953
+ }
954
+ const phaseIndex = phase.number - 1; // stable pre-migration order
955
+ const nextPP = assignPriority(phase.priority ?? 0, phaseIndex < 0 ? 0 : phaseIndex);
956
+ if (nextPP !== phase.priority) {
957
+ phase.priority = nextPP;
958
+ phaseDirty = true;
959
+ }
960
+ const sortedTasks = [...phase.tasks].sort((a, b) => a.number - b.number || a.createdAt.localeCompare(b.createdAt));
961
+ sortedTasks.forEach((t, index) => {
962
+ if (!t.shortId) {
963
+ t.shortId = createShortId(existing);
964
+ existing.add(t.shortId);
965
+ shortIdsAssigned += 1;
966
+ phaseDirty = true;
967
+ }
968
+ const nextTP = assignPriority(t.priority ?? 0, index);
969
+ if (nextTP !== t.priority) {
970
+ t.priority = nextTP;
971
+ phaseDirty = true;
972
+ }
973
+ });
974
+ if (phaseDirty) {
975
+ phase.tasks.sort((a, b) => a.priority - b.priority || a.number - b.number);
976
+ phase.taskIds = phase.tasks.map((t) => t.id);
977
+ phase.updatedAt = nowISO();
978
+ await this.savePhase(phase);
979
+ }
980
+ }
981
+ // Duplicate shortId report (across all entities)
982
+ const allShortIds = [];
983
+ for (const f of featuresDoc.features)
984
+ if (f.shortId)
985
+ allShortIds.push(f.shortId);
986
+ for (const ph of phases) {
987
+ if (ph.shortId)
988
+ allShortIds.push(ph.shortId);
989
+ for (const t of ph.tasks)
990
+ if (t.shortId)
991
+ allShortIds.push(t.shortId);
992
+ }
993
+ const counts = new Map();
994
+ for (const id of allShortIds)
995
+ counts.set(id, (counts.get(id) ?? 0) + 1);
996
+ const duplicateShortIds = [...counts.entries()].filter(([, c]) => c > 1).map(([id]) => id);
997
+ return { shortIdsAssigned, prioritiesAssigned, duplicateShortIds };
998
+ });
999
+ }
798
1000
  /** Repair dangling references and report integrity. One-shot maintenance op. */
799
1001
  async repair() {
800
1002
  return this.runAsBatch(async () => {
801
1003
  const migrated = await this.migratePhaseIds();
1004
+ const backfill = await this.ensureShortIdsAndPriority();
802
1005
  const integrity = await this.validateIntegrity();
803
1006
  await this.writeGenerated();
804
- return { migrated, integrity };
1007
+ return { migrated, backfill, integrity };
805
1008
  });
806
1009
  }
807
1010
  /** Validate plan integrity: globally unique phase ids and resolvable feature.phaseIds. */
@@ -821,7 +1024,23 @@ export class PlanStore {
821
1024
  danglingPhaseIds.push(`${feature.id} -> ${ref}`);
822
1025
  }
823
1026
  }
824
- return { duplicatePhaseIds, danglingPhaseIds };
1027
+ // Duplicate shortIds across all entities
1028
+ const allShortIds = [];
1029
+ for (const f of features.features)
1030
+ if (f.shortId)
1031
+ allShortIds.push(f.shortId);
1032
+ for (const ph of phases) {
1033
+ if (ph.shortId)
1034
+ allShortIds.push(ph.shortId);
1035
+ for (const t of ph.tasks)
1036
+ if (t.shortId)
1037
+ allShortIds.push(t.shortId);
1038
+ }
1039
+ const sidCounts = new Map();
1040
+ for (const id of allShortIds)
1041
+ sidCounts.set(id, (sidCounts.get(id) ?? 0) + 1);
1042
+ const duplicateShortIds = [...sidCounts.entries()].filter(([, c]) => c > 1).map(([id]) => id);
1043
+ return { duplicatePhaseIds, danglingPhaseIds, duplicateShortIds };
825
1044
  }
826
1045
  derivePhaseStatus(phase) {
827
1046
  if (phase.tasks.length === 0)
@@ -881,13 +1100,24 @@ export class PlanStore {
881
1100
  async syncStatuses() {
882
1101
  // Run as a batch so the internal saveFeatures + N savePhase calls do not
883
1102
  // re-trigger syncStatuses on every write (O(N^2) on large planners).
1103
+ // Returns the composite refs of phases whose handoff was auto-cleared
1104
+ // (phase transitioned to done).
1105
+ const cleared = [];
884
1106
  await this.runAsBatch(async () => {
885
1107
  await this.migratePhaseIds();
886
1108
  const workspace = await this.loadAll();
887
1109
  const { phases, features } = workspace;
888
- // 1. Update Phase statuses based on tasks
1110
+ const featNum = new Map(features.features.map((f) => [f.id, f.number]));
1111
+ // 1. Update Phase statuses based on tasks; auto-clear handoff when a
1112
+ // phase transitions to done (completed phases don't keep stale
1113
+ // handoffs). handoffUpdatedAt is kept as an audit trail.
889
1114
  for (const phase of phases) {
1115
+ const was = phase.status;
890
1116
  phase.status = this.derivePhaseStatus(phase);
1117
+ if (phase.status === "done" && was !== "done" && phase.handoff) {
1118
+ phase.handoff = "";
1119
+ cleared.push(formatPhaseRef(phase.number, featNum.get(phase.featureId ?? "")));
1120
+ }
891
1121
  }
892
1122
  // 2. Update Feature statuses based on phases
893
1123
  for (const feature of features.features) {
@@ -901,16 +1131,25 @@ export class PlanStore {
901
1131
  // 4. Refresh resume focus so a subentrating agent sees current state
902
1132
  await this.refreshResume();
903
1133
  });
1134
+ return cleared;
904
1135
  }
905
1136
  /** Optimized rollup: syncs only the affected phase and its parent feature.
906
- * Drastically reduces write operations and 'busy' window for task updates. */
1137
+ * Drastically reduces write operations and 'busy' window for task updates.
1138
+ * Returns the composite ref of the phase if its handoff was auto-cleared
1139
+ * (phase transitioned to done), else null. */
907
1140
  async syncTaskStatusRollup(phaseId) {
908
1141
  const phase = await this.loadPhase(phaseId);
1142
+ const was = phase.status;
909
1143
  phase.status = this.derivePhaseStatus(phase);
1144
+ // Auto-clear handoff when the phase transitions to done.
1145
+ const cleared = phase.status === "done" && was !== "done" && phase.handoff !== "";
1146
+ if (cleared)
1147
+ phase.handoff = "";
910
1148
  await this.savePhase(phase);
1149
+ let feature;
911
1150
  if (phase.featureId) {
912
1151
  const featuresDoc = await this.loadFeatures();
913
- const feature = featuresDoc.features.find((f) => f.id === phase.featureId);
1152
+ feature = featuresDoc.features.find((f) => f.id === phase.featureId);
914
1153
  if (feature) {
915
1154
  // To derive feature status, we still need the statuses of all its phases
916
1155
  const allPhases = await this.loadAllPhases();
@@ -919,6 +1158,7 @@ export class PlanStore {
919
1158
  }
920
1159
  }
921
1160
  await this.refreshResume();
1161
+ return cleared ? formatPhaseRef(phase.number, feature?.number) : null;
922
1162
  }
923
1163
  // ── Savers ───────────────────────────────────────────────────────────
924
1164
  async updateProject(updater) {
@@ -927,7 +1167,13 @@ export class PlanStore {
927
1167
  return updated;
928
1168
  }
929
1169
  async updateFeatures(updater) {
930
- const updated = await atomicUpdateJson(this.featuresPath(), FeaturesDocumentSchema, (current) => this.normalizeFeaturesDocument(updater(current)).doc);
1170
+ const updated = await this.withFeaturesLock(async () => {
1171
+ await this.migrateLegacy();
1172
+ const current = await this.loadFeatures();
1173
+ const upd = this.normalizeFeaturesDocument(updater(current)).doc;
1174
+ await this.saveFeaturesRaw(upd);
1175
+ return upd;
1176
+ });
931
1177
  await this.maybeAutoSync();
932
1178
  return updated;
933
1179
  }
@@ -943,8 +1189,45 @@ export class PlanStore {
943
1189
  await this.maybeAutoSync();
944
1190
  }
945
1191
  async saveFeatures(features) {
1192
+ await this.withFeaturesLock(async () => {
1193
+ await this.migrateLegacy();
1194
+ await this.saveFeaturesRaw(features);
1195
+ });
1196
+ await this.touchManifest();
1197
+ await this.maybeAutoSync();
1198
+ }
1199
+ /** Per-file write of all features + orphan reconcile. No lock (caller holds withFeaturesLock). */
1200
+ async saveFeaturesRaw(features) {
946
1201
  const parsed = FeaturesDocumentSchema.parse(this.normalizeFeaturesDocument(features).doc);
947
- await atomicWriteJson(this.featuresPath(), parsed);
1202
+ await mkdir(this.featuresDir(), { recursive: true });
1203
+ const wantIds = new Set(parsed.features.map((f) => f.id));
1204
+ for (const feat of parsed.features) {
1205
+ await atomicWriteJson(this.featurePath(feat.id), feat);
1206
+ }
1207
+ // Orphan reconcile: remove feature files no longer in the document.
1208
+ try {
1209
+ const files = await readdir(this.featuresDir());
1210
+ for (const f of files) {
1211
+ if (!f.endsWith(".json"))
1212
+ continue;
1213
+ const id = f.replace(/\.json$/, "");
1214
+ if (!wantIds.has(id)) {
1215
+ await unlink(this.featurePath(id)).catch(() => { });
1216
+ }
1217
+ }
1218
+ }
1219
+ catch {
1220
+ // dir absent — nothing to reconcile
1221
+ }
1222
+ }
1223
+ /** Granular single-feature write (per-file lock; parallel-safe across features). */
1224
+ async saveFeature(feature) {
1225
+ await this.withFeaturesLock(async () => {
1226
+ await this.migrateLegacy();
1227
+ await mkdir(this.featuresDir(), { recursive: true });
1228
+ const parsed = FeatureSchema.parse(feature);
1229
+ await atomicWriteJson(this.featurePath(parsed.id), parsed);
1230
+ });
948
1231
  await this.touchManifest();
949
1232
  await this.maybeAutoSync();
950
1233
  }
@@ -968,6 +1251,45 @@ export class PlanStore {
968
1251
  await this.maybeAutoSync();
969
1252
  return updated;
970
1253
  }
1254
+ // ── Phase-scoped handoff (entity field, harness-agnostic) ────────────
1255
+ /** Get the handoff text for a phase ("" if none). Throws if phase missing. */
1256
+ async getPhaseHandoff(phaseId) {
1257
+ return (await this.loadPhase(phaseId)).handoff;
1258
+ }
1259
+ /** Set the handoff text for a phase + stamp handoffUpdatedAt. Atomic per-file
1260
+ * update via updatePhase (so the web UI refreshes via maybeAutoSync). */
1261
+ async setPhaseHandoff(phaseId, text) {
1262
+ const now = new Date().toISOString();
1263
+ await this.updatePhase(phaseId, (phase) => ({ ...phase, handoff: text, handoffUpdatedAt: now }));
1264
+ }
1265
+ /** Clear the handoff text for a phase (handoff=""). handoffUpdatedAt is left
1266
+ * unchanged as an audit trail (when a handoff last existed). */
1267
+ async clearPhaseHandoff(phaseId) {
1268
+ await this.updatePhase(phaseId, (phase) => ({ ...phase, handoff: "" }));
1269
+ }
1270
+ /** List all phases that have a non-empty handoff, newest first, with a
1271
+ * human-readable composite ref (P00x or P00x(F00x)) and a first-line excerpt. */
1272
+ async listHandoffs() {
1273
+ const phases = await this.loadAllPhases();
1274
+ const features = await this.loadFeatures();
1275
+ const featureNumber = new Map();
1276
+ for (const f of features.features)
1277
+ featureNumber.set(f.id, f.number);
1278
+ const out = [];
1279
+ for (const p of phases) {
1280
+ if (!p.handoff)
1281
+ continue;
1282
+ const fnum = p.featureId ? featureNumber.get(p.featureId) : undefined;
1283
+ out.push({
1284
+ phaseId: p.id,
1285
+ compositeRef: formatPhaseRef(p.number, fnum),
1286
+ updatedAt: p.handoffUpdatedAt || p.updatedAt,
1287
+ firstLine: handoffFirstLine(p.handoff),
1288
+ });
1289
+ }
1290
+ out.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
1291
+ return out;
1292
+ }
971
1293
  async deletePhase(phaseId) {
972
1294
  try {
973
1295
  await unlink(this.phasePath(phaseId));
package/dist/refs.d.ts ADDED
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Harness-agnostic reference resolution for planner entities.
3
+ *
4
+ * Used by adapters (pi-adapter, plan-mcp) and any future harness to resolve a
5
+ * human-facing ref to a concrete entity. Kept in @agent-plan/core so every
6
+ * adapter shares the exact same resolution semantics.
7
+ *
8
+ * Supported phase refs (case-insensitive):
9
+ * - UUID: "bd6ed366-..." -> phase.id
10
+ * - Short: "P001" | "p1" -> phase.number (globally unique)
11
+ * - Compos: "P002(F001)" -> phase.number with parent feature validation
12
+ * - Title: exact match, then includes (backward-compat fallback)
13
+ */
14
+ import type { Phase, Feature } from "./schema.js";
15
+ /**
16
+ * Resolve a phase reference to a Phase. Returns `undefined` when not found or
17
+ * when a composite (F00x) parent does not match the phase's featureId.
18
+ *
19
+ * @param phases all phases (st.loadAllPhases())
20
+ * @param features all features (st.loadAllFeatures() / loadFeatures())
21
+ * @param ref the human-facing ref (P00x / P00x(F00x) / UUID / title)
22
+ */
23
+ export declare function findPhaseByRef(phases: Phase[], features: Feature[], ref: string): Phase | undefined;
24
+ //# sourceMappingURL=refs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refs.d.ts","sourceRoot":"","sources":["../src/refs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAKlD;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,KAAK,EAAE,EACf,QAAQ,EAAE,OAAO,EAAE,EACnB,GAAG,EAAE,MAAM,GACV,KAAK,GAAG,SAAS,CA2BnB"}
package/dist/refs.js ADDED
@@ -0,0 +1,37 @@
1
+ // P00x or P00x(F00x) — accept 1+ digits so "p1" == "p001".
2
+ const PHASE_REF_RE = /^p(\d+)(?:\(f(\d+)\))?$/;
3
+ /**
4
+ * Resolve a phase reference to a Phase. Returns `undefined` when not found or
5
+ * when a composite (F00x) parent does not match the phase's featureId.
6
+ *
7
+ * @param phases all phases (st.loadAllPhases())
8
+ * @param features all features (st.loadAllFeatures() / loadFeatures())
9
+ * @param ref the human-facing ref (P00x / P00x(F00x) / UUID / title)
10
+ */
11
+ export function findPhaseByRef(phases, features, ref) {
12
+ const normalized = ref.trim().toLowerCase();
13
+ if (!normalized)
14
+ return undefined;
15
+ // 1. UUID (exact)
16
+ let found = phases.find((p) => p.id.toLowerCase() === normalized);
17
+ if (found)
18
+ return found;
19
+ // 2. Composite / short ref: P00x or P00x(F00x)
20
+ const m = normalized.match(PHASE_REF_RE);
21
+ if (m) {
22
+ const phaseNum = parseInt(m[1], 10);
23
+ found = phases.find((p) => p.number === phaseNum);
24
+ if (found && m[2]) {
25
+ // validate parent feature when the (F00x) disambiguator is present
26
+ const featureNum = parseInt(m[2], 10);
27
+ const feat = features.find((f) => f.number === featureNum);
28
+ if (!feat || found.featureId !== feat.id)
29
+ return undefined;
30
+ }
31
+ if (found)
32
+ return found;
33
+ }
34
+ // 3. Title fallback (backward compatibility with agents using titles)
35
+ return (phases.find((p) => p.title.toLowerCase() === normalized) ??
36
+ phases.find((p) => p.title.toLowerCase().includes(normalized)));
37
+ }
package/dist/schema.d.ts CHANGED
@@ -442,6 +442,8 @@ export declare const TaskSchema: z.ZodEffects<z.ZodObject<{
442
442
  id: z.ZodString;
443
443
  phaseId: z.ZodString;
444
444
  number: z.ZodDefault<z.ZodNumber>;
445
+ shortId: z.ZodDefault<z.ZodString>;
446
+ priority: z.ZodDefault<z.ZodNumber>;
445
447
  shortName: z.ZodString;
446
448
  title: z.ZodString;
447
449
  status: z.ZodEnum<["planned", "in-progress", "done", "blocked", "canceled", "rejected", "deferred", "waiting"]>;
@@ -551,6 +553,8 @@ export declare const TaskSchema: z.ZodEffects<z.ZodObject<{
551
553
  acceptedAt: string;
552
554
  }[];
553
555
  phaseId: string;
556
+ shortId: string;
557
+ priority: number;
554
558
  shortName: string;
555
559
  statusLog: {
556
560
  id: string;
@@ -596,6 +600,8 @@ export declare const TaskSchema: z.ZodEffects<z.ZodObject<{
596
600
  rationale?: string | undefined;
597
601
  implementationNotes?: string | undefined;
598
602
  }[] | undefined;
603
+ shortId?: string | undefined;
604
+ priority?: number | undefined;
599
605
  statusLog?: {
600
606
  id: string;
601
607
  date: string;
@@ -644,6 +650,8 @@ export declare const TaskSchema: z.ZodEffects<z.ZodObject<{
644
650
  acceptedAt: string;
645
651
  }[];
646
652
  phaseId: string;
653
+ shortId: string;
654
+ priority: number;
647
655
  shortName: string;
648
656
  statusLog: {
649
657
  id: string;
@@ -684,6 +692,8 @@ export declare const TaskSchema: z.ZodEffects<z.ZodObject<{
684
692
  rationale?: string | undefined;
685
693
  implementationNotes?: string | undefined;
686
694
  }[] | undefined;
695
+ shortId?: string | undefined;
696
+ priority?: number | undefined;
687
697
  statusLog?: {
688
698
  id: string;
689
699
  date: string;
@@ -713,6 +723,8 @@ export declare const PhaseSchema: z.ZodObject<{
713
723
  id: z.ZodString;
714
724
  featureId: z.ZodOptional<z.ZodString>;
715
725
  number: z.ZodNumber;
726
+ shortId: z.ZodDefault<z.ZodString>;
727
+ priority: z.ZodDefault<z.ZodNumber>;
716
728
  slug: z.ZodString;
717
729
  title: z.ZodString;
718
730
  status: z.ZodEnum<["draft", "discovery", "planned", "in-progress", "done", "blocked", "canceled", "rejected", "deferred", "waiting"]>;
@@ -757,6 +769,8 @@ export declare const PhaseSchema: z.ZodObject<{
757
769
  id: z.ZodString;
758
770
  phaseId: z.ZodString;
759
771
  number: z.ZodDefault<z.ZodNumber>;
772
+ shortId: z.ZodDefault<z.ZodString>;
773
+ priority: z.ZodDefault<z.ZodNumber>;
760
774
  shortName: z.ZodString;
761
775
  title: z.ZodString;
762
776
  status: z.ZodEnum<["planned", "in-progress", "done", "blocked", "canceled", "rejected", "deferred", "waiting"]>;
@@ -866,6 +880,8 @@ export declare const PhaseSchema: z.ZodObject<{
866
880
  acceptedAt: string;
867
881
  }[];
868
882
  phaseId: string;
883
+ shortId: string;
884
+ priority: number;
869
885
  shortName: string;
870
886
  statusLog: {
871
887
  id: string;
@@ -911,6 +927,8 @@ export declare const PhaseSchema: z.ZodObject<{
911
927
  rationale?: string | undefined;
912
928
  implementationNotes?: string | undefined;
913
929
  }[] | undefined;
930
+ shortId?: string | undefined;
931
+ priority?: number | undefined;
914
932
  statusLog?: {
915
933
  id: string;
916
934
  date: string;
@@ -959,6 +977,8 @@ export declare const PhaseSchema: z.ZodObject<{
959
977
  acceptedAt: string;
960
978
  }[];
961
979
  phaseId: string;
980
+ shortId: string;
981
+ priority: number;
962
982
  shortName: string;
963
983
  statusLog: {
964
984
  id: string;
@@ -999,6 +1019,8 @@ export declare const PhaseSchema: z.ZodObject<{
999
1019
  rationale?: string | undefined;
1000
1020
  implementationNotes?: string | undefined;
1001
1021
  }[] | undefined;
1022
+ shortId?: string | undefined;
1023
+ priority?: number | undefined;
1002
1024
  statusLog?: {
1003
1025
  id: string;
1004
1026
  date: string;
@@ -1026,6 +1048,8 @@ export declare const PhaseSchema: z.ZodObject<{
1026
1048
  }>, "many">>;
1027
1049
  createdAt: z.ZodString;
1028
1050
  updatedAt: z.ZodString;
1051
+ handoff: z.ZodDefault<z.ZodString>;
1052
+ handoffUpdatedAt: z.ZodDefault<z.ZodString>;
1029
1053
  }, "strip", z.ZodTypeAny, {
1030
1054
  number: number;
1031
1055
  status: "planned" | "in-progress" | "done" | "blocked" | "canceled" | "rejected" | "deferred" | "waiting" | "draft" | "discovery";
@@ -1046,6 +1070,8 @@ export declare const PhaseSchema: z.ZodObject<{
1046
1070
  implementationNotes: string;
1047
1071
  acceptedAt: string;
1048
1072
  }[];
1073
+ shortId: string;
1074
+ priority: number;
1049
1075
  dependsOn: string[];
1050
1076
  slug: string;
1051
1077
  discussedAt: string;
@@ -1081,6 +1107,8 @@ export declare const PhaseSchema: z.ZodObject<{
1081
1107
  acceptedAt: string;
1082
1108
  }[];
1083
1109
  phaseId: string;
1110
+ shortId: string;
1111
+ priority: number;
1084
1112
  shortName: string;
1085
1113
  statusLog: {
1086
1114
  id: string;
@@ -1102,6 +1130,8 @@ export declare const PhaseSchema: z.ZodObject<{
1102
1130
  startedAt: string;
1103
1131
  completedAt: string;
1104
1132
  }[];
1133
+ handoff: string;
1134
+ handoffUpdatedAt: string;
1105
1135
  featureId?: string | undefined;
1106
1136
  }, {
1107
1137
  number: number;
@@ -1124,6 +1154,8 @@ export declare const PhaseSchema: z.ZodObject<{
1124
1154
  rationale?: string | undefined;
1125
1155
  implementationNotes?: string | undefined;
1126
1156
  }[] | undefined;
1157
+ shortId?: string | undefined;
1158
+ priority?: number | undefined;
1127
1159
  dependsOn?: string[] | undefined;
1128
1160
  featureId?: string | undefined;
1129
1161
  discussedAt?: string | undefined;
@@ -1155,6 +1187,8 @@ export declare const PhaseSchema: z.ZodObject<{
1155
1187
  rationale?: string | undefined;
1156
1188
  implementationNotes?: string | undefined;
1157
1189
  }[] | undefined;
1190
+ shortId?: string | undefined;
1191
+ priority?: number | undefined;
1158
1192
  statusLog?: {
1159
1193
  id: string;
1160
1194
  date: string;
@@ -1180,10 +1214,14 @@ export declare const PhaseSchema: z.ZodObject<{
1180
1214
  startedAt?: string | undefined;
1181
1215
  completedAt?: string | undefined;
1182
1216
  }[] | undefined;
1217
+ handoff?: string | undefined;
1218
+ handoffUpdatedAt?: string | undefined;
1183
1219
  }>;
1184
1220
  export declare const FeatureSchema: z.ZodObject<{
1185
1221
  id: z.ZodString;
1186
1222
  number: z.ZodDefault<z.ZodNumber>;
1223
+ shortId: z.ZodDefault<z.ZodString>;
1224
+ priority: z.ZodDefault<z.ZodNumber>;
1187
1225
  name: z.ZodString;
1188
1226
  description: z.ZodDefault<z.ZodString>;
1189
1227
  status: z.ZodEnum<["planned", "in-progress", "done", "blocked", "canceled", "rejected", "deferred", "waiting"]>;
@@ -1236,6 +1274,8 @@ export declare const FeatureSchema: z.ZodObject<{
1236
1274
  implementationNotes: string;
1237
1275
  acceptedAt: string;
1238
1276
  }[];
1277
+ shortId: string;
1278
+ priority: number;
1239
1279
  dependsOn: string[];
1240
1280
  discussedAt: string;
1241
1281
  contextReady: boolean;
@@ -1261,6 +1301,8 @@ export declare const FeatureSchema: z.ZodObject<{
1261
1301
  rationale?: string | undefined;
1262
1302
  implementationNotes?: string | undefined;
1263
1303
  }[] | undefined;
1304
+ shortId?: string | undefined;
1305
+ priority?: number | undefined;
1264
1306
  dependsOn?: string[] | undefined;
1265
1307
  discussedAt?: string | undefined;
1266
1308
  contextReady?: boolean | undefined;
@@ -1275,6 +1317,8 @@ export declare const FeaturesDocumentSchema: z.ZodObject<{
1275
1317
  features: z.ZodArray<z.ZodObject<{
1276
1318
  id: z.ZodString;
1277
1319
  number: z.ZodDefault<z.ZodNumber>;
1320
+ shortId: z.ZodDefault<z.ZodString>;
1321
+ priority: z.ZodDefault<z.ZodNumber>;
1278
1322
  name: z.ZodString;
1279
1323
  description: z.ZodDefault<z.ZodString>;
1280
1324
  status: z.ZodEnum<["planned", "in-progress", "done", "blocked", "canceled", "rejected", "deferred", "waiting"]>;
@@ -1327,6 +1371,8 @@ export declare const FeaturesDocumentSchema: z.ZodObject<{
1327
1371
  implementationNotes: string;
1328
1372
  acceptedAt: string;
1329
1373
  }[];
1374
+ shortId: string;
1375
+ priority: number;
1330
1376
  dependsOn: string[];
1331
1377
  discussedAt: string;
1332
1378
  contextReady: boolean;
@@ -1352,6 +1398,8 @@ export declare const FeaturesDocumentSchema: z.ZodObject<{
1352
1398
  rationale?: string | undefined;
1353
1399
  implementationNotes?: string | undefined;
1354
1400
  }[] | undefined;
1401
+ shortId?: string | undefined;
1402
+ priority?: number | undefined;
1355
1403
  dependsOn?: string[] | undefined;
1356
1404
  discussedAt?: string | undefined;
1357
1405
  contextReady?: boolean | undefined;
@@ -1379,6 +1427,8 @@ export declare const FeaturesDocumentSchema: z.ZodObject<{
1379
1427
  implementationNotes: string;
1380
1428
  acceptedAt: string;
1381
1429
  }[];
1430
+ shortId: string;
1431
+ priority: number;
1382
1432
  dependsOn: string[];
1383
1433
  discussedAt: string;
1384
1434
  contextReady: boolean;
@@ -1406,6 +1456,8 @@ export declare const FeaturesDocumentSchema: z.ZodObject<{
1406
1456
  rationale?: string | undefined;
1407
1457
  implementationNotes?: string | undefined;
1408
1458
  }[] | undefined;
1459
+ shortId?: string | undefined;
1460
+ priority?: number | undefined;
1409
1461
  dependsOn?: string[] | undefined;
1410
1462
  discussedAt?: string | undefined;
1411
1463
  contextReady?: boolean | undefined;
@@ -1728,6 +1780,8 @@ export declare const PlanWorkspaceSchema: z.ZodObject<{
1728
1780
  features: z.ZodArray<z.ZodObject<{
1729
1781
  id: z.ZodString;
1730
1782
  number: z.ZodDefault<z.ZodNumber>;
1783
+ shortId: z.ZodDefault<z.ZodString>;
1784
+ priority: z.ZodDefault<z.ZodNumber>;
1731
1785
  name: z.ZodString;
1732
1786
  description: z.ZodDefault<z.ZodString>;
1733
1787
  status: z.ZodEnum<["planned", "in-progress", "done", "blocked", "canceled", "rejected", "deferred", "waiting"]>;
@@ -1780,6 +1834,8 @@ export declare const PlanWorkspaceSchema: z.ZodObject<{
1780
1834
  implementationNotes: string;
1781
1835
  acceptedAt: string;
1782
1836
  }[];
1837
+ shortId: string;
1838
+ priority: number;
1783
1839
  dependsOn: string[];
1784
1840
  discussedAt: string;
1785
1841
  contextReady: boolean;
@@ -1805,6 +1861,8 @@ export declare const PlanWorkspaceSchema: z.ZodObject<{
1805
1861
  rationale?: string | undefined;
1806
1862
  implementationNotes?: string | undefined;
1807
1863
  }[] | undefined;
1864
+ shortId?: string | undefined;
1865
+ priority?: number | undefined;
1808
1866
  dependsOn?: string[] | undefined;
1809
1867
  discussedAt?: string | undefined;
1810
1868
  contextReady?: boolean | undefined;
@@ -1832,6 +1890,8 @@ export declare const PlanWorkspaceSchema: z.ZodObject<{
1832
1890
  implementationNotes: string;
1833
1891
  acceptedAt: string;
1834
1892
  }[];
1893
+ shortId: string;
1894
+ priority: number;
1835
1895
  dependsOn: string[];
1836
1896
  discussedAt: string;
1837
1897
  contextReady: boolean;
@@ -1859,6 +1919,8 @@ export declare const PlanWorkspaceSchema: z.ZodObject<{
1859
1919
  rationale?: string | undefined;
1860
1920
  implementationNotes?: string | undefined;
1861
1921
  }[] | undefined;
1922
+ shortId?: string | undefined;
1923
+ priority?: number | undefined;
1862
1924
  dependsOn?: string[] | undefined;
1863
1925
  discussedAt?: string | undefined;
1864
1926
  contextReady?: boolean | undefined;
@@ -1975,6 +2037,8 @@ export declare const PlanWorkspaceSchema: z.ZodObject<{
1975
2037
  id: z.ZodString;
1976
2038
  featureId: z.ZodOptional<z.ZodString>;
1977
2039
  number: z.ZodNumber;
2040
+ shortId: z.ZodDefault<z.ZodString>;
2041
+ priority: z.ZodDefault<z.ZodNumber>;
1978
2042
  slug: z.ZodString;
1979
2043
  title: z.ZodString;
1980
2044
  status: z.ZodEnum<["draft", "discovery", "planned", "in-progress", "done", "blocked", "canceled", "rejected", "deferred", "waiting"]>;
@@ -2019,6 +2083,8 @@ export declare const PlanWorkspaceSchema: z.ZodObject<{
2019
2083
  id: z.ZodString;
2020
2084
  phaseId: z.ZodString;
2021
2085
  number: z.ZodDefault<z.ZodNumber>;
2086
+ shortId: z.ZodDefault<z.ZodString>;
2087
+ priority: z.ZodDefault<z.ZodNumber>;
2022
2088
  shortName: z.ZodString;
2023
2089
  title: z.ZodString;
2024
2090
  status: z.ZodEnum<["planned", "in-progress", "done", "blocked", "canceled", "rejected", "deferred", "waiting"]>;
@@ -2128,6 +2194,8 @@ export declare const PlanWorkspaceSchema: z.ZodObject<{
2128
2194
  acceptedAt: string;
2129
2195
  }[];
2130
2196
  phaseId: string;
2197
+ shortId: string;
2198
+ priority: number;
2131
2199
  shortName: string;
2132
2200
  statusLog: {
2133
2201
  id: string;
@@ -2173,6 +2241,8 @@ export declare const PlanWorkspaceSchema: z.ZodObject<{
2173
2241
  rationale?: string | undefined;
2174
2242
  implementationNotes?: string | undefined;
2175
2243
  }[] | undefined;
2244
+ shortId?: string | undefined;
2245
+ priority?: number | undefined;
2176
2246
  statusLog?: {
2177
2247
  id: string;
2178
2248
  date: string;
@@ -2221,6 +2291,8 @@ export declare const PlanWorkspaceSchema: z.ZodObject<{
2221
2291
  acceptedAt: string;
2222
2292
  }[];
2223
2293
  phaseId: string;
2294
+ shortId: string;
2295
+ priority: number;
2224
2296
  shortName: string;
2225
2297
  statusLog: {
2226
2298
  id: string;
@@ -2261,6 +2333,8 @@ export declare const PlanWorkspaceSchema: z.ZodObject<{
2261
2333
  rationale?: string | undefined;
2262
2334
  implementationNotes?: string | undefined;
2263
2335
  }[] | undefined;
2336
+ shortId?: string | undefined;
2337
+ priority?: number | undefined;
2264
2338
  statusLog?: {
2265
2339
  id: string;
2266
2340
  date: string;
@@ -2288,6 +2362,8 @@ export declare const PlanWorkspaceSchema: z.ZodObject<{
2288
2362
  }>, "many">>;
2289
2363
  createdAt: z.ZodString;
2290
2364
  updatedAt: z.ZodString;
2365
+ handoff: z.ZodDefault<z.ZodString>;
2366
+ handoffUpdatedAt: z.ZodDefault<z.ZodString>;
2291
2367
  }, "strip", z.ZodTypeAny, {
2292
2368
  number: number;
2293
2369
  status: "planned" | "in-progress" | "done" | "blocked" | "canceled" | "rejected" | "deferred" | "waiting" | "draft" | "discovery";
@@ -2308,6 +2384,8 @@ export declare const PlanWorkspaceSchema: z.ZodObject<{
2308
2384
  implementationNotes: string;
2309
2385
  acceptedAt: string;
2310
2386
  }[];
2387
+ shortId: string;
2388
+ priority: number;
2311
2389
  dependsOn: string[];
2312
2390
  slug: string;
2313
2391
  discussedAt: string;
@@ -2343,6 +2421,8 @@ export declare const PlanWorkspaceSchema: z.ZodObject<{
2343
2421
  acceptedAt: string;
2344
2422
  }[];
2345
2423
  phaseId: string;
2424
+ shortId: string;
2425
+ priority: number;
2346
2426
  shortName: string;
2347
2427
  statusLog: {
2348
2428
  id: string;
@@ -2364,6 +2444,8 @@ export declare const PlanWorkspaceSchema: z.ZodObject<{
2364
2444
  startedAt: string;
2365
2445
  completedAt: string;
2366
2446
  }[];
2447
+ handoff: string;
2448
+ handoffUpdatedAt: string;
2367
2449
  featureId?: string | undefined;
2368
2450
  }, {
2369
2451
  number: number;
@@ -2386,6 +2468,8 @@ export declare const PlanWorkspaceSchema: z.ZodObject<{
2386
2468
  rationale?: string | undefined;
2387
2469
  implementationNotes?: string | undefined;
2388
2470
  }[] | undefined;
2471
+ shortId?: string | undefined;
2472
+ priority?: number | undefined;
2389
2473
  dependsOn?: string[] | undefined;
2390
2474
  featureId?: string | undefined;
2391
2475
  discussedAt?: string | undefined;
@@ -2417,6 +2501,8 @@ export declare const PlanWorkspaceSchema: z.ZodObject<{
2417
2501
  rationale?: string | undefined;
2418
2502
  implementationNotes?: string | undefined;
2419
2503
  }[] | undefined;
2504
+ shortId?: string | undefined;
2505
+ priority?: number | undefined;
2420
2506
  statusLog?: {
2421
2507
  id: string;
2422
2508
  date: string;
@@ -2442,6 +2528,8 @@ export declare const PlanWorkspaceSchema: z.ZodObject<{
2442
2528
  startedAt?: string | undefined;
2443
2529
  completedAt?: string | undefined;
2444
2530
  }[] | undefined;
2531
+ handoff?: string | undefined;
2532
+ handoffUpdatedAt?: string | undefined;
2445
2533
  }>, "many">;
2446
2534
  }, "strip", z.ZodTypeAny, {
2447
2535
  features: {
@@ -2461,6 +2549,8 @@ export declare const PlanWorkspaceSchema: z.ZodObject<{
2461
2549
  implementationNotes: string;
2462
2550
  acceptedAt: string;
2463
2551
  }[];
2552
+ shortId: string;
2553
+ priority: number;
2464
2554
  dependsOn: string[];
2465
2555
  discussedAt: string;
2466
2556
  contextReady: boolean;
@@ -2545,6 +2635,8 @@ export declare const PlanWorkspaceSchema: z.ZodObject<{
2545
2635
  implementationNotes: string;
2546
2636
  acceptedAt: string;
2547
2637
  }[];
2638
+ shortId: string;
2639
+ priority: number;
2548
2640
  dependsOn: string[];
2549
2641
  slug: string;
2550
2642
  discussedAt: string;
@@ -2580,6 +2672,8 @@ export declare const PlanWorkspaceSchema: z.ZodObject<{
2580
2672
  acceptedAt: string;
2581
2673
  }[];
2582
2674
  phaseId: string;
2675
+ shortId: string;
2676
+ priority: number;
2583
2677
  shortName: string;
2584
2678
  statusLog: {
2585
2679
  id: string;
@@ -2601,6 +2695,8 @@ export declare const PlanWorkspaceSchema: z.ZodObject<{
2601
2695
  startedAt: string;
2602
2696
  completedAt: string;
2603
2697
  }[];
2698
+ handoff: string;
2699
+ handoffUpdatedAt: string;
2604
2700
  featureId?: string | undefined;
2605
2701
  }[];
2606
2702
  }, {
@@ -2621,6 +2717,8 @@ export declare const PlanWorkspaceSchema: z.ZodObject<{
2621
2717
  rationale?: string | undefined;
2622
2718
  implementationNotes?: string | undefined;
2623
2719
  }[] | undefined;
2720
+ shortId?: string | undefined;
2721
+ priority?: number | undefined;
2624
2722
  dependsOn?: string[] | undefined;
2625
2723
  discussedAt?: string | undefined;
2626
2724
  contextReady?: boolean | undefined;
@@ -2706,6 +2804,8 @@ export declare const PlanWorkspaceSchema: z.ZodObject<{
2706
2804
  rationale?: string | undefined;
2707
2805
  implementationNotes?: string | undefined;
2708
2806
  }[] | undefined;
2807
+ shortId?: string | undefined;
2808
+ priority?: number | undefined;
2709
2809
  dependsOn?: string[] | undefined;
2710
2810
  featureId?: string | undefined;
2711
2811
  discussedAt?: string | undefined;
@@ -2737,6 +2837,8 @@ export declare const PlanWorkspaceSchema: z.ZodObject<{
2737
2837
  rationale?: string | undefined;
2738
2838
  implementationNotes?: string | undefined;
2739
2839
  }[] | undefined;
2840
+ shortId?: string | undefined;
2841
+ priority?: number | undefined;
2740
2842
  statusLog?: {
2741
2843
  id: string;
2742
2844
  date: string;
@@ -2762,6 +2864,8 @@ export declare const PlanWorkspaceSchema: z.ZodObject<{
2762
2864
  startedAt?: string | undefined;
2763
2865
  completedAt?: string | undefined;
2764
2866
  }[] | undefined;
2867
+ handoff?: string | undefined;
2868
+ handoffUpdatedAt?: string | undefined;
2765
2869
  }[];
2766
2870
  }>;
2767
2871
  export type Timestamp = z.infer<typeof TimestampSchema>;
@@ -1 +1 @@
1
- {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAS5B,CAAC;AAEH,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAErC,CAAC;AAEH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;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"}
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
package/dist/schema.js CHANGED
@@ -138,6 +138,8 @@ export const TaskSchema = z.object({
138
138
  id: z.string(),
139
139
  phaseId: z.string(),
140
140
  number: z.number().int().nonnegative().default(0),
141
+ shortId: z.string().regex(/^(|[A-Z2-9]{5})$/).default(""),
142
+ priority: z.number().int().nonnegative().default(0),
141
143
  shortName: SlugSchema,
142
144
  title: z.string().min(1),
143
145
  status: TaskStatusSchema,
@@ -165,6 +167,8 @@ export const PhaseSchema = z.object({
165
167
  id: z.string(),
166
168
  featureId: z.string().optional(),
167
169
  number: z.number().int().positive(),
170
+ shortId: z.string().regex(/^(|[A-Z2-9]{5})$/).default(""),
171
+ priority: z.number().int().nonnegative().default(0),
168
172
  slug: SlugSchema,
169
173
  title: z.string().min(1),
170
174
  status: PhaseStatusSchema,
@@ -187,10 +191,14 @@ export const PhaseSchema = z.object({
187
191
  tasks: z.array(TaskSchema).default([]),
188
192
  createdAt: TimestampSchema,
189
193
  updatedAt: TimestampSchema,
194
+ handoff: z.string().default(""),
195
+ handoffUpdatedAt: z.string().default(""),
190
196
  });
191
197
  export const FeatureSchema = z.object({
192
198
  id: z.string(),
193
199
  number: z.number().int().nonnegative().default(0),
200
+ shortId: z.string().regex(/^(|[A-Z2-9]{5})$/).default(""),
201
+ priority: z.number().int().nonnegative().default(0),
194
202
  name: z.string().min(1),
195
203
  description: z.string().default(""),
196
204
  status: FeatureStatusSchema,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-plan/core",
3
- "version": "0.2.18",
3
+ "version": "0.2.19-next.0",
4
4
  "private": false,
5
5
  "description": "Harness-agnostic core for Agent Plan: schemas, persistence, ordering, status rollups, and markdown rendering.",
6
6
  "license": "MIT",
@@ -42,6 +42,7 @@
42
42
  },
43
43
  "scripts": {
44
44
  "build": "tsc -p tsconfig.json",
45
- "check": "tsc -p tsconfig.json --noEmit"
45
+ "check": "tsc -p tsconfig.json --noEmit",
46
+ "test": "tsc -p tsconfig.json && node --test \"test/**/*.test.mjs\""
46
47
  }
47
48
  }