gsd-pi 2.60.0-dev.2580e65 → 2.60.0-dev.d9052f5

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 (196) hide show
  1. package/dist/resources/extensions/ask-user-questions.js +4 -7
  2. package/dist/resources/extensions/gsd/auto/phases.js +7 -15
  3. package/dist/resources/extensions/gsd/auto-dashboard.js +8 -21
  4. package/dist/resources/extensions/gsd/auto-dispatch.js +3 -6
  5. package/dist/resources/extensions/gsd/auto-model-selection.js +9 -58
  6. package/dist/resources/extensions/gsd/auto-post-unit.js +2 -3
  7. package/dist/resources/extensions/gsd/auto-prompts.js +20 -36
  8. package/dist/resources/extensions/gsd/auto-recovery.js +18 -37
  9. package/dist/resources/extensions/gsd/auto-start.js +5 -9
  10. package/dist/resources/extensions/gsd/auto-timers.js +5 -11
  11. package/dist/resources/extensions/gsd/auto-unit-closeout.js +3 -5
  12. package/dist/resources/extensions/gsd/auto-verification.js +2 -3
  13. package/dist/resources/extensions/gsd/auto-worktree.js +55 -120
  14. package/dist/resources/extensions/gsd/auto.js +17 -39
  15. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +3 -6
  16. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +2 -2
  17. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +10 -4
  18. package/dist/resources/extensions/gsd/bootstrap/journal-tools.js +1 -2
  19. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +0 -7
  20. package/dist/resources/extensions/gsd/bootstrap/system-context.js +10 -11
  21. package/dist/resources/extensions/gsd/commands/catalog.js +0 -2
  22. package/dist/resources/extensions/gsd/commands-codebase.js +21 -48
  23. package/dist/resources/extensions/gsd/commands-inspect.js +1 -2
  24. package/dist/resources/extensions/gsd/commands-maintenance.js +19 -32
  25. package/dist/resources/extensions/gsd/complexity-classifier.js +4 -8
  26. package/dist/resources/extensions/gsd/custom-verification.js +2 -3
  27. package/dist/resources/extensions/gsd/gsd-db.js +13 -33
  28. package/dist/resources/extensions/gsd/guided-flow.js +9 -19
  29. package/dist/resources/extensions/gsd/init-wizard.js +0 -12
  30. package/dist/resources/extensions/gsd/markdown-renderer.js +9 -11
  31. package/dist/resources/extensions/gsd/md-importer.js +4 -5
  32. package/dist/resources/extensions/gsd/milestone-actions.js +2 -3
  33. package/dist/resources/extensions/gsd/milestone-ids.js +1 -2
  34. package/dist/resources/extensions/gsd/model-router.js +121 -156
  35. package/dist/resources/extensions/gsd/parallel-merge.js +3 -5
  36. package/dist/resources/extensions/gsd/parallel-orchestrator.js +14 -26
  37. package/dist/resources/extensions/gsd/preferences-types.js +0 -1
  38. package/dist/resources/extensions/gsd/preferences-validation.js +0 -45
  39. package/dist/resources/extensions/gsd/preferences.js +3 -15
  40. package/dist/resources/extensions/gsd/prompt-loader.js +2 -3
  41. package/dist/resources/extensions/gsd/prompts/rethink.md +1 -1
  42. package/dist/resources/extensions/gsd/rule-registry.js +6 -7
  43. package/dist/resources/extensions/gsd/safe-fs.js +8 -6
  44. package/dist/resources/extensions/gsd/tools/complete-milestone.js +2 -3
  45. package/dist/resources/extensions/gsd/tools/complete-slice.js +2 -3
  46. package/dist/resources/extensions/gsd/tools/complete-task.js +2 -3
  47. package/dist/resources/extensions/gsd/tools/plan-milestone.js +2 -3
  48. package/dist/resources/extensions/gsd/tools/plan-slice.js +2 -3
  49. package/dist/resources/extensions/gsd/tools/plan-task.js +1 -2
  50. package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +4 -4
  51. package/dist/resources/extensions/gsd/tools/reopen-slice.js +1 -2
  52. package/dist/resources/extensions/gsd/tools/reopen-task.js +1 -2
  53. package/dist/resources/extensions/gsd/tools/replan-slice.js +1 -2
  54. package/dist/resources/extensions/gsd/tools/validate-milestone.js +1 -2
  55. package/dist/resources/extensions/gsd/triage-resolution.js +4 -11
  56. package/dist/resources/extensions/gsd/workflow-events.js +1 -2
  57. package/dist/resources/extensions/gsd/workflow-logger.js +4 -37
  58. package/dist/resources/extensions/gsd/workflow-migration.js +12 -14
  59. package/dist/resources/extensions/gsd/workflow-projections.js +2 -2
  60. package/dist/resources/extensions/gsd/workflow-reconcile.js +2 -2
  61. package/dist/resources/extensions/gsd/worktree-manager.js +14 -26
  62. package/dist/resources/extensions/shared/interview-ui.js +1 -3
  63. package/dist/web/standalone/.next/BUILD_ID +1 -1
  64. package/dist/web/standalone/.next/app-path-routes-manifest.json +19 -19
  65. package/dist/web/standalone/.next/build-manifest.json +2 -2
  66. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  67. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  68. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  69. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  70. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  71. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  76. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  77. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  78. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  79. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/index.html +1 -1
  84. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  85. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  86. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  87. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  88. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  89. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  90. package/dist/web/standalone/.next/server/app-paths-manifest.json +19 -19
  91. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  92. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  93. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  94. package/package.json +1 -1
  95. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  96. package/packages/pi-coding-agent/dist/core/extensions/loader.js +0 -5
  97. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  98. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +1 -2
  99. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
  100. package/packages/pi-coding-agent/dist/core/extensions/runner.js +0 -16
  101. package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
  102. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +0 -26
  103. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  104. package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  105. package/packages/pi-coding-agent/dist/core/lsp/config.d.ts.map +1 -1
  106. package/packages/pi-coding-agent/dist/core/lsp/config.js +1 -6
  107. package/packages/pi-coding-agent/dist/core/lsp/config.js.map +1 -1
  108. package/packages/pi-coding-agent/dist/core/lsp/defaults.json +2 -2
  109. package/packages/pi-coding-agent/src/core/extensions/loader.ts +0 -6
  110. package/packages/pi-coding-agent/src/core/extensions/runner.ts +0 -19
  111. package/packages/pi-coding-agent/src/core/extensions/types.ts +0 -26
  112. package/packages/pi-coding-agent/src/core/lsp/config.ts +1 -7
  113. package/packages/pi-coding-agent/src/core/lsp/defaults.json +2 -2
  114. package/src/resources/extensions/ask-user-questions.ts +3 -7
  115. package/src/resources/extensions/gsd/auto/phases.ts +7 -17
  116. package/src/resources/extensions/gsd/auto-dashboard.ts +8 -22
  117. package/src/resources/extensions/gsd/auto-dispatch.ts +3 -7
  118. package/src/resources/extensions/gsd/auto-model-selection.ts +15 -77
  119. package/src/resources/extensions/gsd/auto-post-unit.ts +4 -4
  120. package/src/resources/extensions/gsd/auto-prompts.ts +20 -37
  121. package/src/resources/extensions/gsd/auto-recovery.ts +18 -38
  122. package/src/resources/extensions/gsd/auto-start.ts +9 -10
  123. package/src/resources/extensions/gsd/auto-timers.ts +5 -12
  124. package/src/resources/extensions/gsd/auto-unit-closeout.ts +2 -6
  125. package/src/resources/extensions/gsd/auto-verification.ts +6 -3
  126. package/src/resources/extensions/gsd/auto-worktree.ts +55 -121
  127. package/src/resources/extensions/gsd/auto.ts +17 -40
  128. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +3 -4
  129. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +2 -2
  130. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +16 -4
  131. package/src/resources/extensions/gsd/bootstrap/journal-tools.ts +1 -2
  132. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +0 -8
  133. package/src/resources/extensions/gsd/bootstrap/system-context.ts +10 -11
  134. package/src/resources/extensions/gsd/commands/catalog.ts +0 -2
  135. package/src/resources/extensions/gsd/commands-codebase.ts +20 -52
  136. package/src/resources/extensions/gsd/commands-inspect.ts +1 -2
  137. package/src/resources/extensions/gsd/commands-maintenance.ts +19 -28
  138. package/src/resources/extensions/gsd/complexity-classifier.ts +4 -9
  139. package/src/resources/extensions/gsd/custom-verification.ts +2 -3
  140. package/src/resources/extensions/gsd/gsd-db.ts +14 -12
  141. package/src/resources/extensions/gsd/guided-flow.ts +8 -9
  142. package/src/resources/extensions/gsd/init-wizard.ts +0 -12
  143. package/src/resources/extensions/gsd/markdown-renderer.ts +17 -11
  144. package/src/resources/extensions/gsd/md-importer.ts +4 -5
  145. package/src/resources/extensions/gsd/milestone-actions.ts +2 -3
  146. package/src/resources/extensions/gsd/milestone-ids.ts +1 -2
  147. package/src/resources/extensions/gsd/model-router.ts +173 -199
  148. package/src/resources/extensions/gsd/parallel-merge.ts +3 -5
  149. package/src/resources/extensions/gsd/parallel-orchestrator.ts +14 -18
  150. package/src/resources/extensions/gsd/preferences-types.ts +0 -13
  151. package/src/resources/extensions/gsd/preferences-validation.ts +0 -45
  152. package/src/resources/extensions/gsd/preferences.ts +3 -16
  153. package/src/resources/extensions/gsd/prompt-loader.ts +2 -3
  154. package/src/resources/extensions/gsd/prompts/rethink.md +1 -1
  155. package/src/resources/extensions/gsd/rule-registry.ts +6 -7
  156. package/src/resources/extensions/gsd/safe-fs.ts +5 -6
  157. package/src/resources/extensions/gsd/tests/codebase-generator.test.ts +0 -63
  158. package/src/resources/extensions/gsd/tests/complexity-classifier.test.ts +2 -27
  159. package/src/resources/extensions/gsd/tests/db-path-worktree-symlink.test.ts +4 -4
  160. package/src/resources/extensions/gsd/tests/model-router.test.ts +3 -403
  161. package/src/resources/extensions/gsd/tests/preferences.test.ts +0 -62
  162. package/src/resources/extensions/gsd/tests/remote-questions.test.ts +0 -21
  163. package/src/resources/extensions/gsd/tests/workflow-logger.test.ts +6 -6
  164. package/src/resources/extensions/gsd/tools/complete-milestone.ts +6 -3
  165. package/src/resources/extensions/gsd/tools/complete-slice.ts +6 -3
  166. package/src/resources/extensions/gsd/tools/complete-task.ts +6 -3
  167. package/src/resources/extensions/gsd/tools/plan-milestone.ts +6 -3
  168. package/src/resources/extensions/gsd/tools/plan-slice.ts +6 -3
  169. package/src/resources/extensions/gsd/tools/plan-task.ts +3 -2
  170. package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +6 -4
  171. package/src/resources/extensions/gsd/tools/reopen-slice.ts +3 -2
  172. package/src/resources/extensions/gsd/tools/reopen-task.ts +3 -2
  173. package/src/resources/extensions/gsd/tools/replan-slice.ts +3 -2
  174. package/src/resources/extensions/gsd/tools/validate-milestone.ts +3 -2
  175. package/src/resources/extensions/gsd/triage-resolution.ts +4 -11
  176. package/src/resources/extensions/gsd/types.ts +0 -1
  177. package/src/resources/extensions/gsd/workflow-events.ts +1 -2
  178. package/src/resources/extensions/gsd/workflow-logger.ts +5 -52
  179. package/src/resources/extensions/gsd/workflow-migration.ts +12 -14
  180. package/src/resources/extensions/gsd/workflow-projections.ts +2 -2
  181. package/src/resources/extensions/gsd/workflow-reconcile.ts +2 -2
  182. package/src/resources/extensions/gsd/worktree-manager.ts +14 -16
  183. package/src/resources/extensions/shared/interview-ui.ts +1 -3
  184. package/packages/pi-coding-agent/dist/core/lsp/lsp-legacy-alias.test.d.ts +0 -2
  185. package/packages/pi-coding-agent/dist/core/lsp/lsp-legacy-alias.test.d.ts.map +0 -1
  186. package/packages/pi-coding-agent/dist/core/lsp/lsp-legacy-alias.test.js +0 -47
  187. package/packages/pi-coding-agent/dist/core/lsp/lsp-legacy-alias.test.js.map +0 -1
  188. package/packages/pi-coding-agent/src/core/lsp/lsp-legacy-alias.test.ts +0 -70
  189. package/src/resources/extensions/gsd/tests/capability-router.test.ts +0 -347
  190. package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +0 -1188
  191. package/src/resources/extensions/gsd/tests/integration/state-machine-runtime-failures.test.ts +0 -841
  192. package/src/resources/extensions/gsd/tests/silent-catch-diagnostics.test.ts +0 -284
  193. package/src/resources/extensions/gsd/tests/workflow-logger-audit.test.ts +0 -120
  194. package/src/resources/extensions/shared/tests/interview-notes-loop.test.ts +0 -144
  195. /package/dist/web/standalone/.next/static/{ogyMN7M-3bGGuRY08L5HR → JVkoVYumy0cDhOQISEYdG}/_buildManifest.js +0 -0
  196. /package/dist/web/standalone/.next/static/{ogyMN7M-3bGGuRY08L5HR → JVkoVYumy0cDhOQISEYdG}/_ssgManifest.js +0 -0
