gsd-pi 2.82.0-dev.725028083 → 2.82.0-dev.ed17d078d

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 (62) hide show
  1. package/dist/resources/.managed-resources-content-hash +1 -1
  2. package/dist/resources/extensions/gsd/auto/orchestrator.js +113 -6
  3. package/dist/resources/extensions/gsd/auto.js +121 -30
  4. package/dist/resources/extensions/gsd/md-importer.js +1 -1
  5. package/dist/resources/extensions/gsd/migrate/command.js +5 -0
  6. package/dist/resources/extensions/gsd/migrate/preview.js +9 -0
  7. package/dist/resources/extensions/gsd/migrate/transformer.js +51 -4
  8. package/dist/resources/extensions/gsd/migrate/writer.js +11 -1
  9. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  10. package/dist/web/standalone/.next/BUILD_ID +1 -1
  11. package/dist/web/standalone/.next/app-path-routes-manifest.json +17 -17
  12. package/dist/web/standalone/.next/build-manifest.json +2 -2
  13. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  14. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  15. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  16. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  17. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  18. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  19. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  20. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  21. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  22. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  23. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  24. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  25. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  26. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  27. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  28. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  29. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  30. package/dist/web/standalone/.next/server/app/index.html +1 -1
  31. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  32. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  33. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  34. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  35. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  36. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  37. package/dist/web/standalone/.next/server/app-paths-manifest.json +17 -17
  38. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  39. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  40. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  41. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  42. package/package.json +1 -1
  43. package/packages/pi-tui/dist/tui.d.ts.map +1 -1
  44. package/packages/pi-tui/dist/tui.js +5 -0
  45. package/packages/pi-tui/dist/tui.js.map +1 -1
  46. package/packages/pi-tui/src/tui.ts +6 -0
  47. package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
  48. package/src/resources/extensions/gsd/auto/contracts.ts +46 -11
  49. package/src/resources/extensions/gsd/auto/orchestrator.ts +118 -6
  50. package/src/resources/extensions/gsd/auto.ts +129 -31
  51. package/src/resources/extensions/gsd/md-importer.ts +1 -1
  52. package/src/resources/extensions/gsd/migrate/command.ts +5 -0
  53. package/src/resources/extensions/gsd/migrate/preview.ts +10 -0
  54. package/src/resources/extensions/gsd/migrate/transformer.ts +58 -4
  55. package/src/resources/extensions/gsd/migrate/writer.ts +14 -1
  56. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +408 -4
  57. package/src/resources/extensions/gsd/tests/auto-runtime-state.test.ts +4 -4
  58. package/src/resources/extensions/gsd/tests/integration/migrate-command.test.ts +48 -3
  59. package/src/resources/extensions/gsd/tests/migrate-transformer.test.ts +5 -1
  60. package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +6 -1
  61. /package/dist/web/standalone/.next/static/{KDRTXR-22LPCsa80X9dey → YEvjuT-fsFfYQhDSWtueS}/_buildManifest.js +0 -0
  62. /package/dist/web/standalone/.next/static/{KDRTXR-22LPCsa80X9dey → YEvjuT-fsFfYQhDSWtueS}/_ssgManifest.js +0 -0
@@ -240,12 +240,14 @@ import { runAutoLoopWithUok } from "./uok/kernel.js";
240
240
  import { resolveUokFlags } from "./uok/flags.js";
241
241
  import { validateDirectory } from "./validate-directory.js";
242
242
  import { createAutoOrchestrator } from "./auto/orchestrator.js";
243
- import type { AutoOrchestrationModule, AutoOrchestratorDeps } from "./auto/contracts.js";
243
+ import type { AutoOrchestrationModule, AutoOrchestratorDeps, DispatchAdapter } from "./auto/contracts.js";
244
244
  import { reconcileBeforeDispatch } from "./state-reconciliation.js";
245
245
  import { compileUnitToolContract } from "./tool-contract.js";
246
246
  import { createWorktreeSafetyModule } from "./worktree-safety.js";
247
247
  import { resolveManifest } from "./unit-context-manifest.js";
248
248
  import { classifyFailure } from "./recovery-classification.js";
249
+ import { supportsStructuredQuestions } from "./workflow-mcp.js";
250
+ import type { MinimalModelRegistry } from "./context-budget.js";
249
251
  // Slice-level parallelism (#2340)
