@papi-ai/adapter-md 0.1.0-alpha → 0.2.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.
Files changed (4) hide show
  1. package/LICENSE +98 -0
  2. package/dist/index.d.ts +1127 -194
  3. package/dist/index.js +577 -457
  4. package/package.json +9 -3
package/dist/index.d.ts CHANGED
@@ -1,24 +1,7 @@
1
- import { TaskStatus as TaskStatus$1, TaskPriority as TaskPriority$1, EffortSize as EffortSize$1, ReviewStage as ReviewStage$1, ReviewVerdict as ReviewVerdict$1, TaskComplexity as TaskComplexity$1, TaskType as TaskType$1, isValidStatus as isValidStatus$1, isValidTransition as isValidTransition$1, validateTransition as validateTransition$1 } from '@papi-ai/shared';
1
+ import { TaskStatus as TaskStatus$1, TaskPriority as TaskPriority$1, EffortSize as EffortSize$1, TaskComplexity as TaskComplexity$1, TaskType as TaskType$1, ReviewStage as ReviewStage$1, ReviewVerdict as ReviewVerdict$1, isValidStatus as isValidStatus$1, isValidTransition as isValidTransition$1, validateTransition as validateTransition$1 } from '@papi-ai/shared';
2
2
 
3
3
  /** Valid phase statuses in the product brief. */
4
4
  type PhaseStatus = 'Not Started' | 'In Progress' | 'Done' | 'Deferred';
5
- /** Feature lifecycle status. */
6
- type FeatureStatus = 'Not Started' | 'In Progress' | 'Done' | 'Deferred';
7
- /** A workstream or capability being built. Replaces Phase as the primary planning construct (AD-23). */
8
- interface Feature {
9
- id: string;
10
- displayId: string;
11
- slug: string;
12
- name: string;
13
- description: string;
14
- status: FeatureStatus;
15
- roadmapPosition: number;
16
- createdAt: string;
17
- completedAt?: string;
18
- commitDate?: string;
19
- createdSprint: number;
20
- completedSprint?: number;
21
- }
22
5
  /** A development phase defined in the product brief. */