@@ -2,7 +2,7 @@
2
2
  // Maps complexity tiers to models, enforcing downgrade-only semantics.
3
3
  // The user's configured model is always the ceiling.
4
4
 
5
- import type { ComplexityTier, ClassificationResult, TaskMetadata } from "./complexity-classifier.js";
5
+ import type { ComplexityTier, ClassificationResult } from "./complexity-classifier.js";
6
6
  import { tierOrdinal } from "./complexity-classifier.js";
7
7
  import type { ResolvedModelConfig } from "./preferences.js";
8
8
 
@@ -33,27 +33,14 @@ export interface RoutingDecision {
33
33
  wasDowngraded: boolean;
34
34
  /** Human-readable reason for this decision */
35
35
  reason: string;
36
- /** How the model was selected */
37
- selectionMethod: "tier-only" | "capability-scored";
38
- /** Capability scores per eligible model (capability-scored path only) */
36
+ /** How the model was selected. */
37
+ selectionMethod?: "tier-only" | "capability-scored";
38
+ /** Capability scores per model (when capability-scored). */
39
39
  capabilityScores?: Record<string, number>;
40
- /** Task requirement vector used for scoring */
40
+ /** Task requirement vector (when capability-scored). */
41
41
  taskRequirements?: Partial<Record<string, number>>;
42
42
  }