250
252
  import { getEligibleSlices } from "./slice-parallel-eligibility.js";
251
253
  import { startSliceParallel } from "./slice-parallel-orchestrator.js";
@@ -1792,6 +1794,75 @@ function buildLifecycle(): WorktreeLifecycle {
1792
1794
  return new WorktreeLifecycle(s, buildWorktreeLifecycleDeps());
1793
1795
  }
1794
1796
 
1797
+ /**
1798
+ * Build the production `DispatchAdapter` used by `createWiredAutoOrchestrationModule`.
1799
+ *
1800
+ * Exported so tests can verify parity with `runDispatch`'s `resolveDispatch` call —
1801
+ * the wired adapter must derive `structuredQuestionsAvailable`, `sessionContextWindow`,
1802
+ * `sessionProvider`, and `modelRegistry` the same way phases.ts:runDispatch does.
1803
+ */
1804
+ export function createWiredDispatchAdapter(
1805
+ ctx: ExtensionContext,
1806
+ pi: ExtensionAPI,
1807
+ dispatchBasePath: string,
1808
+ ): DispatchAdapter {
1809
+ return {
1810
+ async decideNextUnit(input) {
1811
+ const state = input.stateSnapshot;
1812
+ const active = state.activeMilestone;
1813
+ if (!active) return null;
1814
+
1815
+ const prefs = loadEffectiveGSDPreferences(dispatchBasePath)?.preferences;
1816
+
1817
+ // Derive session-derived dispatch inputs the same way phases.ts:runDispatch does
1818
+ // (#5789). Prefer caller-supplied values when present so test harnesses and
1819
+ // alternative wirings can inject deterministic snapshots; otherwise pull from
1820
+ // the captured pi/ctx references.
1821
+ const sessionProvider = input.sessionProvider ?? ctx.model?.provider;
1822
+ const sessionContextWindow = input.sessionContextWindow ?? ctx.model?.contextWindow;
1823
+ const modelRegistry = input.modelRegistry ?? (ctx.modelRegistry as MinimalModelRegistry | undefined);
1824
+ const authMode =
1825
+ sessionProvider && typeof ctx.modelRegistry?.getProviderAuthMode === "function"
1826
+ ? ctx.modelRegistry.getProviderAuthMode(sessionProvider)
1827
+ : undefined;
1828
+ const activeTools = typeof pi.getActiveTools === "function" ? pi.getActiveTools() : [];
1829
+ // Mirrors runDispatch: deep-planning keeps approval gates in plain chat
1830
+ // because structured questions can be cancelled outside the chat turn on
1831
+ // some transports.
1832
+ const structuredQuestionsAvailable =
1833
+ input.structuredQuestionsAvailable ??
1834
+ (prefs?.planning_depth === "deep"
1835
+ ? "false"
1836
+ : supportsStructuredQuestions(activeTools, {
1837
+ authMode,
1838
+ baseUrl: ctx.model?.baseUrl,
1839
+ })
1840
+ ? "true"
1841
+ : "false");
1842
+
1843
+ const action = await resolveDispatch({
1844
+ basePath: dispatchBasePath,
1845
+ mid: active.id,
1846
+ midTitle: active.title,
1847
+ state,
1848
+ prefs,
1849
+ structuredQuestionsAvailable,
1850
+ sessionContextWindow,
1851
+ sessionProvider,
1852
+ modelRegistry,
1853
+ });
1854
+
1855
+ if (action.action !== "dispatch") return null;
1856
+ return {
1857
+ unitType: action.unitType,
1858
+ unitId: action.unitId,
1859
+ reason: action.matchedRule ?? "dispatch",
1860
+ preconditions: [],
1861
+ };
1862
+ },
1863
+ };
1864
+ }
1865
+
1795
1866
  /**
1796
1867
  * Thin entry glue for the new Auto Orchestration module.
1797
1868
  *
@@ -1801,7 +1872,7 @@ function buildLifecycle(): WorktreeLifecycle {
1801
1872
  */
1802
1873
  export function createWiredAutoOrchestrationModule(
1803
1874
  ctx: ExtensionContext,
1804
- _pi: ExtensionAPI,
1875
+ pi: ExtensionAPI,
1805
1876
  dispatchBasePath: string,
1806
1877
  runtimeBasePath = resolveProjectRoot(dispatchBasePath),
1807
1878
  ): AutoOrchestrationModule {
@@ -1830,30 +1901,7 @@ export function createWiredAutoOrchestrationModule(
1830
1901
  };
1831
1902
  },