23
6
  interface Phase {
24
7
  id: string;
@@ -27,27 +10,71 @@ interface Phase {
27
10
  description: string;
28
11
  status: PhaseStatus;
29
12
  order: number;
13
+ /** The stage this phase belongs to (AD-14 hierarchy). */
14
+ stageId?: string;
30
15
  }
31
- /** Sprint lifecycle status. */
32
- type SprintStatus = 'planning' | 'active' | 'complete';
33
- /** A sprint entity giving sprints first-class identity. */
34
- /** Per-section SHA-256 hashes for context diff optimisation between sprints. */
16
+ /**
17
+ * Horizon status in the project hierarchy (AD-14). Shares the PhaseStatus
18
+ * vocabulary the DB constraint + all live rows use 'Not Started'/'In Progress'/
19
+ * 'Done'/'Deferred', NOT the legacy 'active'/'completed'/'deferred' (task-2136).
20
+ */
21
+ type HorizonStatus = PhaseStatus;
22
+ /** A horizon — the highest level of the project hierarchy (AD-14: Horizon → Stage → Phase → Task). */
23
+ interface Horizon {
24
+ id: string;
25
+ slug: string;
26
+ label: string;
27
+ description?: string;
28
+ status: HorizonStatus;
29
+ sortOrder: number;
30
+ projectId: string;
31
+ createdAt: string;
32
+ updatedAt: string;
33
+ }
34
+ /** Stage status in the project hierarchy (AD-14). Shares the PhaseStatus vocabulary (task-2136 — see HorizonStatus). */
35
+ type StageStatus = PhaseStatus;
36
+ /** A stage — sits between Horizon and Phase in the hierarchy (AD-14: Horizon → Stage → Phase → Task). */
37
+ interface Stage {
38
+ id: string;
39
+ slug: string;
40
+ label: string;
41
+ description?: string;
42
+ status: StageStatus;
43
+ sortOrder: number;
44
+ horizonId: string;
45
+ projectId: string;
46
+ exitCriteria?: string[];
47
+ createdAt: string;
48
+ updatedAt: string;
49
+ }
50
+ /** Cycle lifecycle status (renamed from CycleStatus per AD-14). */
51
+ type CycleStatus = 'planning' | 'active' | 'complete';
52
+ /** Per-section SHA-256 hashes for context diff optimisation between cycles. */
35
53
  interface ContextHashes {
36
54
  productBrief?: string;
37
55
  activeDecisions?: string;
38
56
  reviews?: string;
39
57
  [key: string]: string | undefined;
40
58
  }
41
- interface Sprint {
59
+ /** A cycle entity — the methodology loop (renamed from Cycle per AD-14). */
60
+ interface Cycle {
42
61
  id: string;
43
62
  number: number;
44
- status: SprintStatus;
63
+ status: CycleStatus;
45
64
  startDate: string;
46
65
  endDate?: string;
47
66
  goals: string[];
48
67
  boardHealth: string;
49
68
  taskIds: string[];
50
69
  contextHashes?: ContextHashes;
70
+ /**
71
+ * task-2071 (C293, MU-3): the member who owns this cycle. Each member runs
72
+ * their own cycle into the shared repo; the DB enforces one active cycle per
73
+ * (project_id, user_id). createCycle stamps this from the calling identity;
74
+ * the C293 migration backfilled existing active cycles to the project owner so
75
+ * solo/owner-operated projects are unchanged. NULL = legacy/unowned.
76
+ */
77
+ userId?: string;
51
78
  }
52
79
 
53
80
  type TaskStatus = TaskStatus$1;
@@ -63,7 +90,7 @@ declare const validateTransition: typeof validateTransition$1;
63
90
  declare const isValidStatus: typeof isValidStatus$1;
64
91
  /**
65
92
  * Context tiers control which tasks the planner sees:
66
- * Tier 1 (Sprint-eligible): Bug, Task — fed to planner for scheduling
93
+ * Tier 1 (Cycle-eligible): Bug, Task — fed to planner for scheduling
67
94
  * Tier 2 (Plannable): Research — scheduled but output is knowledge not code
68
95
  * Tier 3 (Pre-triage): Idea, Feedback — invisible to planner until promoted
69
96
  */
@@ -78,22 +105,41 @@ type Confidence = 'HIGH' | 'MEDIUM' | 'LOW';
78
105
  type DecisionEventType = 'created' | 'confidence_changed' | 'modified' | 'superseded' | 'validated' | 'invalidated' | 'scored';
79
106
  /** Source of a decision event. */
80
107
  type DecisionEventSource = 'strategy_review' | 'build_complete' | 'strategy_change' | 'manual';
108
+ /**
109
+ * Structured before->after movement of a single metric, captured on a
110
+ * deliberate decision event (task-2168). Consumes cycle_metrics_snapshots.
111
+ */
112
+ interface MetricDelta {
113
+ /** Metric name — ideally a real metric from cycle_metrics_snapshots. */
114
+ metric: string;
115
+ before?: number;
116
+ after?: number;
117
+ delta?: number;
118
+ }
81
119
  /** An append-only event in a decision's lifecycle. */
82
120
  interface DecisionEvent {
83
121
  id: string;
84
122
  decisionId: string;
85
123
  eventType: DecisionEventType;
86
- sprint: number;
124
+ cycle: number;
87
125
  source: DecisionEventSource;
88
126
  sourceRef?: string;
89
127
  detail?: string;
128
+ /**
129
+ * Pointer to supporting evidence (doc path / build-report id / metric name).
130
+ * Captured on deliberate events (validated / invalidated / modified) at
131
+ * strategy_review / strategy_change. Optional — writer fails open (task-2168).
132
+ */
133
+ evidenceRef?: string;
134
+ /** Which metric moved + before->after for this decision (task-2168). */
135
+ metricDelta?: MetricDelta | null;
90
136
  createdAt: string;
91
137
  }
92
138
  /** Dimensional scores for an Active Decision at a point in time. */
93
139
  interface DecisionScore {
94
140
  id: string;
95
141
  decisionId: string;
96
- sprint: number;
142
+ cycle: number;
97
143
  effort: number;
98
144
  risk: number;
99
145
  reversibility: number;
@@ -103,15 +149,17 @@ interface DecisionScore {
103
149
  rationale?: string;
104
150
  createdAt: string;
105
151
  }
106
- /** Sprint health metadata from PLANNING_LOG.md header. */
107
- interface SprintHealth {
108
- totalSprints: number;
109
- latestSprintStatus?: string;
110
- sprintsSinceLastStrategyReview: number;
152
+ /** Cycle health metadata from PLANNING_LOG.md header. */
153
+ interface CycleHealth {
154
+ totalCycles: number;
155
+ latestCycleStatus?: string;
156
+ cyclesSinceLastStrategyReview: number;
111
157
  strategyReviewDue: string;
112
158
  boardHealth: string;
113
159
  strategicDirection: string;
114
160
  lastFullMode: number;
161
+ /** Number of auth.users rows with no matching user_profiles row. Non-zero = trigger gap. pg adapter only. */
162
+ orphanAuthUsers?: number;
115
163
  }
116
164
  /** An Active Decision (AD) tracked in the planning log. */
117
165
  interface ActiveDecision {
@@ -122,46 +170,128 @@ interface ActiveDecision {
122
170
  confidence: Confidence;
123
171
  superseded: boolean;
124
172
  supersededBy?: string;
125
- createdSprint?: number;
126
- modifiedSprint?: number;
173
+ createdCycle?: number;
174
+ modifiedCycle?: number;
127
175
  body: string;
176
+ outcome?: 'pending' | 'confirmed' | 'validated' | 'revised' | 'resolved' | 'abandoned' | 'superseded';
177
+ revisionCount?: number;
128
178
  }
129
- /** A single sprint entry in the planning log. */
130
- interface SprintLogEntry {
179
+ /** A single cycle entry in the planning log. */
180
+ interface CycleLogEntry {
131
181
  uuid: string;
132
- sprintNumber: number;
182
+ cycleNumber: number;
133
183
  title: string;
134
184
  content: string;
135
185
  carryForward?: string;
136
186
  notes?: string;
187
+ /** Number of tasks in this cycle (structured — no prose parsing needed). */
188
+ taskCount?: number;
189
+ /** Total effort points for this cycle (XS=1, S=2, M=3, L=5, XL=8). */
190
+ effortPoints?: number;
191
+ /**
192
+ * ISO timestamp for when this cycle log entry was last written (plan or
193
+ * release). Optional — historical entries may not have it. Surfaced from
194
+ * planning_log_entries.updated_at in the pg adapter.
195
+ */
196
+ date?: string;
137
197
  }
138
198
  /** A strategy review entry stored in its own table (not planning_log_entries). */
139
199
  interface StrategyReviewEntry {
140
- sprintNumber: number;
141
- sprintRange?: string;
200
+ cycleNumber: number;
201
+ cycleRange?: string;
142
202
  title: string;
143
203
  content: string;
144
204
  notes?: string;
145
205
  boardHealth?: string;
146
206
  strategicDirection?: string;
147
207
  recommendations?: unknown;
208
+ fullAnalysis?: string;
209
+ velocityAssessment?: string;
210
+ /** Full ReviewStructuredOutput from the strategy review LLM, stored as JSONB for dashboard querying */
211
+ structuredData?: Record<string, unknown>;
212
+ createdAt?: string;
213
+ /** Sequential review number per project (1, 2, 3...) */
214
+ reviewNumber?: number;
215
+ /** Review type: 'scheduled' (every 5 cycles), 'ad-hoc' (user-triggered), 'triggered' (by event) */
216
+ reviewType?: string;
217
+ }
218
+ /**
219
+ * A single entry in a project's harness inventory (task-1896).
220
+ * Captures what skills / sub-agents / hooks / MCP tools a project is running,
221
+ * scanned from the local filesystem by the MCP server and persisted so the
222
+ * dashboard (which has no filesystem access) can surface it per project.
223
+ */
224
+ interface HarnessInventoryEntry {
225
+ id?: string;
226
+ projectId?: string;
227
+ /** What kind of harness artifact this row describes. */
228
+ kind: 'skill' | 'agent' | 'hook' | 'mcp_tool';
229
+ /** Human-readable name (skill/agent/hook filename stem, or MCP tool name). */
230
+ name: string;
231
+ /** One-line description where available (frontmatter / tool description). */
232
+ description?: string;
233
+ /** Version of the artifact, where known (skills, from the @papi-ai/skills manifest). */
234
+ version?: string;
235
+ /** Content checksum, where known (skills — used to detect stale forks). */
236
+ checksum?: string;
237
+ /**
238
+ * Status signal surfaced in the dashboard:
239
+ * - 'ok': present and current
240
+ * - 'stale_fork': a local skill that has drifted from the pinned registry version
241
+ * - 'missing': a recommended hook that is not installed in the project
242
+ */
243
+ status: 'ok' | 'stale_fork' | 'missing';
244
+ /** Project-relative path, where applicable (never absolute, never file contents). */
245
+ path?: string;
246
+ syncedAt?: string;
247
+ }
248
+ /**
249
+ * Change-detection marker for the harness inventory (task-1896).
250
+ * A cheap fingerprint of the project's harness layout; the sync writer only
251
+ * does the full scan + DB write when this fingerprint changes.
252
+ */
253
+ interface HarnessState {
254
+ fingerprint: string;
255
+ syncedAt?: string;
256
+ }
257
+ /** A dogfood observation captured during strategy reviews or releases. */
258
+ interface DogfoodEntry {
259
+ id?: string;
260
+ projectId?: string;
261
+ cycleNumber: number;
262
+ category: 'friction' | 'methodology' | 'signal' | 'commercial';
263
+ content: string;
264
+ sourceTool?: string;
265
+ sourceRef?: string;
266
+ status?: 'observed' | 'backlog-created' | 'resolved' | 'actioned' | 'dismissed';
267
+ linkedTaskId?: string;
148
268
  createdAt?: string;
149
269
  }
150
270
  /** Full parsed contents of PLANNING_LOG.md. */
151
271
  interface PlanningLog {
152
- sprintHealth: SprintHealth;
272
+ cycleHealth: CycleHealth;
153
273
  northStar: string;
154
274
  activeDecisions: ActiveDecision[];
155
275
  deferred: string[];
156
- sprintLog: SprintLogEntry[];
276
+ cycleLog: CycleLogEntry[];
157
277
  }
158
278
  /** A single state transition entry recording when a task changed status. */
159
279
  interface StateTransition {
160
280
  status: TaskStatus;
161
281
  timestamp: string;
162
282
  }
163
- /** A task on the sprint board (parsed from SPRINT_BOARD.md YAML). */
164
- interface SprintTask {
283
+ /** AD-29: scope_class distinguishes directly-buildable tasks from brief-class items needing decomposition. */
284
+ type ScopeClass = 'task' | 'brief';
285
+ /** A task on the board (parsed from CYCLE_BOARD.md YAML). */
286
+ /** A sibling project's in-flight task, surfaced for the plan duplicate-risk check (task-2139). */
287
+ interface SiblingRepoTask {
288
+ displayId: string;
289
+ title: string;
290
+ epic?: string;
291
+ status: string;
292
+ sourceProjectName: string;
293
+ }
294
+ interface CycleTask {
165
295
  uuid: string;
166
296
  id: string;
167
297
  displayId: string;
@@ -170,12 +300,15 @@ interface SprintTask {
170
300
  priority: TaskPriority;
171
301
  complexity: TaskComplexity;
172
302
  module: string;
173
- epic: string;
303
+ /** @deprecated (AD-14): Use phase + stageId instead. Will be removed after taxonomy migration. */
304
+ epic?: string;
174
305
  phase: string;
306
+ /** Stage FK for AD-14 hierarchy (Horizon → Stage → Phase → Task). */
307
+ stageId?: string;
175
308
  owner: string;
176
309
  reviewed: boolean;
177
- sprint?: number;
178
- createdSprint?: number;
310
+ cycle?: number;
311
+ createdCycle?: number;
179
312
  createdAt?: string;
180
313
  why?: string;
181
314
  dependsOn?: string;
@@ -186,6 +319,63 @@ interface SprintTask {
186
319
  buildReport?: string;
187
320
  taskType?: TaskType;
188
321
  maturity?: TaskMaturity;
322
+ /** Path to a reference document (e.g. docs/research/foo.md). */
323
+ docRef?: string;
324
+ /** Provenance: where this task originated (owner, llm, strategy_review, etc.). */
325
+ source?: string;
326
+ /** What user problem does this solve? Used by planner for opportunity-based clustering. */
327
+ opportunity?: string;
328
+ /** AD-29: 'task' = directly buildable; 'brief' = needs scope_brief decomposition first. Default 'task'. */
329
+ scopeClass?: ScopeClass;
330
+ /** task-1699 (C255): audit who initiated a cancellation. Set when status transitions to 'Cancelled'.
331
+ * 'planner' = planner proposed + user confirmed; 'user' = direct user action; 'system' = automated. */
332
+ cancelledBy?: 'planner' | 'user' | 'system';
333
+ /**
334
+ * Visibility tier (task-2013). Inherited from the source doc when a task is born
335
+ * from `idea`/`plan` with a doc_ref; defaults to 'public'. Read-only after creation —
336
+ * build_execute/review_submit must NOT mutate it.
337
+ */
338
+ visibility?: DocVisibility;
339
+ /** Owner lock (task-2013) — set on private/contributors rows so a project_id reassignment cannot expose them. */
340
+ ownerUserId?: string;
341
+ /**
342
+ * task-1763 (C293): claim assignee — the user who has claimed this task from the
343
+ * shared pool. NULL/undefined = unclaimed. Set only via the atomic claimTask CAS
344
+ * (bearer-derived identity, never client input). Consumed by MU-3 (task-2071).
345
+ */
346
+ assigneeId?: string;
347
+ /**
348
+ * task-2071 (C293, MU-3): how this task entered a personal backlog. 'pool' =
349
+ * claimed from the shared org pool via task_claim; 'self_generated' = created
350
+ * already attributed to a user. NULL/undefined = legacy/unattributed. The
351
+ * pooled-task invariant is `assigneeId == null && cycle == null`.
352
+ */
353
+ claimSource?: 'pool' | 'self_generated';
354
+ /**
355
+ * task-2071 (C293): reviewer slot for MU-4/5 (cross-user review, task-2072).
356
+ * Schema-only this task — no code reads it yet.
357
+ */
358
+ reviewerId?: string;
359
+ /**
360
+ * task-2180 (C295): ISO timestamp the task's branch was successfully merged to the
361
+ * base branch (review_submit accept or release). NULL/undefined = not yet merged.
362
+ * Write-only audit tie between Done status and git reality — set on a successful
363
+ * merge so a Done task is provably backed by a real merge; no reader yet.
364
+ */
365
+ mergedAt?: string;
366
+ }
367
+ /** Structured handoff accuracy — replaces coarse scope_accuracy text. */
368
+ interface HandoffAccuracy {
369
+ scopeMatch: boolean;
370
+ filesMatch: 'exact' | 'partial' | 'missed';
371
+ effortMatch: boolean;
372
+ notes?: string;
373
+ }
374
+ /** Brief implications from a build — discovery learnings that feed back into planning. */
375
+ interface BriefImplication {
376
+ canvasSection: 'landscape' | 'journeys' | 'mvpBoundary' | 'assumptions' | 'successSignals';
377
+ type: 'confirm' | 'contradict' | 'new';
378
+ detail: string;
189
379
  }
190
380
  /** A build report entry from BUILD_REPORTS.md. */
191
381
  interface BuildReport {
@@ -195,7 +385,7 @@ interface BuildReport {
195
385
  taskId: string;
196
386
  taskName: string;
197
387
  date: string;
198
- sprint: number;
388
+ cycle: number;
199
389
  completed: 'Yes' | 'No' | 'Partial';
200
390
  actualEffort: EffortSize;
201
391
  estimatedEffort: EffortSize;
@@ -205,7 +395,139 @@ interface BuildReport {
205
395
  scopeAccuracy: 'accurate' | 'over-scoped' | 'under-scoped' | 'missed-context';
206
396
  commitSha?: string;
207
397
  filesChanged?: string[];
398
+ /**
399
+ * task-1523 (C242): scope drift signal.
400
+ * Populated when filesChanged diverges materially from the handoff's filesLikelyTouched
401
+ * (e.g. >50% of changed files unpredicted, or >5 unexpected files). Format is a single
402
+ * human-readable line surfaced under "Scope drift signal" in the build report. Does NOT block.
403
+ */
404
+ scopeDriftSignal?: string;
208
405
  relatedDecisions?: string[];
406
+ handoffAccuracy?: HandoffAccuracy;
407
+ correctionsCount?: number;
408
+ briefImplications?: BriefImplication[];
409
+ deadEnds?: string;
410
+ /** Number of build iterations for this task (1 = first attempt, 2+ = rework after pushback). */
411
+ iterationCount?: number;
412
+ /** ISO 8601 timestamp when build_execute (start) was called. */
413
+ startedAt?: string;
414
+ /** ISO 8601 timestamp when build_execute (complete) was called. */
415
+ completedAt?: string;
416
+ /** Number of tool calls recorded during this build session. */
417
+ toolCallCount?: number;
418
+ /**
419
+ * task-1853 deploy-verification record. Populated when the task's diff hit a
420
+ * trigger surface (TRIGGER_SURFACE_GLOBS) and the builder verified the live deploy.
421
+ * Required by `completeBuild` for trigger-surface tasks; gates `review_submit` accept.
422
+ */
423
+ productionVerification?: {
424
+ urls: string[];
425
+ curl_command: string;
426
+ http_status: number;
427
+ response_excerpt: string;
428
+ verified_at: string;
429
+ };
430
+ }
431
+ /** A registered document in the doc registry. */
432
+ /**
433
+ * Doc visibility tier (task-1977, C283).
434
+ * - public: shipped with PAPI, readable cross-project by any user.
435
+ * - contributors: "team member" tier — readable by the project's contributor cohort.
436
+ * - private: owner-only (default — safety bias).
437
+ * User-facing label for `contributors` is "team member".
438
+ */
439
+ type DocVisibility = 'public' | 'contributors' | 'private';
440
+ interface DocRegistryEntry {
441
+ id: string;
442
+ title: string;
443
+ type: string;
444
+ path: string;
445
+ status: string;
446
+ summary: string;
447
+ tags: string[];
448
+ cycleCreated: number;
449
+ cycleUpdated?: number;
450
+ supersededBy?: string;
451
+ actions?: Array<{
452
+ description: string;
453
+ status: string;
454
+ linkedTaskId?: string;
455
+ }>;
456
+ /** Visibility tier (task-1977). Defaults to 'private' at write time when omitted. */
457
+ visibility?: DocVisibility;
458
+ /** Owner lock — the user_id that owns this doc; set on register so a project_id reassignment cannot expose private rows. */
459
+ ownerUserId?: string;
460
+ createdAt?: string;
461
+ updatedAt?: string;
462
+ }
463
+ /** A structured learning entry from a build report. */
464
+ interface CycleLearning {
465
+ id?: string;
466
+ projectId?: string;
467
+ taskId: string;
468
+ cycleNumber: number;
469
+ category: 'surprise' | 'issue' | 'dead_end' | 'architecture' | 'estimation_miss' | 'doc_closure' | 'observability';
470
+ severity?: 'P0' | 'P1' | 'P2' | 'P3';
471
+ summary: string;
472
+ detail?: string;
473
+ tags: string[];
474
+ relatedDecision?: string;
475
+ actionTaken?: 'idea_submitted' | 'task_created' | 'ad_updated' | 'none';
476
+ actionRef?: string;
477
+ createdAt?: string;
478
+ /** When the underlying issue was marked resolved. null/undefined = still open. (task-1541) */
479
+ resolvedAt?: string;
480
+ /** Identifier of the agent/user that resolved the issue (task-1541). */
481
+ resolvedBy?: string;
482
+ }
483
+ /** A cross-cycle pattern detected from learning tags. */
484
+ interface CycleLearningPattern {
485
+ tag: string;
486
+ cycles: number[];
487
+ frequency: number;
488
+ }
489
+ /** Input for doc_search queries. */
490
+ interface DocSearchInput {
491
+ type?: string;
492
+ status?: string;
493
+ tags?: string[];
494
+ keyword?: string;
495
+ hasPendingActions?: boolean;
496
+ sinceCycle?: number;
497
+ limit?: number;
498
+ /**
499
+ * Visibility scoping (task-1977). The user_id of the requester. When set and the
500
+ * requester is NOT the project owner, results are filtered to public docs plus
501
+ * contributors-tier docs the requester is a cohort member of (private excluded —
502
+ * fail-closed). When omitted (local owner/CLI context), all rows are returned.
503
+ */
504
+ requesterUserId?: string;
505
+ }
506
+ /** Aggregated recommendation effectiveness row from v_recommendation_effectiveness. */
507
+ interface RecommendationEffectivenessRow {
508
+ type: string;
509
+ total: number;
510
+ actioned: number;
511
+ dismissed: number;
512
+ pending: number;
513
+ acceptanceRate: string;
514
+ avgCyclesToAction: string | null;
515
+ }
516
+ /** Aggregated estimation calibration row from v_estimation_accuracy. */
517
+ interface EstimationCalibrationRow {
518
+ estimatedEffort: string;
519
+ actualEffort: string;
520
+ accuracyLabel: string;
521
+ count: number;
522
+ }
523
+ /** Per-module estimation stats — builds joined with task module via cycle_tasks. */
524
+ interface ModuleEstimationRow {
525
+ module: string;
526
+ total: number;
527
+ /** % of tasks where estimated effort matched actual effort. */
528
+ accuracyPct: number;
529
+ /** % of tasks where scope_accuracy was 'accurate'. */
530
+ scopeAccuratePct: number;
209
531
  }
210
532
  /** A single tool call metric entry from METRICS.md. */
211
533
  interface ToolCallMetric {
@@ -216,12 +538,21 @@ interface ToolCallMetric {
216
538
  outputTokens?: number;
217
539
  estimatedCostUsd?: number;
218
540
  model?: string;
219
- /** Sprint number when this metric was recorded. Absent on legacy rows. */
220
- sprintNumber?: number;
541
+ /** Cycle number when this metric was recorded. Absent on legacy rows. */
542
+ cycleNumber?: number;
221
543
  /** Context size in bytes for plan tool calls. Absent on non-plan tools. */
222
544
  contextBytes?: number;
223
545
  /** Context utilisation ratio (0.0–1.0). Fraction of input entities referenced in output. */
224
546
  contextUtilisation?: number;
547
+ /** Whether the tool call succeeded (no error prefix in response). */
548
+ success?: boolean;
549
+ /**
550
+ * MCP client `Implementation.name` advertised at initialize time.
551
+ * Captured verbatim (Claude Code = "claude-code"; Codex advertises a
552
+ * different string). Used to split activation/feature funnels by CLI.
553
+ * Absent on legacy rows + non-MCP code paths. (task-1680)
554
+ */
555
+ clientName?: string;
225
556
  }
226
557
  /** Per-command cost aggregation. */
227
558
  interface CommandCost {
@@ -241,8 +572,32 @@ interface CostSummary {
241
572
  avgCostPerCall: number;
242
573
  }
243
574
  /** A cost snapshot persisted in the Cost Summary section of METRICS.md. */
575
+ /** Discovery canvas — progressive discovery sections on product brief. */
576
+ interface DiscoveryCanvas {
577
+ landscapeReferences?: {
578
+ name: string;
579
+ url?: string;
580
+ notes?: string;
581
+ }[];
582
+ userJourneys?: {
583
+ persona: string;
584
+ journey: string;
585
+ priority?: string;
586
+ }[];
587
+ mvpBoundary?: string;
588
+ assumptionsOpenQuestions?: {
589
+ text: string;
590
+ status: string;
591
+ evidence?: string;
592
+ }[];
593
+ successSignals?: {
594
+ signal: string;
595
+ metric?: string;
596
+ target?: string;
597
+ }[];
598
+ }
244
599
  interface CostSnapshot {
245
- sprint: number;
600
+ cycle: number;
246
601
  date: string;
247
602
  totalCostUsd: number;
248
603
  totalInputTokens: number;
@@ -268,7 +623,7 @@ type TextClusterer = (entries: ClusterEntry[]) => Promise<ClusterResult[]>;
268
623
  interface RecurringSurprise {
269
624
  text: string;
270
625
  count: number;
271
- sprints: number[];
626
+ cycles: number[];
272
627
  }
273
628
  /** Build pattern detection results from analyzing recent build reports. */
274
629
  interface BuildPatterns {
@@ -278,28 +633,28 @@ interface BuildPatterns {
278
633
  scopeAccuracyBreakdown: Record<string, number>;
279
634
  untriagedIssues: string[];
280
635
  }
281
- /** Per-sprint estimation accuracy stats. */
282
- interface SprintEstimationAccuracy {
283
- sprint: number;
636
+ /** Per-cycle estimation accuracy stats. */
637
+ interface CycleEstimationAccuracy {
638
+ cycle: number;
284
639
  reports: number;
285
640
  matchRate: number;
286
641
  mae: number;
287
642
  bias: number;
288
643
  }
289
- /** Per-sprint velocity stats. */
290
- interface SprintVelocity {
291
- sprint: number;
644
+ /** Per-cycle velocity stats. */
645
+ interface CycleVelocity {
646
+ cycle: number;
292
647
  completed: number;
293
648
  partial: number;
294
649
  failed: number;
295
650
  effortPoints: number;
296
651
  }
297
- /** A sprint methodology metrics snapshot stored in SPRINT_METRICS.md. */
298
- interface SprintMetricsSnapshot {
299
- sprint: number;
652
+ /** A cycle methodology metrics snapshot stored in CYCLE_METRICS.md. */
653
+ interface CycleMetricsSnapshot {
654
+ cycle: number;
300
655
  date: string;
301
- accuracy: SprintEstimationAccuracy[];
302
- velocity: SprintVelocity[];
656
+ accuracy: CycleEstimationAccuracy[];
657
+ velocity: CycleVelocity[];
303
658
  }
304
659
  /** A recurring feedback theme detected across human reviews. */
305
660
  interface RecurringFeedback {
@@ -313,6 +668,21 @@ interface ReviewPatterns {
313
668
  verdictBreakdown: Record<string, number>;
314
669
  requestChangesRate: number;
315
670
  }
671
+ /** Auto-review verdict from automated code review. */
672
+ type AutoReviewVerdict = 'pass' | 'warn' | 'fail';
673
+ /** A single finding from an automated code review. */
674
+ interface AutoReviewFinding {
675
+ severity: 'error' | 'warning' | 'info';
676
+ file?: string;
677
+ line?: number;
678
+ message: string;
679
+ }
680
+ /** Automated code review results — supplements human review. */
681
+ interface AutoReview {
682
+ verdict: AutoReviewVerdict;
683
+ findings: AutoReviewFinding[];
684
+ summary: string;
685
+ }
316
686
  /** A human review entry stored in REVIEWS.md. */
317
687
  interface HumanReview {
318
688
  uuid: string;
@@ -321,11 +691,13 @@ interface HumanReview {
321
691
  stage: ReviewStage;
322
692
  reviewer: string;
323
693
  verdict: ReviewVerdict;
324
- sprint: number;
694
+ cycle: number;
325
695
  date: string;
326
696
  comments: string;
327
697
  handoffRevision?: number;
328
698
  buildCommitSha?: string;
699
+ /** Optional automated code review results attached to this review. */
700
+ autoReview?: AutoReview;
329
701
  }
330
702
  /** A structured BUILD HANDOFF parsed from the markdown handoff block. */
331
703
  interface BuildHandoff {
@@ -334,7 +706,7 @@ interface BuildHandoff {
334
706
  createdAt?: string;
335
707
  taskId: string;
336
708
  taskTitle: string;
337
- sprint: number;
709
+ cycle: number;
338
710
  whyNow: string;
339
711
  scope: string[];
340
712
  scopeBoundary: string[];
@@ -342,12 +714,27 @@ interface BuildHandoff {
342
714
  securityConsiderations: string;
343
715
  filesLikelyTouched: string[];
344
716
  effort: EffortSize;
717
+ /** Files the builder should check before implementing to verify the feature doesn't already exist. */
718
+ verificationFiles?: string[];
345
719
  }
346
720
  /** Module and epic registries loaded from REGISTRIES.md. */
347
721
  interface Registries {
348
722
  modules: string[];
349
723
  epics: string[];
350
724
  }
725
+ /** Lifecycle status for a queued strategy review agenda topic. */
726
+ type AgendaTopicStatus = 'pending' | 'addressed' | 'dismissed';
727
+ /** A topic queued for the next strategy review. */
728
+ interface AgendaTopic {
729
+ id: string;
730
+ topic: string;
731
+ source: string;
732
+ sourceCycle?: number;
733
+ status: AgendaTopicStatus;
734
+ createdAt: string;
735
+ addressedAt?: string;
736
+ addressedInReview?: number;
737
+ }
351
738
  /** Recommendation types written by strategy_review, consumed by plan. */
352
739
  type RecommendationType = 'priority_change' | 'new_task' | 'ad_update' | 'phase_transition' | 'process_improvement' | 'infrastructure' | 'velocity_observation';
353
740
  /** Recommendation lifecycle status. */
@@ -358,8 +745,25 @@ interface StrategyRecommendation {
358
745
  type: RecommendationType;
359
746
  status: RecommendationStatus;
360
747
  content: string;
361
- createdSprint: number;
362
- actionedSprint?: number;
748
+ createdCycle: number;
749
+ actionedCycle?: number;
750
+ target?: string;
751
+ }
752
+ /** Upstream submission (bug or idea) sent to PAPI for cross-project visibility. */
753
+ interface BugReport {
754
+ id: string;
755
+ projectId: string;
756
+ userId?: string;
757
+ /** Kind of submission. Defaults to 'bug' for back-compat with the legacy `bug report=true` flow (task-2174, C296). */
758
+ type?: 'bug' | 'idea';
759
+ description: string;
760
+ diagnostics: Record<string, unknown>;
761
+ status: 'open' | 'resolved' | 'dismissed';
762
+ /** Submitter opted in to be notified when this is resolved (friction-moment opt-in). */
763
+ notifyRequested?: boolean;
764
+ /** Submitter consented to the PAPI team reaching out about this submission. */
765
+ contactOk?: boolean;
766
+ createdAt: string;
363
767
  }
364
768
  /** Entity reference — tracks which decisions/entities are used in tool calls. */
365
769
  interface EntityReference {
@@ -368,22 +772,30 @@ interface EntityReference {
368
772
  entityId: string;
369
773
  referenceContext: 'plan_input' | 'plan_output' | 'strategy_input' | 'strategy_output';
370
774
  toolName: string;
371
- sprintNumber: number;
775
+ cycleNumber: number;
372
776
  createdAt: string;
373
777
  }
778
+ /** Per-tool context utilisation summary from v_context_utilisation. */
779
+ interface ContextUtilisationSummary {
780
+ tool: string;
781
+ cycleNumber: number;
782
+ callCount: number;
783
+ avgUtilisation: number;
784
+ avgContextBytes: number;
785
+ }
374
786
  /** Per-AD usage summary returned by getDecisionUsage. */
375
787
  interface DecisionUsageSummary {
376
788
  decisionId: string;
377
789
  referenceCount: number;
378
- lastReferencedSprint: number;
379
- sprintsSinceLastReference: number;
790
+ lastReferencedCycle: number;
791
+ cyclesSinceLastReference: number;
380
792
  }
381
793
  /** Options for updateTask to control validation behaviour. */
382
794
  interface UpdateTaskOptions {
383
795
  /** Skip state-machine transition validation when true. */
384
796
  force?: boolean;
385
797
  }
386
- /** Filter options for querying the sprint board. */
798
+ /** Filter options for querying the cycle board. */
387
799
  interface BoardQueryOptions {
388
800
  status?: TaskStatus[];
389
801
  priority?: TaskPriority[];
@@ -391,31 +803,183 @@ interface BoardQueryOptions {
391
803
  reviewed?: boolean;
392
804
  module?: string;
393
805
  epic?: string;
394
- /** Filter by context tier (1=sprint-eligible, 2=plannable, 3=pre-triage). */
806
+ /** Filter by context tier (1=cycle-eligible, 2=plannable, 3=pre-triage). */
395
807
  contextTier?: ContextTier;
808
+ /** Filter to tasks assigned to this cycle or later (cycle >= N). */
809
+ cycleSince?: number;
810
+ /** Exclude heavy JSONB columns (build_handoff, build_report, state_history) to reduce DB transfer. */
811
+ compact?: boolean;
812
+ /**
813
+ * Visibility scoping (task-2013). The user_id of the requester. When set and the
814
+ * requester is NOT the project owner, results are filtered to public tasks plus
815
+ * contributors-tier tasks the requester is a cohort member of (private excluded —
816
+ * fail-closed). When omitted (local owner/CLI context), all rows are returned.
817
+ * Mirrors DocSearchInput.requesterUserId exactly.
818
+ */
819
+ requesterUserId?: string;
820
+ /**
821
+ * task-2071 (C293, MU-3): filter to tasks claimed by this user (assignee_id =
822
+ * value). Used by plan's per-user "draw from my personal backlog" path. Does
823
+ * NOT widen visibility — it narrows an already-authorised result set.
824
+ */
825
+ assigneeId?: string;
826
+ }
827
+ interface OwnerActionInput {
828
+ name: string;
829
+ project_ids?: string[];
830
+ category?: string | null;
831
+ status?: 'todo' | 'waiting' | 'done';
832
+ dependency?: string | null;
833
+ docs_link?: string | null;
834
+ unlocks_task_id?: string | null;
835
+ }
836
+ interface OwnerActionRow extends OwnerActionInput {
837
+ id: string;
838
+ user_id: string;
839
+ project_ids: string[];
840
+ status: 'todo' | 'waiting' | 'done';
841
+ completed_at: string | null;
842
+ created_at: string;
843
+ updated_at: string;
396
844
  }
397
845
  /** Core adapter interface for reading/writing .papi/ markdown files. */
398
846
  interface PapiAdapter {
399
847
  readPlanningLog(): Promise<PlanningLog>;
400
- getSprintHealth(): Promise<SprintHealth>;
401
- getActiveDecisions(): Promise<ActiveDecision[]>;
402
- getSprintLog(limit?: number): Promise<SprintLogEntry[]>;
403
- getSprintLogSince(sprintNumber: number): Promise<SprintLogEntry[]>;
404
- setSprintHealth(updates: Partial<SprintHealth>): Promise<void>;
405
- writeSprintLogEntry(entry: SprintLogEntry): Promise<void>;
406
- updateActiveDecision(id: string, body: string, sprintNumber?: number): Promise<void>;
407
- queryBoard(options?: BoardQueryOptions): Promise<SprintTask[]>;
408
- getTask(id: string): Promise<SprintTask | null>;
409
- getTasks(ids: string[]): Promise<SprintTask[]>;
410
- createTask(task: Omit<SprintTask, 'id'>): Promise<SprintTask>;
411
- updateTask(id: string, updates: Partial<Omit<SprintTask, 'id'>>, options?: UpdateTaskOptions): Promise<void>;
848
+ getCycleHealth(): Promise<CycleHealth>;
849
+ /**
850
+ * Read Active Decisions for this project.
851
+ *
852
+ * Default behaviour (no options or `{ includeRetired: false }`) excludes ADs that
853
+ * are no longer steering the project: outcome ∈ ('abandoned', 'superseded', 'resolved')
854
+ * OR superseded = true. This is the correct signal for context-injection surfaces
855
+ * (orient, build_execute handoffs, plan context, strategy housekeeping displays).
856
+ *
857
+ * Pass `{ includeRetired: true }` for management/triage surfaces that need to see
858
+ * retired ADs explicitly — e.g. strategy_review (so retired ADs can be re-validated
859
+ * or restored), and any code that needs the full ID space (next-AD-number computation,
860
+ * dedupe against existing IDs, lifecycle counts).
861
+ *
862
+ * task-1546 (C242 hot-fix): closes the bug where retired ADs (e.g. AD-26, abandoned
863
+ * in C241) continued to appear in build/plan/orient context as "active" because
864
+ * consumers only filtered the `superseded` boolean and ignored the `outcome` lifecycle.
865
+ */
866
+ getActiveDecisions(options?: {
867
+ includeRetired?: boolean;
868
+ }): Promise<ActiveDecision[]>;
869
+ /** Query ADs from sibling project IDs in the same Supabase instance. pg adapter only. */
870
+ getSiblingAds?(projectIds: string[]): Promise<Array<ActiveDecision & {
871
+ sourceProjectId: string;
872
+ }>>;
873
+ /**
874
+ * task-2139: in-flight tasks from OTHER projects that share THIS project's
875
+ * repo_url. Surfaced in plan prepare context so the planner does not
876
+ * re-schedule work already underway on the shared repo (the C292 duplicate
877
+ * incident). Scoped to repo_url siblings (never the whole DB); returns only
878
+ * the fields the dup-check needs. pg adapter only — md/proxy omit it and the
879
+ * caller feature-checks.
880
+ */
881
+ getSiblingRepoTasks?(): Promise<SiblingRepoTask[]>;
882
+ getCycleLog(limit?: number): Promise<CycleLogEntry[]>;
883
+ getCycleLogSince(cycleNumber: number): Promise<CycleLogEntry[]>;
884
+ setCycleHealth(updates: Partial<CycleHealth>): Promise<void>;
885
+ writeCycleLogEntry(entry: CycleLogEntry): Promise<void>;
886
+ updateActiveDecision(id: string, body: string, cycleNumber?: number, action?: 'confidence_change' | 'modify' | 'resolve' | 'supersede' | 'new' | 'delete'): Promise<void>;
887
+ /** Upsert an AD — creates if display_id doesn't exist, updates if it does. */
888
+ upsertActiveDecision?(id: string, body: string, title: string, confidence: string, cycleNumber: number): Promise<void>;
889
+ /** Delete an AD permanently by display_id. */
890
+ deleteActiveDecision?(id: string): Promise<void>;
891
+ /** After a strategy review, mark all still-pending ADs as confirmed (reviewed, no changes). */
892
+ confirmPendingActiveDecisions?(cycleNumber: number): Promise<void>;
893
+ queryBoard(options?: BoardQueryOptions): Promise<CycleTask[]>;
894
+ getTask(id: string): Promise<CycleTask | null>;
895
+ getTasks(ids: string[]): Promise<CycleTask[]>;
896
+ createTask(task: Omit<CycleTask, 'id'>): Promise<CycleTask>;
897
+ /**
898
+ * task-1917 (C280): Owner Action Queue support.
899
+ * Optional — only adapters backed by a database that has the owner_actions
900
+ * table need to implement these. Callers must feature-check before use:
901
+ * if (adapter.createOwnerAction) { ... }
902
+ */
903
+ createOwnerAction?(userId: string, input: OwnerActionInput): Promise<OwnerActionRow>;
904
+ /**
905
+ * Count rows in owner_actions for the given user that are not yet
906
+ * completed. Used by orient to surface "N actions waiting on you".
907
+ * task-2041 (C284): optional projectId scopes to one project (untagged actions
908
+ * count as global). Omit it for the cross-project /hub aggregate.
909
+ */
910
+ countOpenOwnerActions?(userId: string, projectId?: string): Promise<number>;
911
+ /**
912
+ * Count open owner_actions a delegate has nudged the owner about (task-2018).
913
+ * Surfaced by orient separately from the open-count. Optional — md adapter and
914
+ * pre-v2 deployments simply skip the line. task-2041: optional projectId scope.
915
+ */
916
+ countNudgedOwnerActions?(userId: string, projectId?: string): Promise<number>;
917
+ /** task-2041 (C284): the project this adapter is scoped to (pg adapter only). */
918
+ getProjectId?(): string;
919
+ /**
920
+ * task-2051 (C284): count the user's OPEN owner actions whose unlocks_task_id
921
+ * points at a build task currently 'Blocked'. Surfaced by orient as a
922
+ * "complete these to unblock build work" nudge. Optional — md adapter and
923
+ * pre-status deployments skip the line.
924
+ */
925
+ countOwnerActionsBlockingTasks?(userId: string): Promise<number>;
926
+ /**
927
+ * Team Ops v4: count the user's OPEN owner actions by due date — dueToday
928
+ * (due_date = today) and overdue (due_date < today). Folded into orient's
929
+ * "N actions waiting on you" line. Optional — md adapter and pre-v4
930
+ * deployments skip the suffix. Optional projectId scope as countOpenOwnerActions.
931
+ */
932
+ countDueOwnerActions?(userId: string, projectId?: string): Promise<{
933
+ dueToday: number;
934
+ overdue: number;
935
+ }>;
936
+ updateTask(id: string, updates: Partial<Omit<CycleTask, 'id'>>, options?: UpdateTaskOptions): Promise<void>;
412
937
  updateTaskStatus(id: string, status: TaskStatus): Promise<void>;
938
+ /**
939
+ * task-2182: correct a mis-recorded estimate/actual on the LATEST build report
940
+ * for a task. pg-only (project_id-scoped UPDATE on the MAX(created_at) row); md
941
+ * is legacy and omits it. Carried by board_edit's estimated_effort/actual_effort.
942
+ */
943
+ correctLatestBuildReportEffort?(taskId: string, fields: {
944
+ estimatedEffort?: string;
945
+ actualEffort?: string;
946
+ }): Promise<void>;
947
+ /**
948
+ * task-1763 (C293): atomic compare-and-swap task claim. Sets assignee_id ONLY if
949
+ * the task is currently unclaimed (assignee_id IS NULL) — first-claim-wins, no
950
+ * queueing. Returns the claimed task on success, or null if it was already claimed
951
+ * (or does not exist). assigneeId must be the bearer-derived identity, never client
952
+ * input; the implementation scopes by project_id so a claim can never cross tenant.
953
+ * Optional: the primitive layer for MU-3 (task-2071); proxy/edge wiring lands with
954
+ * the task_claim tool in task-2071.
955
+ */
956
+ claimTask?(taskId: string, assigneeId: string): Promise<CycleTask | null>;
957
+ /**
958
+ * task-2071 (C293, MU-3): atomic claimer-only release. Clears assignee_id (and
959
+ * claim_source) ONLY if the task is currently assigned to `assigneeId` AND has
960
+ * not yet entered review (status NOT IN 'In Review','Done'). Returns the
961
+ * unclaimed task on success, or null if the caller is not the claimer, the task
962
+ * has progressed past claim, or it does not exist. Scoped by project_id.
963
+ */
964
+ unclaimTask?(taskId: string, assigneeId: string): Promise<CycleTask | null>;
965
+ /**
966
+ * task-2072 (C293, MU-4): atomic compare-and-swap REVIEW claim — the cross-user
967
+ * review queue primitive. Sets reviewer_id ONLY if the task is currently In
968
+ * Review and unclaimed-for-review (reviewer_id IS NULL). Returns the claimed
969
+ * task on success, or null if another reviewer already claimed it, it is not In
970
+ * Review, or it does not exist. reviewerId is bearer-derived, never client
971
+ * input; scoped by project_id. First-claim-wins (no double-review race).
972
+ */
973
+ claimReview?(taskId: string, reviewerId: string): Promise<CycleTask | null>;
974
+ /** Record a task status transition for audit trail. */
975
+ recordTransition(taskId: string, fromStatus: string, toStatus: string, changedBy?: string): Promise<void>;
413
976
  appendBuildReport(report: BuildReport): Promise<void>;
414
977
  getRecentBuildReports(count: number): Promise<BuildReport[]>;
415
- getBuildReportsSince(sprintNumber: number): Promise<BuildReport[]>;
978
+ getBuildReportCountForTask(taskId: string): Promise<number>;
979
+ getBuildReportsSince(cycleNumber: number): Promise<BuildReport[]>;
416
980
  getRecentReviews(count?: number): Promise<HumanReview[]>;
417
981
  writeReview(review: HumanReview): Promise<void>;
418
- compressSprintLog(threshold: number, summary: string): Promise<void>;
982
+ compressCycleLog(threshold: number, summary: string): Promise<void>;
419
983
  compressBuildReports(threshold: number, summary: string): Promise<void>;
420
984
  archiveTasks(phases: string[], statuses?: string[]): Promise<{
421
985
  archivedCount: number;
@@ -423,38 +987,210 @@ interface PapiAdapter {
423
987
  }>;
424
988
  readProductBrief(): Promise<string>;
425
989
  updateProductBrief(content: string): Promise<void>;
990
+ /** Read discovery canvas sections from product brief. */
991
+ readDiscoveryCanvas(): Promise<DiscoveryCanvas>;
992
+ /** Update discovery canvas sections on product brief. */
993
+ updateDiscoveryCanvas(canvas: Partial<DiscoveryCanvas>): Promise<void>;
426
994
  readPhases(): Promise<Phase[]>;
427
995
  writePhases(phases: Phase[]): Promise<void>;
428
- readFeatures(): Promise<Feature[]>;
429
- getFeature(id: string): Promise<Feature | null>;
430
- createFeature(feature: Omit<Feature, 'id'>): Promise<Feature>;
431
- updateFeature(id: string, updates: Partial<Omit<Feature, 'id'>>): Promise<void>;
996
+ /** Read all horizons for the project (AD-14 hierarchy). */
997
+ readHorizons?(): Promise<Horizon[]>;
998
+ /** Read all stages for the project, optionally filtered by horizon (AD-14 hierarchy). */
999
+ readStages?(horizonId?: string): Promise<Stage[]>;
1000
+ /** Update a stage's status (e.g. 'Active' → 'Complete'). */
1001
+ updateStageStatus?(stageId: string, status: string): Promise<void>;
1002
+ updateStageExitCriteria?(stageId: string, exitCriteria: string[]): Promise<void>;
1003
+ /** Update a horizon's status (e.g. 'Active' → 'Complete'). */
1004
+ updateHorizonStatus?(horizonId: string, status: string): Promise<void>;
1005
+ /** Update a phase's status (e.g. 'Active' → 'Complete'). */
1006
+ updatePhaseStatus?(phaseId: string, status: string): Promise<void>;
1007
+ /** Get the currently active stage for the project. */
1008
+ getActiveStage?(): Promise<Stage | undefined>;
1009
+ /** Create a new horizon (AD-14 hierarchy). Returns the created horizon's ID. */
1010
+ createHorizon?(horizon: {
1011
+ slug: string;
1012
+ label: string;
1013
+ description?: string;
1014
+ status: HorizonStatus;
1015
+ sortOrder: number;
1016
+ }): Promise<string>;
1017
+ /** Create a new stage (AD-14 hierarchy). Returns the created stage's ID. */
1018
+ createStage?(stage: {
1019
+ slug: string;
1020
+ label: string;
1021
+ description?: string;
1022
+ status: StageStatus;
1023
+ sortOrder: number;
1024
+ horizonId: string;
1025
+ }): Promise<string>;
1026
+ /** Link phases to a stage by setting their stage_id. */
1027
+ linkPhasesToStage?(stageId: string): Promise<void>;
432
1028
  appendToolMetric(metric: ToolCallMetric): Promise<void>;
433
1029
  readToolMetrics(): Promise<ToolCallMetric[]>;
434
- getCostSummary(sprintNumber?: number): Promise<CostSummary>;
435
- writeCostSnapshot(snapshot: CostSnapshot): Promise<void>;
1030
+ /** Count tool calls recorded between two ISO timestamps. Optional — pg only. */
1031
+ getToolCallCount?(startedAt: string, completedAt: string): Promise<number>;
1032
+ /**
1033
+ * Cheap existence check for a single milestone tool name. Returns true when
1034
+ * any tool_call_metrics row matches `tool = name`. Replaces full-table reads
1035
+ * for one-shot orient checks (skill scan, TTFV) which previously pulled up
1036
+ * to 5000 rows just to test for one milestone.
1037
+ */
1038
+ hasToolMilestone?(name: string): Promise<boolean>;
1039
+ getCostSummary(cycleNumber?: number): Promise<CostSummary>;
436
1040
  getCostSnapshots(): Promise<CostSnapshot[]>;
437
- appendSprintMetrics(snapshot: SprintMetricsSnapshot): Promise<void>;
438
- readSprintMetrics(): Promise<SprintMetricsSnapshot[]>;
439
- readSprints(): Promise<Sprint[]>;
440
- createSprint(sprint: Sprint): Promise<void>;
441
- getContextHashes?(sprintNumber: number): Promise<Record<string, string> | null>;
1041
+ appendCycleMetrics(snapshot: CycleMetricsSnapshot): Promise<void>;
1042
+ readCycleMetrics(): Promise<CycleMetricsSnapshot[]>;
1043
+ readCycles(): Promise<Cycle[]>;
1044
+ createCycle(cycle: Cycle): Promise<void>;
1045
+ getContextHashes?(cycleNumber: number): Promise<Record<string, string> | null>;
442
1046
  readRegistries(): Promise<Registries>;
443
1047
  updateRegistries(registries: Registries): Promise<void>;
444
1048
  writeRecommendation(rec: Omit<StrategyRecommendation, 'id'>): Promise<StrategyRecommendation>;
445
1049
  getPendingRecommendations(): Promise<StrategyRecommendation[]>;
446
- actionRecommendation(id: string, sprintNumber: number): Promise<void>;
1050
+ actionRecommendation(id: string, cycleNumber: number): Promise<void>;
1051
+ dismissRecommendation?(id: string, reason: string): Promise<void>;
1052
+ /**
1053
+ * Append a topic to the strategy review agenda queue.
1054
+ * Topics surface at the next strategy_review prepare phase and are marked addressed after apply.
1055
+ */
1056
+ addAgendaTopic?(topic: {
1057
+ topic: string;
1058
+ source: string;
1059
+ sourceCycle?: number;
1060
+ }): Promise<AgendaTopic>;
1061
+ /** List pending (unaddressed) agenda topics for the current project. */
1062
+ getPendingAgendaTopics?(): Promise<AgendaTopic[]>;
1063
+ /** Mark a set of agenda topics as addressed in the given review cycle. */
1064
+ markAgendaTopicsAddressed?(ids: string[], cycleNumber: number): Promise<void>;
447
1065
  appendDecisionEvent(event: Omit<DecisionEvent, 'id' | 'createdAt'>): Promise<DecisionEvent>;
448
1066
  getDecisionEvents(decisionId: string, limit?: number): Promise<DecisionEvent[]>;
449
- getDecisionEventsSince(sprint: number): Promise<DecisionEvent[]>;
1067
+ getDecisionEventsSince(cycle: number): Promise<DecisionEvent[]>;
450
1068
  writeDecisionScore(score: Omit<DecisionScore, 'id' | 'createdAt' | 'totalScore'>): Promise<DecisionScore>;
451
1069
  getDecisionScores(decisionId: string): Promise<DecisionScore[]>;
452
1070
  getLatestDecisionScores(): Promise<DecisionScore[]>;
453
1071
  logEntityReferences(refs: Omit<EntityReference, 'id' | 'createdAt'>[]): Promise<void>;
454
- getDecisionUsage(currentSprint: number): Promise<DecisionUsageSummary[]>;
1072
+ getDecisionUsage(currentCycle: number): Promise<DecisionUsageSummary[]>;
1073
+ /** Get context utilisation summary from v_context_utilisation view. Optional — pg adapter only. */
1074
+ getContextUtilisation?(): Promise<ContextUtilisationSummary[]>;
455
1075
  writeStrategyReview(review: StrategyReviewEntry): Promise<void>;
456
- getLastStrategyReviewSprint(): Promise<number>;
457
- getStrategyReviews(limit?: number): Promise<StrategyReviewEntry[]>;
1076
+ getLastStrategyReviewCycle(): Promise<number>;
1077
+ getStrategyReviews(limit?: number, includeFullAnalysis?: boolean): Promise<StrategyReviewEntry[]>;
1078
+ /** Save a strategy review LLM response that failed write-back, for retry next session. */
1079
+ savePendingReviewResponse?(cycleNumber: number, rawResponse: string): Promise<void>;
1080
+ /** Get the pending review response, if any. Returns null if none. */
1081
+ getPendingReviewResponse?(): Promise<{
1082
+ cycleNumber: number;
1083
+ rawResponse: string;
1084
+ } | null>;
1085
+ /** Clear the pending review response after successful retry. */
1086
+ clearPendingReviewResponse?(): Promise<void>;
1087
+ /** Register a document in the doc registry. */
1088
+ registerDoc?(entry: Omit<DocRegistryEntry, 'id' | 'createdAt' | 'updatedAt'>): Promise<DocRegistryEntry>;
1089
+ /** Search documents by filters. */
1090
+ searchDocs?(input: DocSearchInput): Promise<DocRegistryEntry[]>;
1091
+ /** Get a single doc by ID or path. */
1092
+ getDoc?(idOrPath: string): Promise<DocRegistryEntry | null>;
1093
+ /** Update a doc's status (e.g. supersede, archive). */
1094
+ updateDocStatus?(id: string, status: string, supersededBy?: string): Promise<void>;
1095
+ /**
1096
+ * Update a single action on a doc — used by `doc_action_promote` to mark an action
1097
+ * as resolved and link it to the Backlog task that now owns the work.
1098
+ * `actionIndex` is the 0-based index in the doc's `actions` array.
1099
+ */
1100
+ updateDocAction?(docId: string, actionIndex: number, update: {
1101
+ status?: 'pending' | 'resolved';
1102
+ linkedTaskId?: string;
1103
+ }): Promise<void>;
1104
+ /**
1105
+ * Find every (doc, action) pair whose action references the given task and is still pending.
1106
+ * Used by `submitReview` accept-path to auto-resolve doc actions when their owning task ships
1107
+ * (task-1719). Bounded to 50 results per task — anything beyond is bug-shaped.
1108
+ */
1109
+ findPendingDocActionsForTask?(taskDisplayId: string): Promise<Array<{
1110
+ docId: string;
1111
+ actionIndex: number;
1112
+ description: string;
1113
+ }>>;
1114
+ writeDogfoodEntries?(entries: DogfoodEntry[]): Promise<void>;
1115
+ getDogfoodLog?(limit?: number): Promise<DogfoodEntry[]>;
1116
+ getUnactionedDogfoodEntries?(limit?: number): Promise<DogfoodEntry[]>;
1117
+ updateDogfoodEntryStatus?(id: string, status: DogfoodEntry['status'], linkedTaskId?: string): Promise<void>;
1118
+ /**
1119
+ * Harness inventory (task-1896). Persists per-project skills/agents/hooks/MCP
1120
+ * tools so the dashboard can surface them. All scoped to the adapter's project.
1121
+ * Optional — the md adapter does not implement these; the sync writer is a
1122
+ * no-op when they are absent.
1123
+ */
1124
+ getHarnessInventory?(): Promise<HarnessInventoryEntry[]>;
1125
+ /** Replace the entire inventory for this project in one atomic operation. */
1126
+ replaceHarnessInventory?(entries: HarnessInventoryEntry[]): Promise<void>;
1127
+ /** Read the stored change-detection fingerprint, or null if never synced. */
1128
+ getHarnessState?(): Promise<HarnessState | null>;
1129
+ /** Persist the change-detection fingerprint after a successful sync. */
1130
+ setHarnessState?(fingerprint: string): Promise<void>;
1131
+ /** Append structured cycle learning entries. */
1132
+ appendCycleLearnings?(learnings: CycleLearning[]): Promise<void>;
1133
+ /**
1134
+ * Get cycle learnings, optionally filtered by cycle and/or category.
1135
+ * `includeResolved` defaults to false — resolved discovered-issues (resolved_at IS NOT NULL)
1136
+ * are excluded by default so planner / orient reads stay focused on still-open work (task-1541).
1137
+ */
1138
+ getCycleLearnings?(opts?: {
1139
+ cycleNumber?: number;
1140
+ category?: string;
1141
+ limit?: number;
1142
+ includeResolved?: boolean;
1143
+ }): Promise<CycleLearning[]>;
1144
+ /** Get cross-cycle patterns from learning tags (tags appearing in 2+ cycles). */
1145
+ getCycleLearningPatterns?(): Promise<CycleLearningPattern[]>;
1146
+ /** Set action_ref on a cycle learning to link it to the task that acted on it. */
1147
+ updateCycleLearningActionRef?(learningId: string, taskDisplayId: string): Promise<void>;
1148
+ /**
1149
+ * Mark a cycle learning resolved. Sets resolved_at = NOW() and (optionally) resolved_by.
1150
+ * Pg-only — md / proxy adapters can leave this unimplemented. (task-1541)
1151
+ */
1152
+ markCycleLearningResolved?(learningId: string, resolvedBy?: string): Promise<void>;
1153
+ /**
1154
+ * Auto-resolve discovered-issues (category='issue') whose linked task is Done/Cancelled,
1155
+ * draining the stale-issue clog so orient/planner reads stay clean (task-2079, C291).
1156
+ * Pass a task display_id to resolve only that task's issues (task→Done transition path);
1157
+ * omit for a full sweep. Restricted to category='issue'. Pg-only; returns rows resolved.
1158
+ */
1159
+ resolveLearningsForDoneTasks?(taskDisplayId?: string): Promise<number>;
1160
+ /**
1161
+ * Optional: return the current (unsuperseded) North Star statement.
1162
+ * When implemented, plan and strategy_review use this instead of relying
1163
+ * on the North Star embedded in PLANNING_LOG.md (which may be stale).
1164
+ */
1165
+ getCurrentNorthStar?(): Promise<string | null>;
1166
+ /**
1167
+ * Optional: return the cycle number when the current North Star was set.
1168
+ * Used for drift detection — flags when North Star hasn't been validated in 10+ cycles.
1169
+ */
1170
+ getNorthStarSetCycle?(): Promise<number | null>;
1171
+ /**
1172
+ * Optional: return both cycle number and set_at date for the current North Star.
1173
+ * Used for hybrid drift detection — warns only when both cycle gap AND time gap exceed thresholds.
1174
+ */
1175
+ getNorthStarStaleness?(): Promise<{
1176
+ setCycle: number;
1177
+ setAt: string;
1178
+ } | null>;
1179
+ /**
1180
+ * Optional: persist a new North Star statement, superseding the previous active one.
1181
+ * Called by strategy_review apply and strategy_change apply when the LLM outputs a northStar field.
1182
+ */
1183
+ upsertNorthStar?(statement: string, cycleNumber: number): Promise<void>;
1184
+ /**
1185
+ * Optional: return aggregated estimation calibration data from v_estimation_accuracy.
1186
+ * Used by the planner to adjust effort estimates based on historical patterns.
1187
+ */
1188
+ getEstimationCalibration?(): Promise<EstimationCalibrationRow[]>;
1189
+ /**
1190
+ * Optional: return per-module estimation accuracy stats from the last 20 cycles.
1191
+ * Joins build_reports with cycle_tasks on task_id. pg adapter only.
1192
+ */
1193
+ getModuleEstimationStats?(): Promise<ModuleEstimationRow[]>;
458
1194
  /**
459
1195
  * Optional: return recent task comments for plan context.
460
1196
  * Returns up to `limit` comments across all active tasks, newest first.
@@ -465,6 +1201,11 @@ interface PapiAdapter {
465
1201
  content: string;
466
1202
  createdAt: string;
467
1203
  }[]>;
1204
+ /**
1205
+ * Optional: return recommendation effectiveness data from v_recommendation_effectiveness.
1206
+ * Used by strategy review to assess whether past recommendations were followed.
1207
+ */
1208
+ getRecommendationEffectiveness?(): Promise<RecommendationEffectivenessRow[]>;
468
1209
  /**
469
1210
  * Optional: return a lean pre-formatted plan context summary.
470
1211
  * When implemented (e.g. by pg adapter with SQL aggregates), assembleContext
@@ -472,6 +1213,163 @@ interface PapiAdapter {
472
1213
  * Return undefined to fall back to the default assembly path.
473
1214
  */
474
1215
  getPlanContextSummary?(): Promise<PlanContextSummary | undefined>;
1216
+ /**
1217
+ * Optional: check if the project exists in the data store.
1218
+ * Returns true if the project is found, false otherwise.
1219
+ * When not implemented, defaults to true (assumes project exists).
1220
+ */
1221
+ projectExists?(): Promise<boolean>;
1222
+ /**
1223
+ * Optional: fetch the project name, slug, papi_dir, and repo_url for connection + path-identity verification.
1224
+ * `papi_dir` is the absolute path of the project's `.papi/` directory as stored in the database.
1225
+ * Used by the path-identity guardrail to detect cwd↔stored-path mismatches.
1226
+ * `repo_url` (task-1979, C279) is the GitHub URL the project is associated with, used by the
1227
+ * release tool's remote-project guard. Optional in the result so adapters that don't expose it
1228
+ * can return null without breaking legacy callers.
1229
+ */
1230
+ getProjectInfo?(): Promise<{
1231
+ name: string;
1232
+ slug: string;
1233
+ papi_dir?: string | null;
1234
+ repo_url?: string | null;
1235
+ } | null>;
1236
+ /**
1237
+ * Optional: persist a new value for the project's papi_dir column. Used by the path-identity
1238
+ * guardrail when backfilling legacy null values or applying PAPI_ALLOW_PATH_MIGRATE updates.
1239
+ */
1240
+ setProjectPapiDir?(papiDir: string): Promise<void>;
1241
+ /**
1242
+ * Optional: resolve the user_id that owns the currently-bound project.
1243
+ * Returns null for legacy pre-multi-user projects whose row was created before the
1244
+ * user_id column existed. Used by verifyProject's detect-only legacy-project probe
1245
+ * (task-1621). pg-only — md/proxy adapters can leave it unimplemented.
1246
+ */
1247
+ getProjectOwnerUserId?(): Promise<string | null>;
1248
+ /**
1249
+ * Optional (task-2052, C288): resolve BOTH sides of the owner-gate identity in
1250
+ * one call — who is calling, and who owns the project. On the proxy adapter the
1251
+ * edge function derives callerUserId from the bearer token server-side, so a
1252
+ * hosted/proxy owner is recognised without needing PAPI_USER_ID configured
1253
+ * locally (and a non-owner cannot claim ownership by setting it). Adapters that
1254
+ * cannot resolve the caller (pg/md, where identity comes from local config)
1255
+ * leave this unimplemented and the gate falls back to
1256
+ * getProjectOwnerUserId + config.userId. Consumed by resolveOwnerGate in
1257
+ * packages/server/src/lib/owner-identity.ts.
1258
+ */
1259
+ getOwnerIdentity?(): Promise<OwnerIdentity>;
1260
+ /**
1261
+ * task-2029 (C288): contributor cohort management on the project_contributors
1262
+ * table (MVP slice — current schema, no roles/invites; that's MU-2/task-2070).
1263
+ * Email resolution happens adapter-side (pg: user_profiles join; proxy: edge
1264
+ * function) so the tool layer never touches identity tables directly. The
1265
+ * OWNER-ONLY rule is enforced in the tool layer via resolveOwnerGate AND
1266
+ * (proxy) server-side in the edge function — defence in depth, because the
1267
+ * npm server runs on the user's machine. pg/proxy only; md leaves these
1268
+ * unimplemented (no cohort concept in local single-user mode).
1269
+ */
1270
+ listContributors?(): Promise<ContributorEntry[]>;
1271
+ addContributorByEmail?(email: string): Promise<ContributorEntry>;
1272
+ removeContributorByEmail?(email: string): Promise<boolean>;
1273
+ /**
1274
+ * Optional (task-1888 / 1885-C): list all projects the authenticated user owns.
1275
+ * Scopes to the auth user (proxy: bearer; pg: the bound project's user_id). Used by the
1276
+ * project_list MCP tool so a user on one API key across many folders can see and select
1277
+ * the right project from the LLM.
1278
+ */
1279
+ listUserProjects?(): Promise<ProjectSummary[]>;
1280
+ /**
1281
+ * Optional (task-1888 / 1885-C): create a project for the authenticated user, idempotent on
1282
+ * papi_dir then name — re-running in the same workspace (or with the same name) returns the
1283
+ * EXISTING project instead of creating a duplicate (fixes the SUP-2026-013 duplicate-project
1284
+ * bug). NO auto-plan / NO seeded backlog — an empty project only. `papiDir` is omitted on the
1285
+ * stateless remote transport (no real workspace there).
1286
+ */
1287
+ createUserProject?(input: {
1288
+ name: string;
1289
+ papiDir?: string;
1290
+ repoUrl?: string;
1291
+ }): Promise<ProjectLifecycleResult>;
1292
+ /**
1293
+ * Optional (task-1888 / 1885-C): resolve a project the user owns by id or slug and (when a
1294
+ * workspace signal is available) stamp it as the papi_dir for the current folder. Callable from
1295
+ * the LLM ("switch to golf"). Fails closed if the identifier does not resolve to a project the
1296
+ * auth user owns.
1297
+ */
1298
+ switchUserProject?(identifier: string, papiDir?: string): Promise<ProjectLifecycleResult>;
1299
+ /**
1300
+ * Optional (task-2119): return a view of this adapter scoped to a DIFFERENT
1301
+ * project for a single call, without rebinding the session adapter (unlike
1302
+ * switchUserProject) and without persisting anything. Callers MUST validate
1303
+ * the target project belongs to the auth user first (listUserProjects) —
1304
+ * this method does not gate ownership itself.
1305
+ */
1306
+ withProject?(projectId: string): PapiAdapter;
1307
+ /** Optional: insert a plan run record for telemetry. Non-blocking — callers should catch errors. */
1308
+ insertPlanRun?(entry: PlanRunEntry): Promise<void>;
1309
+ /** Submit a bug report with diagnostics for cross-project visibility. */
1310
+ submitBugReport?(report: Omit<BugReport, 'id' | 'createdAt'>): Promise<BugReport>;
1311
+ /**
1312
+ * Optional (task-2061): the authenticated user's tier + metered tool-call
1313
+ * count for the current month. Only the proxy adapter implements this —
1314
+ * its identity is server-derived from the bearer. Local adapters (pg/md)
1315
+ * deliberately omit it, so local sessions are never metered or blocked.
1316
+ */
1317
+ getMeteredUsage?(): Promise<{
1318
+ tier: string;
1319
+ monthlyToolCalls: number;
1320
+ }>;
1321
+ }
1322
+ /** A project the auth user owns — returned by listUserProjects (task-1888 / 1885-C). */
1323
+ /**
1324
+ * task-2052 (C288): both sides of the owner-gate identity. callerUserId is the
1325
+ * authenticated caller (proxy: bearer-derived server-side; null when the
1326
+ * transport cannot resolve a caller); ownerUserId is the project's owner row.
1327
+ */
1328
+ interface OwnerIdentity {
1329
+ callerUserId: string | null;
1330
+ ownerUserId: string | null;
1331
+ }
1332
+ /**
1333
+ * task-2029 (C288): one row of a project's contributor cohort
1334
+ * (project_contributors joined to user_profiles for the human-readable bits).
1335
+ */
1336
+ interface ContributorEntry {
1337
+ userId: string;
1338
+ email: string | null;
1339
+ displayName: string | null;
1340
+ role: string;
1341
+ createdAt: string;
1342
+ }
1343
+ interface ProjectSummary {
1344
+ id: string;
1345
+ name: string;
1346
+ slug: string;
1347
+ papi_dir?: string | null;
1348
+ }
1349
+ /** Result of project_create / project_switch (task-1888 / 1885-C). */
1350
+ interface ProjectLifecycleResult {
1351
+ projectId: string;
1352
+ name: string;
1353
+ slug: string;
1354
+ papiDir?: string | null;
1355
+ /** True when a new project row was created; false when an existing one was returned (idempotent). */
1356
+ created: boolean;
1357
+ }
1358
+ /** Plan quality telemetry — one row per plan apply. */
1359
+ interface PlanRunEntry {
1360
+ cycleNumber: number;
1361
+ contextBytes?: number;
1362
+ durationMs?: number;
1363
+ taskCountIn?: number;
1364
+ taskCountOut?: number;
1365
+ backlogDepth?: number;
1366
+ notes?: string;
1367
+ /** Context utilisation ratio (0-1) and other token metrics captured at apply time. */
1368
+ tokenUsage?: {
1369
+ utilisation?: number;
1370
+ };
1371
+ /** Origin of this plan run: "mcp-server" (default) or "dashboard". */
1372
+ source?: string;
475
1373
  }
476
1374
  /** Lean plan context returned by adapters that support SQL aggregation. */
477
1375
  interface PlanContextSummary {
@@ -479,25 +1377,25 @@ interface PlanContextSummary {
479
1377
  board: string;
480
1378
  /** Pre-computed build intelligence: accuracy %, velocity trend, top surprises. */
481
1379
  buildIntelligence: string;
482
- /** Last 3 sprint entries with carry-forward. */
483
- sprintLog: string;
1380
+ /** Last 3 cycle entries with carry-forward. */
1381
+ cycleLog: string;
484
1382
  /** One-liner per active decision: id, title, confidence. */
485
1383
  activeDecisions: string;
486
1384
  }
487
1385
  /** Everything the plan write-back needs to persist in a single transaction. */
488
1386
  interface PlanWriteBackPayload {
489
- /** The NEW sprint number (current + 1). */
490
- sprintNumber: number;
491
- /** Sprint log entry to upsert. */
492
- sprintLog: SprintLogEntry;
1387
+ /** The NEW cycle number (current + 1). */
1388
+ cycleNumber: number;
1389
+ /** Cycle log entry to upsert. */
1390
+ cycleLog: CycleLogEntry;
493
1391
  /** Health updates: boardHealth, strategicDirection, etc. */
494
- healthUpdates: Partial<SprintHealth>;
1392
+ healthUpdates: Partial<CycleHealth>;
495
1393
  /** New tasks to create (already deduplicated by plan service). */
496
- newTasks: Array<Omit<SprintTask, 'id' | 'uuid' | 'displayId'>>;
1394
+ newTasks: Array<Omit<CycleTask, 'id' | 'uuid' | 'displayId'>>;
497
1395
  /** Board corrections: taskId + partial updates. */
498
1396
  boardCorrections: Array<{
499
1397
  taskId: string;
500
- updates: Partial<Omit<SprintTask, 'id'>>;
1398
+ updates: Partial<Omit<CycleTask, 'id'>>;
501
1399
  }>;
502
1400
  /** Phases parsed from product brief update. Empty array = no phase changes. */
503
1401
  phases: Phase[];
@@ -513,10 +1411,8 @@ interface PlanWriteBackPayload {
513
1411
  taskId: string;
514
1412
  handoff: BuildHandoff;
515
1413
  }>;
516
- /** Sprint entity to create. */
517
- sprint: Sprint;
518
- /** Whether to run feature sync from phases. */
519
- featureSync: boolean;
1414
+ /** Cycle entity to create. */
1415
+ cycle: Cycle;
520
1416
  /** IDs of tasks whose reviewed status should be checked before allowing priority changes. */
521
1417
  reviewedTaskIds: string[];
522
1418
  }
@@ -540,33 +1436,40 @@ declare class MdFileAdapter implements PapiAdapter {
540
1436
  private read;
541
1437
  /** Write UTF-8 text to a .papi/ file. */
542
1438
  private write;
543
- /** Parse the full planning context into structured sections (reads from PLANNING_LOG.md + ACTIVE_DECISIONS.md + SPRINT_LOG.md). */
1439
+ /** Parse the full planning context into structured sections (reads from PLANNING_LOG.md + ACTIVE_DECISIONS.md + CYCLE_LOG.md). */
544
1440
  readPlanningLog(): Promise<PlanningLog>;
545
- /** Read the Sprint Health table from PLANNING_LOG.md. */
546
- getSprintHealth(): Promise<SprintHealth>;
547
- /** Read all Active Decisions from ACTIVE_DECISIONS.md. */
548
- getActiveDecisions(): Promise<ActiveDecision[]>;
549
- /** Read sprint log entries (newest first), optionally limited to {@link limit} entries. */
550
- getSprintLog(limit?: number): Promise<SprintLogEntry[]>;
551
- getSprintLogSince(sprintNumber: number): Promise<SprintLogEntry[]>;
552
- /** Merge partial updates into the Sprint Health table and write back. */
553
- setSprintHealth(updates: Partial<SprintHealth>): Promise<void>;
554
- /** Prepend a new sprint log entry at the top of the Sprint Log section. */
555
- writeSprintLogEntry(entry: SprintLogEntry): Promise<void>;
556
- /** Write a strategy review for md adapter, delegates to sprint log. */
1441
+ /** Read the Cycle Health table from PLANNING_LOG.md. */
1442
+ getCycleHealth(): Promise<CycleHealth>;
1443
+ /**
1444
+ * Read Active Decisions from ACTIVE_DECISIONS.md.
1445
+ *
1446
+ * Default filters out retired ADs (outcome ∈ abandoned/superseded/resolved or superseded=true).
1447
+ * Pass { includeRetired: true } for management/triage surfaces. See PapiAdapter docstring.
1448
+ */
1449
+ getActiveDecisions(options?: {
1450
+ includeRetired?: boolean;
1451
+ }): Promise<ActiveDecision[]>;
1452
+ /** Read cycle log entries (newest first), optionally limited to {@link limit} entries. */
1453
+ getCycleLog(limit?: number): Promise<CycleLogEntry[]>;
1454
+ getCycleLogSince(cycleNumber: number): Promise<CycleLogEntry[]>;
1455
+ /** Merge partial updates into the Cycle Health table and write back. */
1456
+ setCycleHealth(updates: Partial<CycleHealth>): Promise<void>;
1457
+ /** Prepend a new cycle log entry at the top of the Cycle Log section. */
1458
+ writeCycleLogEntry(entry: CycleLogEntry): Promise<void>;
1459
+ /** Write a strategy review — for md adapter, delegates to cycle log. */
557
1460
  writeStrategyReview(review: StrategyReviewEntry): Promise<void>;
558
- /** Get the sprint number of the last strategy review. */
559
- getLastStrategyReviewSprint(): Promise<number>;
560
- /** Get strategy reviews — md adapter returns empty (reviews live in sprint log). */
561
- getStrategyReviews(_limit?: number): Promise<StrategyReviewEntry[]>;
1461
+ /** Get the cycle number of the last strategy review. */
1462
+ getLastStrategyReviewCycle(): Promise<number>;
1463
+ /** Get strategy reviews — md adapter returns empty (reviews live in cycle log). */
1464
+ getStrategyReviews(_limit?: number, _includeFullAnalysis?: boolean): Promise<StrategyReviewEntry[]>;
562
1465
  /** Update or insert an Active Decision block by ID. */
563
- updateActiveDecision(id: string, body: string, sprintNumber?: number): Promise<void>;
564
- /** Query the sprint board, optionally filtering by status/priority/phase/etc. */
565
- queryBoard(options?: BoardQueryOptions): Promise<SprintTask[]>;
1466
+ updateActiveDecision(id: string, body: string, cycleNumber?: number, _action?: string): Promise<void>;
1467
+ /** Query the cycle board, optionally filtering by status/priority/phase/etc. */
1468
+ queryBoard(options?: BoardQueryOptions): Promise<CycleTask[]>;
566
1469
  /** Look up a single task by ID, returning null if not found. */
567
- getTask(id: string): Promise<SprintTask | null>;
1470
+ getTask(id: string): Promise<CycleTask | null>;
568
1471
  /** Look up multiple tasks by ID in a single board read. */
569
- getTasks(ids: string[]): Promise<SprintTask[]>;
1472
+ getTasks(ids: string[]): Promise<CycleTask[]>;
570
1473
  /** Warn if a phase name doesn't match any known phase label from PRODUCT_BRIEF.md. */
571
1474
  private warnInvalidPhase;
572
1475
  /** Warn if a module name doesn't match any registered module in REGISTRIES.md. */
@@ -574,32 +1477,61 @@ declare class MdFileAdapter implements PapiAdapter {
574
1477
  /** Warn if an epic name doesn't match any registered epic in REGISTRIES.md. */
575
1478
  private warnInvalidEpic;
576
1479
  /** Create a new task on the board with an auto-generated sequential ID. */
577
- createTask(task: Omit<SprintTask, 'id'>): Promise<SprintTask>;
1480
+ createTask(task: Omit<CycleTask, 'id'>): Promise<CycleTask>;
578
1481
  /** Update one or more fields on an existing task. Throws if the task ID is not found. */
579
- updateTask(id: string, updates: Partial<Omit<SprintTask, 'id'>>, options?: UpdateTaskOptions): Promise<void>;
1482
+ updateTask(id: string, updates: Partial<Omit<CycleTask, 'id'>>, options?: UpdateTaskOptions): Promise<void>;
580
1483
  /** Shorthand to update only the status field of a task. */
581
1484
  updateTaskStatus(id: string, status: TaskStatus): Promise<void>;
1485
+ /**
1486
+ * task-1763 (C293): atomic compare-and-swap task claim. First-claim-wins —
1487
+ * sets assigneeId only if the task is currently unclaimed. The markdown adapter
1488
+ * is single-process, so the read-check-write is trivially atomic here; the real
1489
+ * concurrency guarantee lives in the pg adapter's RETURNING CAS. Returns the
1490
+ * claimed task, or null if it was already claimed or does not exist.
1491
+ *
1492
+ * task-2071 (MU-3): the pooled-task invariant is `assigneeId == null && cycle
1493
+ * == null` — a task already pulled into someone's cycle is NOT in the pool and
1494
+ * cannot be claimed. Sets claimSource='pool' on success.
1495
+ */
1496
+ claimTask(taskId: string, assigneeId: string): Promise<CycleTask | null>;
1497
+ /**
1498
+ * task-2071 (C293, MU-3): claimer-only release. Clears assigneeId + claimSource
1499
+ * only if the task is currently assigned to `assigneeId` and has not entered
1500
+ * review. Returns the unclaimed task, or null if the caller is not the claimer,
1501
+ * the task has progressed, or it does not exist.
1502
+ */
1503
+ unclaimTask(taskId: string, assigneeId: string): Promise<CycleTask | null>;
1504
+ /**
1505
+ * task-2072 (C293, MU-4): atomic review claim. Sets reviewerId only if the task
1506
+ * is In Review and not yet claimed for review (reviewerId == null). First-claim-
1507
+ * wins. Returns the claimed task, or null if already review-claimed / not In
1508
+ * Review / missing.
1509
+ */
1510
+ claimReview(taskId: string, reviewerId: string): Promise<CycleTask | null>;
1511
+ recordTransition(_taskId: string, _fromStatus: string, _toStatus: string, _changedBy?: string): Promise<void>;
582
1512
  /** Insert a new build report at the top of BUILD_REPORTS.md. */
583
1513
  appendBuildReport(report: BuildReport): Promise<void>;
584
1514
  /** Return the most recent {@link count} build reports. */
585
1515
  getRecentBuildReports(count: number): Promise<BuildReport[]>;
586
- /** Return all build reports from sprints >= {@link sprintNumber}. */
587
- getBuildReportsSince(sprintNumber: number): Promise<BuildReport[]>;
1516
+ /** Return the number of build reports for a specific task. */
1517
+ getBuildReportCountForTask(taskId: string): Promise<number>;
1518
+ /** Return all build reports from cycles >= {@link cycleNumber}. */
1519
+ getBuildReportsSince(cycleNumber: number): Promise<BuildReport[]>;
588
1520
  /** Return recent human reviews from REVIEWS.md (newest first), optionally limited to {@link count}. */
589
1521
  getRecentReviews(count?: number): Promise<HumanReview[]>;
590
1522
  /** Write a new human review to REVIEWS.md. */
591
1523
  writeReview(review: HumanReview): Promise<void>;
592
- /** Compress old sprint log entries below {@link threshold} into a summary block. */
593
- compressSprintLog(threshold: number, summary: string): Promise<void>;
1524
+ /** Compress old cycle log entries below {@link threshold} into a summary block. */
1525
+ compressCycleLog(threshold: number, summary: string): Promise<void>;
594
1526
  /** Compress old build reports below {@link threshold} into a summary block. */
595
1527
  compressBuildReports(threshold: number, summary: string): Promise<void>;
596
1528
  /** Read a .papi/ file, returning empty string if it doesn't exist. */
597
1529
  private readOptional;
598
1530
  /** Strip build_handoff and build_report from a task before archiving — these are already in BUILD_REPORTS.md. */
599
1531
  private stripHeavyFields;
600
- /** Append tasks to ARCHIVE_SPRINT_BOARD.md, stripping heavy fields and deduplicating by ID. */
1532
+ /** Append tasks to ARCHIVE_CYCLE_BOARD.md, stripping heavy fields and deduplicating by ID. */
601
1533
  private appendToArchive;
602
- /** Archive tasks matching phases and/or statuses to ARCHIVE_SPRINT_BOARD.md and remove them from active board. */
1534
+ /** Archive tasks matching phases and/or statuses to ARCHIVE_CYCLE_BOARD.md and remove them from active board. */
603
1535
  archiveTasks(phases: string[], statuses?: string[]): Promise<{
604
1536
  archivedCount: number;
605
1537
  taskIds: string[];
@@ -608,38 +1540,29 @@ declare class MdFileAdapter implements PapiAdapter {
608
1540
  readProductBrief(): Promise<string>;
609
1541
  /** Overwrite PRODUCT_BRIEF.md with new content. */
610
1542
  updateProductBrief(content: string): Promise<void>;
1543
+ readDiscoveryCanvas(): Promise<DiscoveryCanvas>;
1544
+ updateDiscoveryCanvas(_canvas: Partial<DiscoveryCanvas>): Promise<void>;
611
1545
  /** Read all phases from PHASES.md (falls back to PRODUCT_BRIEF.md for migration). */
612
1546
  readPhases(): Promise<Phase[]>;
613
1547
  /** Write phases to PHASES.md. */
614
1548
  writePhases(phases: Phase[]): Promise<void>;
615
- /** Read all features from FEATURES.md. */
616
- readFeatures(): Promise<Feature[]>;
617
- /** Get a single feature by UUID. */
618
- getFeature(id: string): Promise<Feature | null>;
619
- /** Create a new feature and append it to FEATURES.md. */
620
- createFeature(feature: Omit<Feature, 'id'>): Promise<Feature>;
621
- /** Update an existing feature by UUID. */
622
- updateFeature(id: string, updates: Partial<Omit<Feature, 'id'>>): Promise<void>;
623
- /** Write all features to FEATURES.md. */
624
- private writeFeatures;
625
1549
  /** Append a tool call metric entry to METRICS.md. */
626
1550
  appendToolMetric(metric: ToolCallMetric): Promise<void>;
627
1551
  /** Read all tool call metrics from METRICS.md. */
628
1552
  readToolMetrics(): Promise<ToolCallMetric[]>;
629
- /** Aggregate tool call metrics into a cost summary, optionally filtered by sprint. */
630
- getCostSummary(sprintNumber?: number): Promise<CostSummary>;
631
- /** Write a cost snapshot to the Cost Summary section of METRICS.md. */
632
- writeCostSnapshot(snapshot: CostSnapshot): Promise<void>;
1553
+ hasToolMilestone(name: string): Promise<boolean>;
1554
+ /** Aggregate tool call metrics into a cost summary, optionally filtered by cycle. */
1555
+ getCostSummary(cycleNumber?: number): Promise<CostSummary>;
633
1556
  /** Read all cost snapshots from the Cost Summary section of METRICS.md. */
634
1557
  getCostSnapshots(): Promise<CostSnapshot[]>;
635
- /** Append a sprint metrics snapshot to SPRINT_METRICS.md. */
636
- appendSprintMetrics(snapshot: SprintMetricsSnapshot): Promise<void>;
637
- /** Read all sprint metrics snapshots from SPRINT_METRICS.md. */
638
- readSprintMetrics(): Promise<SprintMetricsSnapshot[]>;
639
- /** Read all Sprint entities from SPRINTS.md (newest first). */
640
- readSprints(): Promise<Sprint[]>;
641
- /** Write a new Sprint entity to SPRINTS.md. */
642
- createSprint(sprint: Sprint): Promise<void>;
1558
+ /** Append a cycle metrics snapshot to CYCLE_METRICS.md. */
1559
+ appendCycleMetrics(snapshot: CycleMetricsSnapshot): Promise<void>;
1560
+ /** Read all cycle metrics snapshots from CYCLE_METRICS.md. */
1561
+ readCycleMetrics(): Promise<CycleMetricsSnapshot[]>;
1562
+ /** Read all Cycle entities from CYCLES.md (newest first). */
1563
+ readCycles(): Promise<Cycle[]>;
1564
+ /** Write a new Cycle entity to CYCLES.md. */
1565
+ createCycle(cycle: Cycle): Promise<void>;
643
1566
  /** Read module and epic registries from REGISTRIES.md. */
644
1567
  readRegistries(): Promise<Registries>;
645
1568
  /** Overwrite REGISTRIES.md with updated registries. */
@@ -652,17 +1575,31 @@ declare class MdFileAdapter implements PapiAdapter {
652
1575
  /** Read all pending (unactioned) strategy recommendations. */
653
1576
  getPendingRecommendations(): Promise<StrategyRecommendation[]>;
654
1577
  /** Mark a recommendation as actioned. */
655
- actionRecommendation(id: string, sprintNumber: number): Promise<void>;
1578
+ actionRecommendation(id: string, cycleNumber: number): Promise<void>;
1579
+ addAgendaTopic(input: {
1580
+ topic: string;
1581
+ source: string;
1582
+ sourceCycle?: number;
1583
+ }): Promise<AgendaTopic>;
1584
+ getPendingAgendaTopics(): Promise<AgendaTopic[]>;
1585
+ markAgendaTopicsAddressed(ids: string[], cycleNumber: number): Promise<void>;
656
1586
  appendDecisionEvent(event: Omit<DecisionEvent, 'id' | 'createdAt'>): Promise<DecisionEvent>;
657
1587
  getDecisionEvents(decisionId: string, limit?: number): Promise<DecisionEvent[]>;
658
- getDecisionEventsSince(sprint: number): Promise<DecisionEvent[]>;
1588
+ getDecisionEventsSince(cycle: number): Promise<DecisionEvent[]>;
659
1589
  private parseDecisionEvents;
660
1590
  writeDecisionScore(score: Omit<DecisionScore, 'id' | 'createdAt' | 'totalScore'>): Promise<DecisionScore>;
661
1591
  getDecisionScores(decisionId: string): Promise<DecisionScore[]>;
662
1592
  getLatestDecisionScores(): Promise<DecisionScore[]>;
663
1593
  private parseDecisionScores;
664
1594
  logEntityReferences(_refs: Omit<EntityReference, 'id' | 'createdAt'>[]): Promise<void>;
665
- getDecisionUsage(_currentSprint: number): Promise<DecisionUsageSummary[]>;
1595
+ getDecisionUsage(_currentCycle: number): Promise<DecisionUsageSummary[]>;
1596
+ getCurrentNorthStar(): Promise<string | null>;
1597
+ getNorthStarSetCycle(): Promise<number | null>;
1598
+ getNorthStarStaleness(): Promise<{
1599
+ setCycle: number;
1600
+ setAt: string;
1601
+ } | null>;
1602
+ upsertNorthStar(statement: string, _cycleNumber: number): Promise<void>;
666
1603
  }
667
1604
 
668
1605
  /** Validate and normalise an effort string to EffortSize. Returns the value or undefined if invalid. */
@@ -674,12 +1611,12 @@ declare function parseEffortSize(value: string): EffortSize | undefined;
674
1611
  */
675
1612
  declare function parseBuildHandoff(markdown: string): BuildHandoff | null;
676
1613
  /** Serialize a BuildHandoff object back into the standard markdown format. */
677
- declare function serializeBuildHandoff(handoff: BuildHandoff): string;
1614
+ declare function serializeBuildHandoff(raw: BuildHandoff | string): string;
678
1615
 
679
- /** Calculate Tier 1 sprint metrics from build reports. */
680
- declare function calculateSprintMetrics(reports: BuildReport[], currentSprint: number, window?: number): {
681
- accuracy: SprintEstimationAccuracy[];
682
- velocity: SprintVelocity[];
1616
+ /** Calculate Tier 1 cycle metrics from build reports. */
1617
+ declare function calculateCycleMetrics(reports: BuildReport[], currentCycle: number, window?: number): {
1618
+ accuracy: CycleEstimationAccuracy[];
1619
+ velocity: CycleVelocity[];
683
1620
  };
684
1621
  /**
685
1622
  * Detect recurring patterns across build reports.
@@ -687,7 +1624,7 @@ declare function calculateSprintMetrics(reports: BuildReport[], currentSprint: n
687
1624
  * When a TextClusterer is provided, uses LLM-based semantic clustering for surprises
688
1625
  * instead of normalised string matching.
689
1626
  */
690
- declare function detectBuildPatterns(reports: BuildReport[], currentSprint: number, window?: number, clusterer?: TextClusterer): Promise<BuildPatterns>;
1627
+ declare function detectBuildPatterns(reports: BuildReport[], currentCycle: number, window?: number, clusterer?: TextClusterer): Promise<BuildPatterns>;
691
1628
  /** Returns true if the patterns contain any actionable findings. */
692
1629
  declare function hasBuildPatterns(patterns: BuildPatterns): boolean;
693
1630
 
@@ -704,7 +1641,7 @@ declare function prependReview(review: HumanReview, content: string): string;
704
1641
  * When a TextClusterer is provided, uses LLM-based semantic clustering for feedback
705
1642
  * instead of normalised string matching.
706
1643
  */
707
- declare function detectReviewPatterns(reviews: HumanReview[], currentSprint: number, window?: number, clusterer?: TextClusterer): Promise<ReviewPatterns>;
1644
+ declare function detectReviewPatterns(reviews: HumanReview[], currentCycle: number, window?: number, clusterer?: TextClusterer): Promise<ReviewPatterns>;
708
1645
  /** Returns true if the patterns contain any actionable findings. */
709
1646
  declare function hasReviewPatterns(patterns: ReviewPatterns): boolean;
710
1647
 
@@ -714,8 +1651,8 @@ declare function parseToolMetrics(content: string): ToolCallMetric[];
714
1651
  declare function serializeToolMetric(metric: ToolCallMetric): string;
715
1652
  /** Append a tool metric row to METRICS.md content. Creates the file template if content is empty. */
716
1653
  declare function appendToolMetricToContent(metric: ToolCallMetric, content: string): string;
717
- /** Aggregate tool call metrics into a cost summary, optionally filtered by sprint. */
718
- declare function aggregateCostSummary(metrics: ToolCallMetric[], sprintNumber?: number): CostSummary;
1654
+ /** Aggregate tool call metrics into a cost summary, optionally filtered by cycle. */
1655
+ declare function aggregateCostSummary(metrics: ToolCallMetric[], cycleNumber?: number): CostSummary;
719
1656
 
720
1657
  /** Parse the PHASES YAML section from PRODUCT_BRIEF.md content. */
721
1658
  declare function parsePhases(content: string): Phase[];
@@ -724,21 +1661,14 @@ declare function serializePhases(phases: Phase[]): string;
724
1661
  /** Serialize phases and write them into the PHASES section of PRODUCT_BRIEF.md content. */
725
1662
  declare function writePhasesToContent(phases: Phase[], content: string): string;
726
1663
 
727
- /** Parse the FEATURES YAML section from FEATURES.md content. */
728
- declare function parseFeatures(content: string): Feature[];
729
- /** Serialize an array of features to a YAML block. */
730
- declare function serializeFeatures(features: Feature[]): string;
731
- /** Serialize features and write them into the FEATURES section of FEATURES.md content. */
732
- declare function writeFeaturesToContent(features: Feature[], content: string): string;
733
-
734
- /** Parse SPRINTS.md content into an array of Sprint objects (newest first). */
735
- declare function parseSprints(content: string): Sprint[];
736
- /** Serialize sprints to a complete SPRINTS.md file content. */
737
- declare function serializeSprints(sprints: Sprint[], content: string): string;
738
- /** Serialize a single Sprint to a YAML string (for display/export). */
739
- declare function serializeSprint(sprint: Sprint): string;
740
- /** Add a new sprint to SPRINTS.md (prepended to the array so newest is first). */
741
- declare function prependSprint(sprint: Sprint, content: string): string;
1664
+ /** Parse CYCLES.md content into an array of Cycle objects (newest first). */
1665
+ declare function parseCycles(content: string): Cycle[];
1666
+ /** Serialize cycles to a complete CYCLES.md file content. */
1667
+ declare function serializeCycles(cycles: Cycle[], content: string): string;
1668
+ /** Serialize a single Cycle to a YAML string (for display/export). */
1669
+ declare function serializeCycle(cycle: Cycle): string;
1670
+ /** Add a new cycle to CYCLES.md (prepended to the array so newest is first). */
1671
+ declare function prependCycle(cycle: Cycle, content: string): string;
742
1672
 
743
1673
  /** Parse REGISTRIES.md content into a Registries object. */
744
1674
  declare function parseRegistries(content: string): Registries;
@@ -782,6 +1712,9 @@ interface Project {
782
1712
  name: string;
783
1713
  repo_url?: string;
784
1714
  papi_dir?: string;
1715
+ user_id?: string;
1716
+ root_commit_hash?: string;
1717
+ resolution_method?: string;
785
1718
  created_at: string;
786
1719
  updated_at: string;
787
1720
  }
@@ -901,4 +1834,4 @@ interface ConflictAlert {
901
1834
  resolved_at?: string;
902
1835
  }
903
1836
 
904
- export { type Acknowledgement, type AcknowledgementStatus, type ActiveDecision, type BoardQueryOptions, type BuildHandoff, type BuildPatterns, type BuildReport, type ClusterEntry, type ClusterResult, type CommandCost, type Confidence, type ConflictAlert, type ConflictAlertStatus, type ConflictRaisedBy, type ConflictType, type ContextHashes, type ContextTier, type CostSnapshot, type CostSummary, type DecisionEvent, type DecisionEventSource, type DecisionEventType, type DecisionScore, type DecisionUsageSummary, type EffortSize, type EntityReference, type Feature, type FeatureStatus, type HumanReview, MdFileAdapter, type MilestoneDependency, type NorthStar, type PapiAdapter, type Phase, type PhaseStatus, type PlanContextSummary, type PlanWriteBackPayload, type PlanWriteBackResult, type PlanningLog, type Project, type ProjectContribution, type ProjectContributionStatus, type RecommendationStatus, type RecommendationType, type RecurringFeedback, type RecurringSurprise, type Registries, type ReviewPatterns, type ReviewStage, type ReviewVerdict, type SharedDecision, type SharedDecisionConfidence, type SharedDecisionStatus, type SharedMilestone, type SharedMilestoneStatus, type Sprint, type SprintEstimationAccuracy, type SprintHealth, type SprintLogEntry, type SprintMetricsSnapshot, type SprintStatus, type SprintTask, type SprintVelocity, type StateTransition, type StrategyRecommendation, type StrategyReviewEntry, TASK_TYPE_TIERS, type TaskComplexity, type TaskMaturity, type TaskPriority, type TaskStatus, type TaskType, type TextClusterer, type ToolCallMetric, type UpdateTaskOptions, VALID_TRANSITIONS, aggregateCostSummary, appendToolMetricToContent, calculateSprintMetrics, detectBuildPatterns, detectReviewPatterns, hasBuildPatterns, hasReviewPatterns, isValidStatus, isValidTransition, parseBuildHandoff, parseEffortSize, parseFeatures, parsePhases, parseRegistries, parseReviews, parseSprints, parseToolMetrics, prependReview, prependSprint, serializeBuildHandoff, serializeFeatures, serializePhases, serializeRegistries, serializeReview, serializeSprint, serializeSprints, serializeToolMetric, validateTransition, writeFeaturesToContent, writePhasesToContent };
1837
+ export { type Acknowledgement, type AcknowledgementStatus, type ActiveDecision, type AgendaTopic, type AgendaTopicStatus, type AutoReview, type AutoReviewFinding, type AutoReviewVerdict, type BoardQueryOptions, type BriefImplication, type BugReport, type BuildHandoff, type BuildPatterns, type BuildReport, type ClusterEntry, type ClusterResult, type CommandCost, type Confidence, type ConflictAlert, type ConflictAlertStatus, type ConflictRaisedBy, type ConflictType, type ContextHashes, type ContextTier, type ContextUtilisationSummary, type ContributorEntry, type CostSnapshot, type CostSummary, type Cycle, type CycleEstimationAccuracy, type CycleHealth, type CycleLearning, type CycleLearningPattern, type CycleLogEntry, type CycleMetricsSnapshot, type CycleStatus, type CycleTask, type CycleVelocity, type DecisionEvent, type DecisionEventSource, type DecisionEventType, type DecisionScore, type DecisionUsageSummary, type DiscoveryCanvas, type DocRegistryEntry, type DocSearchInput, type DocVisibility, type DogfoodEntry, type EffortSize, type EntityReference, type EstimationCalibrationRow, type HarnessInventoryEntry, type HarnessState, type Horizon, type HorizonStatus, type HumanReview, MdFileAdapter, type MetricDelta, type MilestoneDependency, type ModuleEstimationRow, type NorthStar, type OwnerActionInput, type OwnerActionRow, type OwnerIdentity, type PapiAdapter, type Phase, type PhaseStatus, type PlanContextSummary, type PlanRunEntry, type PlanWriteBackPayload, type PlanWriteBackResult, type PlanningLog, type Project, type ProjectContribution, type ProjectContributionStatus, type ProjectLifecycleResult, type ProjectSummary, type RecommendationEffectivenessRow, type RecommendationStatus, type RecommendationType, type RecurringFeedback, type RecurringSurprise, type Registries, type ReviewPatterns, type ReviewStage, type ReviewVerdict, type SharedDecision, type SharedDecisionConfidence, type SharedDecisionStatus, type SharedMilestone, type SharedMilestoneStatus, type SiblingRepoTask, type Stage, type StageStatus, type StateTransition, type StrategyRecommendation, type StrategyReviewEntry, TASK_TYPE_TIERS, type TaskComplexity, type TaskMaturity, type TaskPriority, type TaskStatus, type TaskType, type TextClusterer, type ToolCallMetric, type UpdateTaskOptions, VALID_TRANSITIONS, aggregateCostSummary, appendToolMetricToContent, calculateCycleMetrics, detectBuildPatterns, detectReviewPatterns, hasBuildPatterns, hasReviewPatterns, isValidStatus, isValidTransition, parseBuildHandoff, parseCycles, parseEffortSize, parsePhases, parseRegistries, parseReviews, parseToolMetrics, prependCycle, prependReview, serializeBuildHandoff, serializeCycle, serializeCycles, serializePhases, serializeRegistries, serializeReview, serializeToolMetric, validateTransition, writePhasesToContent };