43
43
 
44
- // ─── Capability Profiles ─────────────────────────────────────────────────────
45
-
46
- /** Seven-dimension capability profile for a model. All values in 0–100 range. */
47
- export interface ModelCapabilities {
48
- coding: number;
49
- debugging: number;
50
- research: number;
51
- reasoning: number;
52
- speed: number;
53
- longContext: number;
54
- instruction: number;
55
- }
56
-
57
44
  // ─── Known Model Tiers ───────────────────────────────────────────────────────
58
45
  // Maps known model IDs to their capability tier. Used when tier_models is not
59
46
  // explicitly configured to pick the best available model for each tier.
@@ -134,27 +121,33 @@ const MODEL_COST_PER_1K_INPUT: Record<string, number> = {
134
121
  "deepseek-chat": 0.00014,
135
122
  };
136
123
 
137
- // ─── Capability Profiles Data Table ──────────────────────────────────────────
138
- // Per-model capability profiles (0–100 scale). Used for capability-aware
139
- // model selection within an eligible tier set.
124
+ // ─── Capability Profiles (ADR-004 Phase 2) ──────────────────────────────────
125
+ // 7-dimension profiles, 0–100 normalized. Models without a profile
126
+ // score 50 uniformly capability scoring is a no-op for them.
127
+
128
+ export interface ModelCapabilities {
129
+ coding: number;
130
+ debugging: number;
131
+ research: number;
132
+ reasoning: number;
133
+ speed: number;
134
+ longContext: number;
135
+ instruction: number;
136
+ }
140
137
 