1832
1903
  },
1833
- dispatch: {
1834
- async decideNextUnit(input) {
1835
- const state = input.stateSnapshot;
1836
- const active = state.activeMilestone;
1837
- if (!active) return null;
1838
-
1839
- const prefs = loadEffectiveGSDPreferences(dispatchBasePath)?.preferences;
1840
- const action = await resolveDispatch({
1841
- basePath: dispatchBasePath,
1842
- mid: active.id,
1843
- midTitle: active.title,
1844
- state,
1845
- prefs,
1846
- });
1847
-
1848
- if (action.action !== "dispatch") return null;
1849
- return {
1850
- unitType: action.unitType,
1851
- unitId: action.unitId,
1852
- reason: action.matchedRule ?? "dispatch",
1853
- preconditions: [],
1854
- };
1855
- },
1856
- },
1904
+ dispatch: createWiredDispatchAdapter(ctx, pi, dispatchBasePath),
1857
1905
  recovery: {
1858
1906
  async classifyAndRecover(input) {
1859
1907
  const recovery = classifyFailure(input);
@@ -1902,12 +1950,25 @@ export function createWiredAutoOrchestrationModule(
1902
1950
  async cleanupOnStop() {},
1903
1951
  },
1904
1952
  health: {
1953
+ checkResourcesStale() {
1954
+ return checkResourcesStale(s.resourceVersionOnStart);
1955
+ },
1905
1956
  async preAdvanceGate() {
1906
- const gate = await preDispatchHealthGate(dispatchBasePath);
1907
- return {
1908
- allow: gate.proceed,
1909
- reason: gate.reason,
1910
- };
1957
+ try {
1958
+ const gate = await preDispatchHealthGate(dispatchBasePath);
1959
+ if (gate.proceed) {
1960
+ return {
1961
+ kind: "pass",
1962
+ fixesApplied: gate.fixesApplied,
1963
+ };
1964
+ }
1965
+ return {
1966
+ kind: "fail",
1967
+ reason: gate.reason ?? "Pre-dispatch health check failed — run /gsd doctor for details.",
1968
+ };
1969
+ } catch (error) {
1970
+ return { kind: "threw", error };
1971
+ }
1911
1972
  },
1912
1973
  async postAdvanceRecord(result) {
1913
1974
  if (result.kind === "error") {
@@ -1975,6 +2036,43 @@ export function createWiredAutoOrchestrationModule(
1975
2036
  }
1976
2037
  },
1977
2038
  },
2039
+ uokGate: {
2040
+ async emit(input) {
2041
+ const prefs = loadEffectiveGSDPreferences(dispatchBasePath)?.preferences;
2042
+ const uokFlags = resolveUokFlags(prefs);
2043
+ if (!uokFlags.gates) return;
2044
+ const milestoneId = input.milestoneId ?? s.currentMilestoneId ?? undefined;
2045
+ try {
2046
+ const { UokGateRunner } = await import("./uok/gate-runner.js");
2047
+ const runner = new UokGateRunner();
2048
+ runner.register({
2049
+ id: input.gateId,
2050
+ type: input.gateType,
2051
+ execute: async () => ({
2052
+ outcome: input.outcome,
2053
+ failureClass: input.failureClass,
2054
+ rationale: input.rationale,
2055
+ findings: input.findings ?? "",
2056
+ }),
2057
+ });
2058
+ await runner.run(input.gateId, {
2059
+ basePath: dispatchBasePath,
2060
+ traceId: `pre-dispatch:${flowId}`,
2061
+ turnId: `orch-${seq}`,
2062
+ milestoneId,
2063
+ unitType: "pre-dispatch",
2064
+ unitId: `orch-${seq}`,
2065
+ });
2066
+ } catch (err) {
2067
+ logWarning("engine", `uok gate emit failed: ${getErrorMessage(err)}`, {
2068
+ file: "auto.ts",
2069
+ gateId: input.gateId,
2070
+ gateType: input.gateType,
2071
+ ...(milestoneId ? { milestoneId } : {}),
2072
+ });
2073
+ }
2074
+ },
2075
+ },
1978
2076
  };
1979
2077
 
1980
2078
  return createAutoOrchestrator(deps);
@@ -306,7 +306,7 @@ function importRequirements(gsdDir: string): number {
306
306
  // ─── Hierarchy Artifact Walker ─────────────────────────────────────────────
307
307
 
308
308
  /** Artifact suffixes to look for at each hierarchy level */
309
- const MILESTONE_SUFFIXES = ['ROADMAP', 'CONTEXT', 'RESEARCH', 'ASSESSMENT'];
309
+ const MILESTONE_SUFFIXES = ['ROADMAP', 'CONTEXT', 'RESEARCH', 'ASSESSMENT', 'SUMMARY', 'VALIDATION'];
310
310
  const SLICE_SUFFIXES = ['PLAN', 'SUMMARY', 'RESEARCH', 'CONTEXT', 'ASSESSMENT', 'UAT'];
311
311
  const TASK_SUFFIXES = ['PLAN', 'SUMMARY', 'CONTINUE', 'CONTEXT', 'RESEARCH'];
312
312
 
@@ -34,6 +34,9 @@ export type MigrationImportCounts = ReturnType<typeof migrateFromMarkdown>;
34
34
 
35
35
  function assertMigrationImportMatchesPreview(imported: MigrationImportCounts, preview: MigrationPreview): void {
36
36
  const mismatches: string[] = [];
37
+ if (imported.decisions !== preview.decisions.total) {
38
+ mismatches.push(`decisions ${imported.decisions}/${preview.decisions.total}`);
39
+ }
37
40
  if (imported.hierarchy.milestones !== preview.milestoneCount) {
38
41
  mismatches.push(`milestones ${imported.hierarchy.milestones}/${preview.milestoneCount}`);
39
42
  }
@@ -73,6 +76,7 @@ export async function importWrittenMigrationToDb(
73
76
  /** Format preview stats for embedding in the review prompt. */
74
77
  function formatPreviewStats(preview: MigrationPreview): string {
75
78
  const lines = [
79
+ `- Decisions: ${preview.decisions.total}`,
76
80
  `- Milestones: ${preview.milestoneCount}`,
77
81
  `- Slices: ${preview.totalSlices} (${preview.doneSlices} done — ${preview.sliceCompletionPct}%)`,
78
82
  `- Tasks: ${preview.totalTasks} (${preview.doneTasks} done — ${preview.taskCompletionPct}%)`,
@@ -179,6 +183,7 @@ export async function handleMigrate(
179
183
 
180
184
  // ── Build preview text ─────────────────────────────────────────────────────
181
185
  const lines: string[] = [
186
+ `Decisions: ${preview.decisions.total}`,
182
187
  `Milestones: ${preview.milestoneCount}`,
183
188
  `Slices: ${preview.totalSlices} (${preview.doneSlices} done — ${preview.sliceCompletionPct}%)`,
184
189
  `Tasks: ${preview.totalTasks} (${preview.doneTasks} done — ${preview.taskCompletionPct}%)`,
@@ -4,6 +4,13 @@
4
4
  import type { GSDProject } from './types.js';
5
5
  import type { MigrationPreview } from './writer.js';
6
6
 
7
+ function countCanonicalDecisionRows(content: string): number {
8
+ return content
9
+ .split('\n')
10
+ .filter((line) => /^\|\s*D\d+\s*\|/.test(line.trim()))
11
+ .length;
12
+ }
13
+
7
14
  /**
8
15
  * Compute pre-write statistics from a GSDProject without performing I/O.
9
16
  * Used to show the user what a migration will produce before writing anything.
@@ -36,6 +43,9 @@ export function generatePreview(project: GSDProject): MigrationPreview {
36
43
  }
37
44
 
38
45
  return {
46
+ decisions: {
47
+ total: countCanonicalDecisionRows(project.decisionsContent),
48
+ },
39
49
  milestoneCount: project.milestones.length,
40
50
  totalSlices,
41
51
  totalTasks,
@@ -238,16 +238,53 @@ function normalizeStatus(status: string): 'active' | 'validated' | 'deferred' {
238
238
  return 'active';
239
239
  }
240
240
 
241
+ function normalizeRequirementId(id: string): string | null {
242
+ const match = id.trim().match(/^R(\d+)$/i);
243
+ if (!match) return null;
244
+ return `R${match[1].padStart(3, '0')}`;
245
+ }
246
+
241
247
  function mapRequirements(reqs: PlanningRequirement[]): GSDRequirement[] {
242
248
  let autoId = 0;
249
+ const reservedIds = new Set(
250
+ reqs
251
+ .map((req) => normalizeRequirementId(req.id))
252
+ .filter((id): id is string => id !== null),
253
+ );
254
+ const usedIds = new Set<string>();
255
+
256
+ function nextRequirementId(): string {
257
+ let id = '';
258
+ do {
259
+ autoId++;
260
+ id = padId('R', autoId, 3);
261
+ } while (usedIds.has(id) || reservedIds.has(id));
262
+ usedIds.add(id);
263
+ return id;
264
+ }
265
+
243
266
  return reqs.map((req) => {
244
- autoId++;
267
+ const originalId = req.id.trim();
268
+ const canonicalId = normalizeRequirementId(originalId);
269
+ let id: string;
270
+ let description = req.description;
271
+
272
+ if (canonicalId && !usedIds.has(canonicalId)) {
273
+ id = canonicalId;
274
+ usedIds.add(id);
275
+ } else {
276
+ id = nextRequirementId();
277
+ if (originalId) {
278
+ description = `Legacy ID: ${originalId}\n\n${description}`;
279
+ }
280
+ }
281
+
245
282
  return {
246
- id: req.id && req.id.trim() !== '' ? req.id : padId('R', autoId, 3),
283
+ id,
247
284
  title: req.title,
248
285
  class: 'core-capability',
249
286
  status: normalizeStatus(req.status),
250
- description: req.description,
287
+ description,
251
288
  source: 'inferred',
252
289
  primarySlice: 'none yet',
253
290
  };
@@ -286,7 +323,24 @@ function deriveDecisions(parsed: PlanningProject): string {
286
323
  }
287
324
  }
288
325
  if (decisions.length === 0) return '';
289
- return decisions.map((d) => `- ${d}`).join('\n');
326
+ const lines = [
327
+ '# Decisions Register',
328
+ '',
329
+ '<!-- Append-only. Never edit or remove existing rows.',
330
+ ' To reverse a decision, add a new row that supersedes it.',
331
+ ' Read this file at the start of any planning or research phase. -->',
332
+ '',
333
+ '| # | When | Scope | Decision | Choice | Rationale | Revisable? | Made By |',
334
+ '|---|------|-------|----------|--------|-----------|------------|---------|',
335
+ ];
336
+
337
+ decisions.forEach((decision, index) => {
338
+ const id = padId('D', index + 1, 3);
339
+ const escaped = decision.replace(/\|/g, '\\|');
340
+ lines.push(`| ${id} | migration | migrated-summary | ${escaped} | ${escaped} | Migrated from legacy summary key-decisions | Yes | agent |`);
341
+ });
342
+
343
+ return lines.join('\n') + '\n';
290
344
  }
291
345
 
292
346
  // ─── Main Entry Point ──────────────────────────────────────────────────────
@@ -37,6 +37,9 @@ export interface WrittenFiles {
37
37
 
38
38
  /** Pre-write statistics computed from a GSDProject without I/O. */
39
39
  export interface MigrationPreview {
40
+ decisions: {
41
+ total: number;
42
+ };
40
43
  milestoneCount: number;
41
44
  totalSlices: number;
42
45
  totalTasks: number;
@@ -374,7 +377,17 @@ export function formatProject(content: string): string {
374
377
  */
375
378
  export function formatDecisions(content: string): string {
376
379
  if (!content || !content.trim()) {
377
- return '# Decisions\n\n<!-- Append-only register of architectural and pattern decisions -->\n\n| ID | Decision | Rationale | Date |\n|----|----------|-----------|------|\n';
380
+ return [
381
+ '# Decisions Register',
382
+ '',
383
+ '<!-- Append-only. Never edit or remove existing rows.',
384
+ ' To reverse a decision, add a new row that supersedes it.',
385
+ ' Read this file at the start of any planning or research phase. -->',
386
+ '',
387
+ '| # | When | Scope | Decision | Choice | Rationale | Revisable? | Made By |',
388
+ '|---|------|-------|----------|--------|-----------|------------|---------|',
389
+ '',
390
+ ].join('\n');
378
391
  }
379
392
  return content.endsWith('\n') ? content : content + '\n';
380
393
  }