141
138
  export const MODEL_CAPABILITY_PROFILES: Record<string, ModelCapabilities> = {
142
- "claude-opus-4-6": { coding: 95, debugging: 90, research: 85, reasoning: 95, speed: 30, longContext: 80, instruction: 90 },
143
- "claude-sonnet-4-6": { coding: 85, debugging: 80, research: 75, reasoning: 80, speed: 60, longContext: 75, instruction: 85 },
144
- "claude-haiku-4-5": { coding: 60, debugging: 50, research: 45, reasoning: 50, speed: 95, longContext: 50, instruction: 75 },
145
- "gpt-4o": { coding: 80, debugging: 75, research: 70, reasoning: 75, speed: 65, longContext: 70, instruction: 80 },
146
- "gpt-4o-mini": { coding: 55, debugging: 45, research: 40, reasoning: 45, speed: 90, longContext: 45, instruction: 70 },
147
- "gemini-2.5-pro": { coding: 75, debugging: 70, research: 85, reasoning: 75, speed: 55, longContext: 90, instruction: 75 },
148
- "gemini-2.0-flash": { coding: 50, debugging: 40, research: 50, reasoning: 40, speed: 95, longContext: 60, instruction: 65 },
149
- "deepseek-chat": { coding: 75, debugging: 65, research: 55, reasoning: 70, speed: 70, longContext: 55, instruction: 65 },
150
- "o3": { coding: 80, debugging: 85, research: 80, reasoning: 92, speed: 25, longContext: 70, instruction: 85 },
139
+ "claude-opus-4-6": { coding: 95, debugging: 90, research: 85, reasoning: 95, speed: 30, longContext: 80, instruction: 90 },
140
+ "claude-sonnet-4-6": { coding: 85, debugging: 80, research: 75, reasoning: 80, speed: 60, longContext: 75, instruction: 85 },
141
+ "claude-haiku-4-5": { coding: 60, debugging: 50, research: 45, reasoning: 50, speed: 95, longContext: 50, instruction: 75 },
142
+ "gpt-4o": { coding: 80, debugging: 75, research: 70, reasoning: 75, speed: 65, longContext: 70, instruction: 80 },
143
+ "gpt-4o-mini": { coding: 55, debugging: 45, research: 40, reasoning: 45, speed: 90, longContext: 45, instruction: 70 },
144
+ "gemini-2.5-pro": { coding: 75, debugging: 70, research: 85, reasoning: 75, speed: 55, longContext: 90, instruction: 75 },
145
+ "gemini-2.0-flash": { coding: 50, debugging: 40, research: 50, reasoning: 40, speed: 95, longContext: 60, instruction: 65 },
146
+ "deepseek-chat": { coding: 75, debugging: 65, research: 55, reasoning: 70, speed: 70, longContext: 55, instruction: 65 },
147
+ "o3": { coding: 80, debugging: 85, research: 80, reasoning: 92, speed: 25, longContext: 70, instruction: 85 },
151
148
  };
152
149
 
153
- // ─── Base Task Requirements Data Table ───────────────────────────────────────
154
- // Per-unit-type base requirement vectors. Weights indicate how important each
155
- // capability dimension is for this unit type.
156
-
157
- export const BASE_REQUIREMENTS: Record<string, Partial<Record<keyof ModelCapabilities, number>>> = {
150
+ const BASE_REQUIREMENTS: Record<string, Partial<Record<keyof ModelCapabilities, number>>> = {
158
151
  "execute-task": { coding: 0.9, instruction: 0.7, speed: 0.3 },
159
152
  "research-milestone": { research: 0.9, longContext: 0.7, reasoning: 0.5 },
160
153
  "research-slice": { research: 0.9, longContext: 0.7, reasoning: 0.5 },
@@ -168,36 +161,15 @@ export const BASE_REQUIREMENTS: Record<string, Partial<Record<keyof ModelCapabil
168
161
  "complete-milestone": { instruction: 0.8, reasoning: 0.5 },
169
162
  };
170
163
 
171
- // ─── Public API ──────────────────────────────────────────────────────────────
172
-
173
164
  /**
174
- * Score a model's suitability for a task given a requirement vector.
175
- * Returns a weighted average of capability dimensions (0–100).
176
- * Returns 50 if requirements are empty (neutral score).
177
- */
178
- export function scoreModel(
179
- model: ModelCapabilities,
180
- requirements: Partial<Record<keyof ModelCapabilities, number>>,
181
- ): number {
182
- let weightedSum = 0;
183
- let weightSum = 0;
184
- for (const [dim, weight] of Object.entries(requirements)) {
185
- const capability = model[dim as keyof ModelCapabilities] ?? 50;
186
- weightedSum += weight * capability;
187
- weightSum += weight;
188
- }
189
- return weightSum > 0 ? weightedSum / weightSum : 50;
190
- }
191
-
192
- /**
193
- * Compute dynamic task requirements from unit type and optional task metadata.
194
- * Returns a requirement vector refined by task-specific signals.
165
+ * Compute a task requirement vector from unit type and optional metadata.
195
166
  */
196
167
  export function computeTaskRequirements(
197
168
  unitType: string,
198
- metadata?: TaskMetadata,
169
+ metadata?: { tags?: string[]; complexityKeywords?: string[]; fileCount?: number; estimatedLines?: number },
199
170
  ): Partial<Record<keyof ModelCapabilities, number>> {
200
- const base = BASE_REQUIREMENTS[unitType] ?? { reasoning: 0.5 };
171
+ const base = { ...(BASE_REQUIREMENTS[unitType] ?? { reasoning: 0.5 }) };
172
+
201
173
  if (unitType === "execute-task" && metadata) {
202
174
  if (metadata.tags?.some(t => /^(docs?|readme|comment|config|typo|rename)$/i.test(t))) {
203
175
  return { ...base, instruction: 0.9, coding: 0.3, speed: 0.7 };
@@ -212,101 +184,29 @@ export function computeTaskRequirements(
212
184
  return { ...base, coding: 0.9, reasoning: 0.7 };
213
185
  }
214
186
  }
187
+
215
188
  return base;
216
189
  }
217
190
 
218
191
  /**
219
- * Score all eligible models against a requirement vector and return them
220
- * sorted by score descending. Within 2 points: prefer cheaper; equal cost:
221
- * lexicographic tie-break by model ID.
192
+ * Score a model against a task requirement vector.
193
+ * Returns weighted average in range 0–100. Returns 50 for empty requirements.
222
194
  */
223
- export function scoreEligibleModels(
224
- eligibleModelIds: string[],
195
+ export function scoreModel(
196
+ capabilities: ModelCapabilities,
225
197
  requirements: Partial<Record<keyof ModelCapabilities, number>>,
226
- capabilityOverrides?: Record<string, Partial<ModelCapabilities>>,
227
- ): Array<{ modelId: string; score: number }> {
228
- const scored = eligibleModelIds.map(modelId => {
229
- const builtin = MODEL_CAPABILITY_PROFILES[modelId];
230
- const override = capabilityOverrides?.[modelId];
231
- const profile: ModelCapabilities = builtin
232
- ? override ? { ...builtin, ...override } : builtin
233
- : { coding: 50, debugging: 50, research: 50, reasoning: 50, speed: 50, longContext: 50, instruction: 50 };
234
- return { modelId, score: scoreModel(profile, requirements) };
235
- });
236
- scored.sort((a, b) => {
237
- const scoreDiff = b.score - a.score;
238
- if (Math.abs(scoreDiff) > 2) return scoreDiff;
239
- const costA = MODEL_COST_PER_1K_INPUT[a.modelId] ?? Infinity;
240
- const costB = MODEL_COST_PER_1K_INPUT[b.modelId] ?? Infinity;
241
- if (costA !== costB) return costA - costB;
242
- return a.modelId.localeCompare(b.modelId);
243
- });
244
- return scored;
245
- }
246
-
247
- /**
248
- * Return all models eligible for a given tier, sorted cheapest first.
249
- * If routingConfig.tier_models[tier] is set and available, returns only that
250
- * model. Otherwise filters availableModelIds by tier from MODEL_CAPABILITY_TIER.
251
- */
252
- export function getEligibleModels(
253
- tier: ComplexityTier,
254
- availableModelIds: string[],
255
- routingConfig: DynamicRoutingConfig,
256
- ): string[] {
257
- // 1. Check explicit tier_models config
258
- const explicitModel = routingConfig.tier_models?.[tier];
259
- if (explicitModel) {
260
- // Exact match
261
- if (availableModelIds.includes(explicitModel)) return [explicitModel];
262
- // Provider-prefix-stripped match
263
- const match = availableModelIds.find(id => {
264
- const bareAvail = id.includes("/") ? id.split("/").pop()! : id;
265
- const bareExplicit = explicitModel.includes("/") ? explicitModel.split("/").pop()! : explicitModel;
266
- return bareAvail === bareExplicit;
267
- });
268
- if (match) return [match];
198
+ ): number {
199
+ let weightedSum = 0;
200
+ let weightSum = 0;
201
+ for (const [dim, weight] of Object.entries(requirements)) {
202
+ const capability = capabilities[dim as keyof ModelCapabilities] ?? 50;
203
+ weightedSum += weight * capability;
204
+ weightSum += weight;
269
205
  }
270
-
271
- // 2. Auto-detect: filter by tier, sort cheapest first
272
- return availableModelIds
273
- .filter(id => getModelTier(id) === tier)
274
- .sort((a, b) => {
275
- const costA = getModelCost(a);
276
- const costB = getModelCost(b);
277
- return costA - costB;
278
- });
279
- }
280
-
281
- /**
282
- * Build a fallback chain for a selected model: [selectedModel, ...configuredFallbacks, configuredPrimary]
283
- * Deduplicates entries while preserving order.
284
- */
285
- function buildFallbackChain(selectedModelId: string, phaseConfig: ResolvedModelConfig): string[] {
286
- return [
287
- ...phaseConfig.fallbacks.filter(f => f !== selectedModelId),
288
- phaseConfig.primary,
289
- ].filter(f => f !== selectedModelId);
206
+ return weightSum > 0 ? weightedSum / weightSum : 50;
290
207
  }
291
208
 
292
- /**
293
- * Load capability overrides from user preferences' modelOverrides section.
294
- * Returns a map of model ID → partial capability overrides to deep-merge with built-in profiles.
295
- *
296
- * Per D-17: partial capability overrides via models.json modelOverrides, deep-merged with defaults.
297
- */
298
- export function loadCapabilityOverrides(
299
- prefs: { modelOverrides?: Record<string, { capabilities?: Partial<ModelCapabilities> }> },
300
- ): Record<string, Partial<ModelCapabilities>> {
301
- const result: Record<string, Partial<ModelCapabilities>> = {};
302
- if (!prefs.modelOverrides) return result;
303
- for (const [modelId, overrideEntry] of Object.entries(prefs.modelOverrides)) {
304
- if (overrideEntry.capabilities) {
305
- result[modelId] = overrideEntry.capabilities;
306
- }
307
- }
308
- return result;
309
- }
209
+ // ─── Public API ──────────────────────────────────────────────────────────────
310
210
 
311
211
  /**
312
212
  * Resolve the model to use for a given complexity tier.
@@ -314,18 +214,10 @@ export function loadCapabilityOverrides(
314
214
  * Downgrade-only: the returned model is always equal to or cheaper than
315
215
  * the user's configured primary model. Never upgrades beyond configuration.
316
216
  *
317
- * STEP 1: Filter to eligible models for the requested tier.
318
- * STEP 2: Capability scoring ranks eligible models by task-capability match
319
- * when capability_routing is enabled and multiple eligible models exist.
320
- * STEP 3: Fallback chain assembly.
321
- *
322
- * @param classification The complexity classification result
323
- * @param phaseConfig The user's configured model for this phase (ceiling)
324
- * @param routingConfig Dynamic routing configuration
325
- * @param availableModelIds List of available model IDs (from registry)
326
- * @param unitType The unit type for capability requirement computation (optional)
327
- * @param taskMetadata Task metadata for refined requirement vectors (optional)
328
- * @param capabilityOverrides User-provided capability overrides (deep-merged with built-in profiles, optional)
217
+ * @param classification The complexity classification result
218
+ * @param phaseConfig The user's configured model for this phase (ceiling)
219
+ * @param routingConfig Dynamic routing configuration
220
+ * @param availableModelIds List of available model IDs (from registry)
329
221
  */
330
222
  export function resolveModelForComplexity(
331
223
  classification: ClassificationResult,
@@ -333,8 +225,7 @@ export function resolveModelForComplexity(
333
225
  routingConfig: DynamicRoutingConfig,
334
226
  availableModelIds: string[],
335
227
  unitType?: string,
336
- taskMetadata?: TaskMetadata,
337
- capabilityOverrides?: Record<string, Partial<ModelCapabilities>>,
228
+ metadata?: { tags?: string[]; complexityKeywords?: string[]; fileCount?: number; estimatedLines?: number },
338
229
  ): RoutingDecision {
339
230
  // If no phase config or routing disabled, pass through
340
231
  if (!phaseConfig || !routingConfig.enabled) {
@@ -344,7 +235,6 @@ export function resolveModelForComplexity(
344
235
  tier: classification.tier,
345
236
  wasDowngraded: false,
346
237
  reason: "dynamic routing disabled or no phase config",
347
- selectionMethod: "tier-only",
348
238
  };
349
239
  }
350
240
 
@@ -364,7 +254,6 @@ export function resolveModelForComplexity(
364
254
  tier: requestedTier,
365
255
  wasDowngraded: false,
366
256
  reason: `configured model "${configuredPrimary}" is not in the known tier map — honoring explicit config`,
367
- selectionMethod: "tier-only",
368
257
  };
369
258
  }
370
259
 
@@ -376,52 +265,48 @@ export function resolveModelForComplexity(
376
265
  tier: requestedTier,
377
266
  wasDowngraded: false,
378
267
  reason: `tier ${requestedTier} >= configured ${configuredTier}`,
379
- selectionMethod: "tier-only",
380
268
  };
381
269
  }
382
270
 
383
- // STEP 1: Get all eligible models for the requested tier
384
- const eligible = getEligibleModels(requestedTier, availableModelIds, routingConfig);
271
+ // Find the best model for the requested tier
272
+ const useCapabilityScoring = routingConfig.capability_routing && unitType;
273
+
274
+ let targetModelId: string | null;
275
+ let capabilityScores: Record<string, number> | undefined;
276
+ let taskRequirements: Partial<Record<string, number>> | undefined;
277
+ let selectionMethod: "tier-only" | "capability-scored" = "tier-only";
278
+
279
+ if (useCapabilityScoring) {
280
+ const result = findModelForTierWithCapability(
281
+ requestedTier, routingConfig, availableModelIds,
282
+ routingConfig.cross_provider !== false, unitType, metadata,
283
+ );
284
+ targetModelId = result.modelId;
285
+ capabilityScores = Object.keys(result.scores).length > 0 ? result.scores : undefined;
286
+ taskRequirements = Object.keys(result.requirements).length > 0 ? result.requirements : undefined;
287
+ selectionMethod = capabilityScores ? "capability-scored" : "tier-only";
288
+ } else {
289
+ targetModelId = findModelForTier(
290
+ requestedTier, routingConfig, availableModelIds,
291
+ routingConfig.cross_provider !== false,
292
+ );
293
+ }
385
294
 
386
- if (eligible.length === 0) {
387
- // No suitable model found — use configured primary
295
+ if (!targetModelId) {
388
296
  return {
389
297
  modelId: configuredPrimary,
390
298
  fallbacks: phaseConfig.fallbacks,
391
299
  tier: requestedTier,
392
300
  wasDowngraded: false,
393
301
  reason: `no ${requestedTier}-tier model available`,
394
- selectionMethod: "tier-only",
302
+ selectionMethod,
395
303
  };
396
304
  }
397
305
 
398
- // STEP 2: Capability scoring (when enabled and multiple eligible models exist)
399
- if (routingConfig.capability_routing !== false && eligible.length > 1 && unitType) {
400
- const requirements = computeTaskRequirements(unitType, taskMetadata);
401
- const scored = scoreEligibleModels(eligible, requirements, capabilityOverrides);
402
- const winner = scored[0];
403
- if (winner) {
404
- const capScores: Record<string, number> = {};
405
- for (const s of scored) capScores[s.modelId] = s.score;
406
- const fallbacks = buildFallbackChain(winner.modelId, phaseConfig);
407
- return {
408
- modelId: winner.modelId,
409
- fallbacks,
410
- tier: requestedTier,
411
- wasDowngraded: true,
412
- reason: `capability-scored: ${winner.modelId} (${winner.score.toFixed(1)}) for ${unitType}`,
413
- capabilityScores: capScores,
414
- taskRequirements: requirements,
415
- selectionMethod: "capability-scored",
416
- };
417
- }
418
- }
419
-
420
- // STEP 3: Fallback — use first eligible model (cheapest in tier, or single eligible)
421
- const targetModelId = eligible[0];
422
-
423
- // Build fallback chain: [downgraded_model, ...configured_fallbacks, configured_primary]
424
- const fallbacks = buildFallbackChain(targetModelId, phaseConfig);
306
+ const fallbacks = [
307
+ ...phaseConfig.fallbacks.filter(f => f !== targetModelId),
308
+ configuredPrimary,
309
+ ].filter(f => f !== targetModelId);
425
310
 
426
311
  return {
427
312
  modelId: targetModelId,
@@ -429,7 +314,9 @@ export function resolveModelForComplexity(
429
314
  tier: requestedTier,
430
315
  wasDowngraded: true,
431
316
  reason: classification.reason,
432
- selectionMethod: "tier-only",
317
+ selectionMethod,
318
+ capabilityScores,
319
+ taskRequirements,
433
320
  };
434
321
  }
435
322
 
@@ -451,7 +338,7 @@ export function escalateTier(currentTier: ComplexityTier): ComplexityTier | null
451
338
  export function defaultRoutingConfig(): DynamicRoutingConfig {
452
339
  return {
453
340
  enabled: true,
454
- capability_routing: true,
341
+ capability_routing: false,
455
342
  escalate_on_failure: true,
456
343
  budget_pressure: true,
457
344
  cross_provider: true,
@@ -473,8 +360,8 @@ function getModelTier(modelId: string): ComplexityTier {
473
360
  if (bareId.includes(knownId) || knownId.includes(bareId)) return tier;
474
361
  }
475
362
 
476
- // Unknown models are assumed standard (per D-15: avoids silently ignoring user config)
477
- return "standard";
363
+ // Unknown models are assumed heavy (safest assumption)
364
+ return "heavy";
478
365
  }
479
366
 
480
367
  /** Check if a model ID has a known capability tier mapping. (#2192) */
@@ -487,6 +374,93 @@ function isKnownModel(modelId: string): boolean {
487
374
  return false;
488
375
  }
489
376
 
377
+ function findModelForTier(
378
+ tier: ComplexityTier,
379
+ config: DynamicRoutingConfig,
380
+ availableModelIds: string[],
381
+ crossProvider: boolean,
382
+ ): string | null {
383
+ // 1. Check explicit tier_models config
384
+ const explicitModel = config.tier_models?.[tier];
385
+ if (explicitModel && availableModelIds.includes(explicitModel)) {
386
+ return explicitModel;
387
+ }
388
+ // Also check with provider prefix stripped
389
+ if (explicitModel) {
390
+ const match = availableModelIds.find(id => {
391
+ const bareAvail = id.includes("/") ? id.split("/").pop()! : id;
392
+ const bareExplicit = explicitModel.includes("/") ? explicitModel.split("/").pop()! : explicitModel;
393
+ return bareAvail === bareExplicit;
394
+ });
395
+ if (match) return match;
396
+ }
397
+
398
+ // 2. Auto-detect: find the cheapest available model in the requested tier
399
+ const candidates = availableModelIds
400
+ .filter(id => {
401
+ const modelTier = getModelTier(id);
402
+ return modelTier === tier;
403
+ })
404
+ .sort((a, b) => {
405
+ if (!crossProvider) return 0;
406
+ const costA = getModelCost(a);
407
+ const costB = getModelCost(b);
408
+ return costA - costB;
409
+ });
410
+
411
+ return candidates[0] ?? null;
412
+ }
413
+
414
+ function findModelForTierWithCapability(
415
+ tier: ComplexityTier,
416
+ config: DynamicRoutingConfig,
417
+ availableModelIds: string[],
418
+ crossProvider: boolean,
419
+ unitType: string,
420
+ metadata?: { tags?: string[]; complexityKeywords?: string[]; fileCount?: number; estimatedLines?: number },
421
+ ): { modelId: string | null; scores: Record<string, number>; requirements: Partial<Record<string, number>> } {
422
+ const explicitModel = config.tier_models?.[tier];
423
+ if (explicitModel) {
424
+ const match = availableModelIds.find(id => {
425
+ const bareAvail = id.includes("/") ? id.split("/").pop()! : id;
426
+ const bareExplicit = explicitModel.includes("/") ? explicitModel.split("/").pop()! : explicitModel;
427
+ return bareAvail === bareExplicit || id === explicitModel;
428
+ });
429
+ if (match) return { modelId: match, scores: {}, requirements: {} };
430
+ }
431
+
432
+ const requirements = computeTaskRequirements(unitType, metadata);
433
+ const candidates = availableModelIds.filter(id => getModelTier(id) === tier);
434
+ if (candidates.length === 0) return { modelId: null, scores: {}, requirements };
435
+
436
+ const scores: Record<string, number> = {};
437
+ for (const id of candidates) {
438
+ const bareId = id.includes("/") ? id.split("/").pop()! : id;
439
+ const profile = getModelProfile(bareId);
440
+ scores[id] = scoreModel(profile, requirements);
441
+ }
442
+
443
+ candidates.sort((a, b) => {
444
+ const scoreDiff = scores[b] - scores[a];
445
+ if (Math.abs(scoreDiff) > 2) return scoreDiff;
446
+ if (crossProvider) {
447
+ const costDiff = getModelCost(a) - getModelCost(b);
448
+ if (costDiff !== 0) return costDiff;
449
+ }
450
+ return a.localeCompare(b);
451
+ });
452
+
453
+ return { modelId: candidates[0], scores, requirements };
454
+ }
455
+
456
+ function getModelProfile(bareId: string): ModelCapabilities {
457
+ if (MODEL_CAPABILITY_PROFILES[bareId]) return MODEL_CAPABILITY_PROFILES[bareId];
458
+ for (const [knownId, profile] of Object.entries(MODEL_CAPABILITY_PROFILES)) {
459
+ if (bareId.includes(knownId) || knownId.includes(bareId)) return profile;
460
+ }
461
+ return { coding: 50, debugging: 50, research: 50, reasoning: 50, speed: 50, longContext: 50, instruction: 50 };
462
+ }
463
+
490
464
  function getModelCost(modelId: string): number {
491
465
  const bareId = modelId.includes("/") ? modelId.split("/").pop()! : modelId;
492
466
 
@@ -15,7 +15,6 @@ import { MergeConflictError } from "./git-service.js";
15
15
  import { removeSessionStatus } from "./session-status-io.js";
16
16
  import type { WorkerInfo } from "./parallel-orchestrator.js";
17
17
  import { getErrorMessage } from "./error-utils.js";
18
- import { logWarning } from "./workflow-logger.js";
19
18
 
20
19
  // ─── Types ─────────────────────────────────────────────────────────────────
21
20
 
@@ -48,8 +47,7 @@ export function isMilestoneCompleteInWorktreeDb(basePath: string, mid: string):
48
47
  { timeout: 3000, encoding: "utf-8" },
49
48
  );
50
49
  return (result.stdout || "").trim() === "complete";
51
- } catch (e) {
52
- logWarning("parallel", `spawnSync milestone completion check failed for ${mid}: ${(e as Error).message}`);
50
+ } catch {
53
51
  return false;
54
52
  }
55
53
  }
@@ -67,8 +65,8 @@ function discoverDbCompletedMilestones(basePath: string): Set<string> {
67
65
  completed.add(entry);
68
66
  }
69
67
  }
70
- } catch (e) {
71
- logWarning("parallel", `readdirSync for completed set failed: ${(e as Error).message}`);
68
+ } catch {
69
+ // worktrees dir may not exist
72
70
  }
73
71
  return completed;
74
72
  }
@@ -41,7 +41,6 @@ import {
41
41
  type ParallelCandidates,
42
42
  } from "./parallel-eligibility.js";
43
43
  import { getErrorMessage } from "./error-utils.js";
44
- import { logWarning } from "./workflow-logger.js";
45
44
 
46
45
  // ─── Types ─────────────────────────────────────────────────────────────────
47
46
 
@@ -127,7 +126,7 @@ export function persistState(basePath: string): void {
127
126
  const tmp = dest + TMP_SUFFIX;
128
127
  writeFileSync(tmp, JSON.stringify(persisted, null, 2), "utf-8");
129
128
  renameSync(tmp, dest);
130
- } catch (e) { logWarning("parallel", `persist parallel state failed: ${(e as Error).message}`); }
129
+ } catch { /* non-fatal */ }
131
130
  }
132
131
 
133
132
  /**
@@ -137,7 +136,7 @@ function removeStateFile(basePath: string): void {
137
136
  try {
138
137
  const p = stateFilePath(basePath);
139
138
  if (existsSync(p)) unlinkSync(p);
140
- } catch (e) { logWarning("parallel", `clear parallel state file failed: ${(e as Error).message}`); }
139
+ } catch { /* non-fatal */ }
141
140
  }
142
141
 
143
142
  function isPidAlive(pid: number): boolean {
@@ -145,8 +144,7 @@ function isPidAlive(pid: number): boolean {
145
144
  try {
146
145
  process.kill(pid, 0);
147
146
  return true;
148
- } catch (e) {
149
- logWarning("parallel", `pid alive check failed for pid ${pid}: ${(e as Error).message}`);
147
+ } catch {
150
148
  return false;
151
149
  }
152
150
  }
@@ -178,8 +176,7 @@ export function restoreState(basePath: string): PersistedState | null {
178
176
  }
179
177
 
180
178
  return persisted;
181
- } catch (e) {
182
- logWarning("parallel", `readParallelState JSON parse failed: ${(e as Error).message}`);
179
+ } catch {
183
180
  return null;
184
181
  }
185
182
  }
@@ -193,8 +190,8 @@ function appendWorkerLog(basePath: string, milestoneId: string, chunk: string):
193
190
  const dir = join(gsdRoot(basePath), "parallel");
194
191
  if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
195
192
  appendFileSync(workerLogPath(basePath, milestoneId), chunk, "utf-8");
196
- } catch (e) {
197
- logWarning("parallel", `appendFileSync worker log failed for ${milestoneId}: ${(e as Error).message}`);
193
+ } catch {
194
+ // Non-fatal diagnostics should never break orchestration.
198
195
  }
199
196
  }
200
197
 
@@ -433,8 +430,9 @@ export async function startParallel(
433
430
  let wtPath: string;
434
431
  try {
435
432
  wtPath = createMilestoneWorktree(basePath, mid);
436
- } catch (e) {
437
- logWarning("parallel", `createMilestoneWorktree fallback for ${mid}: ${(e as Error).message}`);
433
+ } catch {
434
+ // Worktree creation may fail in test environments or when git
435
+ // is not available. Fall back to a placeholder path.
438
436
  wtPath = worktreePath(basePath, mid);
439
437
  }
440
438
 
@@ -566,8 +564,7 @@ export function spawnWorker(
566
564
  stdio: ["ignore", "pipe", "pipe"],
567
565
  detached: false,
568
566
  });
569
- } catch (e) {
570
- logWarning("parallel", `spawnSync worker failed for ${milestoneId}: ${(e as Error).message}`);
567
+ } catch {
571
568
  return false;
572
569
  }
573
570
 
@@ -697,8 +694,7 @@ function resolveGsdBin(): string | null {
697
694
  let thisDir: string;
698
695
  try {
699
696
  thisDir = dirname(fileURLToPath(import.meta.url));
700
- } catch (e) {
701
- logWarning("parallel", `dirname(fileURLToPath) failed: ${(e as Error).message}`);
697
+ } catch {
702
698
  thisDir = process.cwd();
703
699
  }
704
700
  const candidates = [
@@ -726,7 +722,7 @@ function processWorkerLine(basePath: string, milestoneId: string, line: string):
726
722
  try {
727
723
  event = JSON.parse(line);
728
724
  } catch {
729
- return; // Non-NDJSON lines (progress text, tool output) are expected — silent drop
725
+ return; // Not valid JSON — skip (stderr leakage, debug output, etc.)
730
726
  }
731
727
 
732
728
  const type = String(event.type ?? "");
@@ -821,7 +817,7 @@ export async function stopParallel(
821
817
  } else if (worker.pid !== process.pid) {
822
818
  process.kill(worker.pid, "SIGTERM");
823
819
  }
824
- } catch (e) { logWarning("parallel", `process.kill SIGTERM failed for pid ${worker.pid}: ${(e as Error).message}`); }
820
+ } catch { /* process may already be dead */ }
825
821
  }
826
822
 
827
823
  // Wait for the headless process to cascade SIGTERM to its RPC child.
@@ -837,7 +833,7 @@ export async function stopParallel(
837
833
  } else if (worker.pid !== process.pid) {
838
834
  process.kill(worker.pid, "SIGKILL");
839
835
  }
840
- } catch (e) { logWarning("parallel", `process.kill SIGKILL failed for pid ${worker.pid}: ${(e as Error).message}`); }
836
+ } catch { /* process may already be dead */ }
841
837
  await waitForWorkerExit(worker, 250);
842
838
  }
843
839