sneakoscope 4.8.7 → 5.3.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 (261) hide show
  1. package/README.md +51 -726
  2. package/config/bench-baseline.json +7 -0
  3. package/crates/sks-core/Cargo.lock +1 -1
  4. package/crates/sks-core/Cargo.toml +1 -1
  5. package/crates/sks-core/src/main.rs +1 -1
  6. package/dist/bin/install.js +35 -0
  7. package/dist/bin/sks.js +1 -1
  8. package/dist/cli/cli-theme.js +51 -0
  9. package/dist/cli/command-registry.js +15 -4
  10. package/dist/cli/help-fast.js +18 -8
  11. package/dist/cli/insane-search-command.js +36 -8
  12. package/dist/cli/install-helpers.js +32 -0
  13. package/dist/commands/codex-lb.js +2 -0
  14. package/dist/commands/doctor.js +94 -9
  15. package/dist/commands/proof.js +6 -3
  16. package/dist/config/skills-manifest.json +82 -54
  17. package/dist/core/agents/agent-conflict-graph.js +5 -0
  18. package/dist/core/agents/agent-lease.js +5 -0
  19. package/dist/core/agents/agent-ledger-schemas.js +1 -0
  20. package/dist/core/agents/agent-lifecycle.js +5 -3
  21. package/dist/core/agents/agent-orchestrator.js +268 -62
  22. package/dist/core/agents/agent-output-validator.js +14 -3
  23. package/dist/core/agents/agent-patch-queue-store.js +95 -2
  24. package/dist/core/agents/agent-patch-queue.js +6 -3
  25. package/dist/core/agents/agent-patch-schema.js +13 -0
  26. package/dist/core/agents/agent-proof-evidence.js +6 -1
  27. package/dist/core/agents/agent-runner-fake.js +40 -0
  28. package/dist/core/agents/agent-worker-pipeline.js +36 -2
  29. package/dist/core/agents/codex-exec-worker-adapter.js +2 -0
  30. package/dist/core/agents/native-cli-worker.js +0 -1
  31. package/dist/core/agents/native-worker-backend-router.js +58 -1
  32. package/dist/core/codex-app/sks-menubar.js +477 -132
  33. package/dist/core/codex-app.js +1 -0
  34. package/dist/core/codex-control/codex-sdk-config-policy.js +2 -1
  35. package/dist/core/codex-control/codex-sdk-env-policy.js +1 -1
  36. package/dist/core/codex-control/schemas/agent-worker-result.schema.js +12 -3
  37. package/dist/core/codex-hooks/codex-hook-managed-install.js +5 -0
  38. package/dist/core/codex-hooks/codex-hook-state-writer.js +70 -11
  39. package/dist/core/codex-hooks/codex-hook-trust-doctor.js +2 -2
  40. package/dist/core/codex-lb/codex-lb-env.js +20 -1
  41. package/dist/core/commands/basic-cli.js +18 -1
  42. package/dist/core/commands/check-command.js +37 -5
  43. package/dist/core/commands/computer-use-command.js +62 -7
  44. package/dist/core/commands/db-command.js +31 -5
  45. package/dist/core/commands/fast-mode-command.js +3 -0
  46. package/dist/core/commands/gate-result-contract.js +43 -0
  47. package/dist/core/commands/gates-command.js +6 -1
  48. package/dist/core/commands/gc-command.js +29 -2
  49. package/dist/core/commands/goal-command.js +9 -2
  50. package/dist/core/commands/gx-command.js +79 -7
  51. package/dist/core/commands/image-ux-review-command.js +188 -13
  52. package/dist/core/commands/mad-db-command.js +85 -176
  53. package/dist/core/commands/mad-sks-command.js +230 -29
  54. package/dist/core/commands/menubar-command.js +24 -2
  55. package/dist/core/commands/naruto-command.js +64 -10
  56. package/dist/core/commands/plan-command.js +76 -0
  57. package/dist/core/commands/ppt-command.js +159 -24
  58. package/dist/core/commands/qa-loop-command.js +6 -2
  59. package/dist/core/commands/release-command.js +55 -2
  60. package/dist/core/commands/research-command.js +1 -1
  61. package/dist/core/commands/review-command.js +217 -0
  62. package/dist/core/commands/route-success-helpers.js +59 -0
  63. package/dist/core/commands/run-command.js +19 -7
  64. package/dist/core/commands/seo-command.js +49 -1
  65. package/dist/core/commands/ui-command.js +161 -0
  66. package/dist/core/commands/uninstall-command.js +15 -1
  67. package/dist/core/db-safety.js +2 -2
  68. package/dist/core/doctor/doctor-dirty-planner.js +51 -7
  69. package/dist/core/doctor/doctor-transaction.js +8 -0
  70. package/dist/core/doctor/doctor-zellij-repair.js +1 -0
  71. package/dist/core/doctor/imagegen-repair.js +161 -0
  72. package/dist/core/feature-fixture-runner.js +2 -2
  73. package/dist/core/feature-fixtures.js +58 -37
  74. package/dist/core/feature-registry.js +102 -2
  75. package/dist/core/fsx.js +39 -6
  76. package/dist/core/image-ux-review/imagegen-adapter.js +48 -11
  77. package/dist/core/image-ux-review.js +16 -0
  78. package/dist/core/imagegen/imagegen-capability.js +11 -5
  79. package/dist/core/imagegen/require-imagegen.js +57 -0
  80. package/dist/core/init/skills.js +10 -5
  81. package/dist/core/mad-db/mad-db-coordinator.js +94 -18
  82. package/dist/core/mad-db/mad-db-policy.js +12 -10
  83. package/dist/core/mad-sks/executors/executor-base.js +8 -2
  84. package/dist/core/mad-sks/executors/index.js +4 -0
  85. package/dist/core/mad-sks/executors/sql-plane-executor.js +194 -0
  86. package/dist/core/naruto/naruto-active-pool.js +15 -4
  87. package/dist/core/naruto/naruto-real-worker-child.js +11 -1
  88. package/dist/core/naruto/naruto-role-policy.js +3 -0
  89. package/dist/core/naruto/naruto-task-hints.js +10 -0
  90. package/dist/core/naruto/naruto-work-graph.js +8 -2
  91. package/dist/core/naruto/naruto-work-item.js +6 -0
  92. package/dist/core/naruto/solution-tournament.js +101 -0
  93. package/dist/core/permission-gates.js +30 -0
  94. package/dist/core/pipeline-internals/runtime-core.js +11 -3
  95. package/dist/core/pipeline-internals/runtime-gates.js +27 -0
  96. package/dist/core/ppt-review/index.js +6 -1
  97. package/dist/core/ppt-review/slide-imagegen-review.js +75 -10
  98. package/dist/core/ppt.js +70 -2
  99. package/dist/core/proof/auto-finalize.js +70 -11
  100. package/dist/core/proof/proof-schema.js +2 -0
  101. package/dist/core/proof/route-adapter.js +5 -1
  102. package/dist/core/proof/route-finalizer.js +8 -5
  103. package/dist/core/proof/selftest-proof-fixtures.js +18 -5
  104. package/dist/core/proof/validation.js +2 -0
  105. package/dist/core/provider/model-router.js +53 -0
  106. package/dist/core/release/release-gate-node.js +3 -0
  107. package/dist/core/release/release-gate-resource-governor.js +1 -0
  108. package/dist/core/routes/constants.js +1 -1
  109. package/dist/core/routes.js +65 -18
  110. package/dist/core/safety/mutation-guard.js +5 -1
  111. package/dist/core/stop-gate/gate-evaluator.js +102 -0
  112. package/dist/core/stop-gate/stop-gate-check.js +16 -1
  113. package/dist/core/stop-gate/stop-gate-writer.js +4 -0
  114. package/dist/core/triwiki/agents-md-projector.js +184 -0
  115. package/dist/core/triwiki-wrongness/wrongness-schema.js +3 -1
  116. package/dist/core/trust-kernel/trust-kernel-schema.js +1 -0
  117. package/dist/core/trust-kernel/trust-report.js +1 -1
  118. package/dist/core/trust-kernel/trust-status.js +2 -0
  119. package/dist/core/ui/dashboard-html.js +111 -0
  120. package/dist/core/update/update-migration-state.js +386 -15
  121. package/dist/core/update-check.js +178 -81
  122. package/dist/core/verification/diff-quality.js +100 -0
  123. package/dist/core/verification/impact-scan.js +164 -0
  124. package/dist/core/verification/machine-feedback.js +146 -0
  125. package/dist/core/verification/mistake-rule-compiler.js +195 -0
  126. package/dist/core/version.js +1 -1
  127. package/dist/core/zellij/zellij-self-heal.js +9 -6
  128. package/dist/core/zellij/zellij-slot-telemetry.js +1 -1
  129. package/dist/scripts/check-feature-quality.js +2 -2
  130. package/dist/scripts/cli-output-consistency-check.js +57 -0
  131. package/dist/scripts/coding-bench-check.js +136 -0
  132. package/dist/scripts/doctor-imagegen-repair-check.js +62 -0
  133. package/dist/scripts/ensure-bin-executable.js +11 -3
  134. package/dist/scripts/harness-benchmark-check.js +104 -0
  135. package/dist/scripts/legacy-update-e2e-check.js +228 -0
  136. package/dist/scripts/mad-db-command-check.js +11 -8
  137. package/dist/scripts/mad-db-route-identity-check.js +8 -6
  138. package/dist/scripts/mad-db-safety-conflict-matrix-check.js +31 -8
  139. package/dist/scripts/mad-db-skill-policy-snapshot-check.js +4 -5
  140. package/dist/scripts/naruto-real-active-pool-runtime-check.js +26 -1
  141. package/dist/scripts/ppt-real-imagegen-wiring-check.js +5 -1
  142. package/dist/scripts/proof-root-cause-policy-check.js +2 -0
  143. package/dist/scripts/release-dag-full-coverage-check.js +13 -4
  144. package/dist/scripts/release-gate-dag-runner.js +14 -0
  145. package/dist/scripts/release-gate-existence-audit.js +2 -1
  146. package/dist/scripts/release-gate-planner.js +2 -1
  147. package/dist/scripts/release-gate-script-parity-check.js +45 -17
  148. package/dist/scripts/release-metadata-1-19-check.js +2 -1
  149. package/dist/scripts/seo-geo-feature-fixture-quality-check.js +1 -1
  150. package/dist/scripts/sks-1-11-gate-lib.js +8 -6
  151. package/dist/scripts/sks-menubar-install-check.js +57 -20
  152. package/dist/scripts/ux-review-real-loop-fixture-check.js +9 -4
  153. package/dist/scripts/ux-review-run-wires-imagegen-check.js +4 -0
  154. package/dist/scripts/zellij-slot-telemetry-runtime-check.js +1 -0
  155. package/docs/assets/sneakoscope-architecture-pipeline.jpg +0 -0
  156. package/docs/demo.tape +28 -0
  157. package/package.json +31 -24
  158. package/schemas/codex/completion-proof.schema.json +3 -2
  159. package/schemas/release/release-gate-node.schema.json +2 -1
  160. package/dist/scripts/agent-backfill-route-blackbox.js +0 -5
  161. package/dist/scripts/agent-dynamic-pool-route-blackbox.js +0 -5
  162. package/dist/scripts/agent-parallel-write-blackbox.js +0 -56
  163. package/dist/scripts/agent-patch-swarm-route-blackbox.js +0 -10
  164. package/dist/scripts/agent-route-blackbox-lib.js +0 -132
  165. package/dist/scripts/blackbox-command-import-smoke.js +0 -143
  166. package/dist/scripts/blackbox-global-shim.js +0 -77
  167. package/dist/scripts/blackbox-matrix.js +0 -70
  168. package/dist/scripts/blackbox-npx-one-shot.js +0 -69
  169. package/dist/scripts/blackbox-pack-install.js +0 -174
  170. package/dist/scripts/brand-neutrality-zero-leakage-blackbox.js +0 -4
  171. package/dist/scripts/build-once-runner-blackbox.js +0 -34
  172. package/dist/scripts/codex-0140-integration-blackbox.js +0 -13
  173. package/dist/scripts/codex-agent-type-blackbox.js +0 -4
  174. package/dist/scripts/codex-app-harness-blackbox.js +0 -4
  175. package/dist/scripts/codex-app-skill-agent-blackbox.js +0 -4
  176. package/dist/scripts/codex-hook-approval-blackbox.js +0 -4
  177. package/dist/scripts/codex-init-deep-directory-local-blackbox.js +0 -4
  178. package/dist/scripts/codex-native-feature-broker-blackbox.js +0 -4
  179. package/dist/scripts/codex-native-pattern-analysis-blackbox.js +0 -4
  180. package/dist/scripts/codex-native-read-repair-split-blackbox.js +0 -55
  181. package/dist/scripts/codex-native-reference-cache-blackbox.js +0 -41
  182. package/dist/scripts/core-skill-integrity-blackbox.js +0 -32
  183. package/dist/scripts/dfix-fast-blackbox-check.js +0 -37
  184. package/dist/scripts/dfix-parallel-write-blackbox.js +0 -48
  185. package/dist/scripts/dfix-patch-swarm-route-blackbox.js +0 -10
  186. package/dist/scripts/doctor-dirty-repair-blackbox.js +0 -22
  187. package/dist/scripts/doctor-dirty-semantic-blackbox.js +0 -8
  188. package/dist/scripts/doctor-native-capability-repair-blackbox.js +0 -39
  189. package/dist/scripts/doctor-transaction-engine-blackbox.js +0 -28
  190. package/dist/scripts/doctor-zellij-fix-blackbox.js +0 -4
  191. package/dist/scripts/doctor-zellij-no-homebrew-blackbox.js +0 -4
  192. package/dist/scripts/doctor-zellij-upgrade-blackbox.js +0 -4
  193. package/dist/scripts/gate-pack-runner-blackbox.js +0 -27
  194. package/dist/scripts/gate-pack-v2-blackbox.js +0 -18
  195. package/dist/scripts/geo-cli-blackbox-check.js +0 -18
  196. package/dist/scripts/loop-collision-blackbox.js +0 -3
  197. package/dist/scripts/loop-concurrency-oversubscription-blackbox.js +0 -3
  198. package/dist/scripts/loop-fixture-production-misuse-blackbox.js +0 -3
  199. package/dist/scripts/loop-kill-interrupt-real-blackbox.js +0 -3
  200. package/dist/scripts/loop-merge-strategy-blackbox.js +0 -3
  201. package/dist/scripts/loop-mesh-production-e2e-blackbox.js +0 -3
  202. package/dist/scripts/loop-side-effect-blackbox.js +0 -3
  203. package/dist/scripts/mad-db-operation-lifecycle-blackbox.js +0 -29
  204. package/dist/scripts/mad-sks-actual-executor-blackbox.js +0 -5
  205. package/dist/scripts/mad-zellij-headless-fallback-blackbox.js +0 -4
  206. package/dist/scripts/mad-zellij-self-heal-blackbox.js +0 -4
  207. package/dist/scripts/naruto-loop-mesh-blackbox.js +0 -3
  208. package/dist/scripts/naruto-real-parallelism-blackbox.js +0 -307
  209. package/dist/scripts/naruto-worktree-coding-blackbox.js +0 -29
  210. package/dist/scripts/parallel-runtime-real-blackbox.js +0 -44
  211. package/dist/scripts/pipeline-codex-native-doctor-mad-routing-real-blackbox.js +0 -77
  212. package/dist/scripts/pipeline-codex-native-e2e-blackbox.js +0 -13
  213. package/dist/scripts/pipeline-codex-native-image-routing-real-blackbox.js +0 -50
  214. package/dist/scripts/pipeline-codex-native-loop-routing-real-blackbox.js +0 -74
  215. package/dist/scripts/pipeline-codex-native-qa-routing-real-blackbox.js +0 -51
  216. package/dist/scripts/pipeline-codex-native-research-routing-real-blackbox.js +0 -45
  217. package/dist/scripts/pipeline-execution-profile-routing-blackbox.js +0 -4
  218. package/dist/scripts/postinstall-global-doctor-blackbox.js +0 -12
  219. package/dist/scripts/ppt-full-e2e-blackbox-check.js +0 -109
  220. package/dist/scripts/ppt-imagegen-blackbox-check.js +0 -46
  221. package/dist/scripts/qa-backfill-route-blackbox.js +0 -5
  222. package/dist/scripts/qa-patch-swarm-route-blackbox.js +0 -10
  223. package/dist/scripts/release-full-parallelism-blackbox.js +0 -41
  224. package/dist/scripts/release-triwiki-first-runner-blackbox.js +0 -48
  225. package/dist/scripts/release-wiring-3110-blackbox.js +0 -27
  226. package/dist/scripts/release-wiring-3112-blackbox.js +0 -17
  227. package/dist/scripts/release-wiring-3113-blackbox.js +0 -17
  228. package/dist/scripts/research-backfill-route-blackbox.js +0 -5
  229. package/dist/scripts/research-final-reviewer-blackbox.js +0 -70
  230. package/dist/scripts/research-stage-cycle-runtime-blackbox.js +0 -40
  231. package/dist/scripts/research-synthesis-writer-blackbox.js +0 -24
  232. package/dist/scripts/route-blackbox-realism-check.js +0 -21
  233. package/dist/scripts/scheduler-resource-claim-blackbox.js +0 -24
  234. package/dist/scripts/seo-cli-blackbox-check.js +0 -18
  235. package/dist/scripts/sks-3110-all-feature-regression-blackbox.js +0 -116
  236. package/dist/scripts/sks-3112-all-feature-regression-blackbox.js +0 -29
  237. package/dist/scripts/sks-3113-all-feature-regression-blackbox.js +0 -17
  238. package/dist/scripts/sks-400-all-feature-regression-blackbox.js +0 -21
  239. package/dist/scripts/sks-400-extreme-parallel-blackbox.js +0 -8
  240. package/dist/scripts/sks-400-five-minute-blackbox.js +0 -9
  241. package/dist/scripts/sks-400-legacy-purge-blackbox.js +0 -8
  242. package/dist/scripts/sks-401-all-feature-regression-blackbox.js +0 -46
  243. package/dist/scripts/sks-401-five-minute-actual-blackbox.js +0 -23
  244. package/dist/scripts/sks-402-all-feature-regression-blackbox.js +0 -9
  245. package/dist/scripts/sks-402-five-minute-real-blackbox.js +0 -22
  246. package/dist/scripts/sksd-warm-cache-blackbox.js +0 -12
  247. package/dist/scripts/team-backfill-route-blackbox.js +0 -5
  248. package/dist/scripts/team-parallel-write-blackbox.js +0 -55
  249. package/dist/scripts/team-patch-swarm-route-blackbox.js +0 -10
  250. package/dist/scripts/triwiki-affected-graph-blackbox.js +0 -28
  251. package/dist/scripts/triwiki-proof-bank-blackbox.js +0 -30
  252. package/dist/scripts/triwiki-proof-bank-lock-blackbox.js +0 -7
  253. package/dist/scripts/ux-patch-swarm-route-blackbox.js +0 -10
  254. package/dist/scripts/ux-review-imagegen-blackbox-check.js +0 -67
  255. package/dist/scripts/zellij-initial-main-only-blackbox.js +0 -28
  256. package/dist/scripts/zellij-pane-lock-concurrency-blackbox.js +0 -80
  257. package/dist/scripts/zellij-pane-lock-open-worker-integration-blackbox.js +0 -137
  258. package/dist/scripts/zellij-self-heal-typed-blackbox.js +0 -4
  259. package/dist/scripts/zellij-slot-telemetry-real-blackbox.js +0 -20
  260. package/dist/scripts/zellij-stacked-fallback-integration-blackbox.js +0 -81
  261. package/dist/scripts/zellij-worker-pane-real-ui-blackbox.js +0 -202
@@ -1,7 +1,11 @@
1
1
  import path from 'node:path';
2
- import { appendJsonl, appendJsonlMany, readJson, writeJsonAtomic } from '../fsx.js';
2
+ import { appendJsonl, appendJsonlMany, ensureDir, readJson, writeJsonAtomic } from '../fsx.js';
3
3
  import { AGENT_PATCH_QUEUE_SCHEMA, InMemoryAgentPatchQueue, buildAgentPatchOwnershipLedger } from './agent-patch-queue.js';
4
4
  import { AgentPatchTransactionJournal } from './agent-patch-transaction-journal.js';
5
+ import { normalizeAgentPatchEnvelope } from './agent-patch-schema.js';
6
+ import { scanImpact } from '../verification/impact-scan.js';
7
+ import { runMachineFeedback } from '../verification/machine-feedback.js';
8
+ import { analyzeDiffQuality } from '../verification/diff-quality.js';
5
9
  export const AGENT_PATCH_QUEUE_ARTIFACT = 'agent-patch-queue.json';
6
10
  export const AGENT_PATCH_QUEUE_EVENTS_ARTIFACT = 'agent-patch-queue-events.jsonl';
7
11
  export const AGENT_PATCH_OWNERSHIP_LEDGER_ARTIFACT = 'agent-patch-ownership-ledger.json';
@@ -15,7 +19,13 @@ export class PersistentAgentPatchQueueStore {
15
19
  this.journal = new AgentPatchTransactionJournal(artifactDir);
16
20
  }
17
21
  async enqueue(input, context = {}) {
18
- const entry = this.queue.enqueue(input, context);
22
+ const preflight = await this.runQualityPreflight(input, context);
23
+ const entry = this.queue.enqueue(input, {
24
+ ...(context.mission_id ? { mission_id: context.mission_id } : {}),
25
+ ...(context.route ? { route: context.route } : {}),
26
+ preflight_violations: preflight.violations,
27
+ preflight_reports: preflight.reports
28
+ });
19
29
  await this.persistSnapshot();
20
30
  await this.appendEvent(this.queue.events.at(-1));
21
31
  await this.journal.append({
@@ -29,6 +39,43 @@ export class PersistentAgentPatchQueueStore {
29
39
  });
30
40
  return entry;
31
41
  }
42
+ async runQualityPreflight(input, context = {}) {
43
+ const envelope = normalizeAgentPatchEnvelope(input);
44
+ const changedFiles = changedFilesForEnvelope(envelope);
45
+ const patchText = patchTextForEnvelope(envelope);
46
+ const root = envelope.git_worktree?.worktree_path || context.root || process.cwd();
47
+ const reportDir = path.join(this.artifactDir, 'patch-quality', safeName(`${envelope.agent_id}-${envelope.session_id}-${Date.now()}`));
48
+ await ensureDir(reportDir);
49
+ const [impact, diffQuality, feedback] = await Promise.all([
50
+ scanImpact(root, changedFiles, patchText).catch((err) => ({ schema: 'sks.impact-scan.v1', changed_symbols: [], references: [], cochange_required: [], tool: 'builtin', error: errorMessage(err) })),
51
+ analyzeDiffQuality({
52
+ root,
53
+ changedFiles,
54
+ patchText,
55
+ plannedFiles: envelope.allowed_paths || envelope.lease_proof?.allowed_paths || []
56
+ }).catch((err) => ({ schema: 'sks.diff-quality.v1', minimality: { plan_files: 0, touched_files: changedFiles.length, ratio: 1 }, dead_additions: [], comment_noise: 0, guard_bloat: 0, warnings: [], errors: [`diff_quality_failed:${errorMessage(err)}`] })),
57
+ runMachineFeedback(root, changedFiles, { timeoutMs: 60_000 }).catch((err) => ({ schema: 'sks.machine-feedback.v1', ok: false, typecheck: { ok: false, errors: [errorMessage(err)] }, lint: { ok: true, errors: [] }, tests: { ok: true, selected: [], failed: [], skipped_reason: 'machine_feedback_exception' }, duration_ms: 0 }))
58
+ ]);
59
+ await writeJsonAtomic(path.join(reportDir, 'impact-scan.json'), impact);
60
+ await writeJsonAtomic(path.join(reportDir, 'diff-quality.json'), diffQuality);
61
+ await writeJsonAtomic(path.join(reportDir, 'machine-feedback.json'), feedback);
62
+ const cochangeAck = envelope.cochange_acknowledged === true && String(envelope.cochange_acknowledged_reason || '').trim().length > 0;
63
+ const violations = [
64
+ ...(impact.cochange_required?.length && !cochangeAck ? [`impact_scan_cochange_missing:${impact.cochange_required.slice(0, 5).join(',')}`] : []),
65
+ ...((diffQuality.errors || []).map((issue) => `diff_quality:${issue}`)),
66
+ ...(feedback.ok === false ? ['machine_feedback_failed'] : []),
67
+ ...(isBugfixKind(context.work_item_kind, envelope.task_slice_id) && !validRegressionProof(envelope.regression_proof || context.regression_proof) ? ['tdd_evidence_missing'] : []),
68
+ ...(isRepairKind(context.work_item_kind, envelope.task_slice_id) && !(envelope.repair_hypothesis || context.repair_hypothesis) ? ['repair_without_hypothesis'] : [])
69
+ ];
70
+ return {
71
+ violations,
72
+ reports: {
73
+ impact_scan: path.relative(this.artifactDir, path.join(reportDir, 'impact-scan.json')),
74
+ diff_quality: path.relative(this.artifactDir, path.join(reportDir, 'diff-quality.json')),
75
+ machine_feedback: path.relative(this.artifactDir, path.join(reportDir, 'machine-feedback.json'))
76
+ }
77
+ };
78
+ }
32
79
  async markApplying(id) {
33
80
  await this.transition(id, () => this.queue.markApplying(id));
34
81
  }
@@ -98,4 +145,50 @@ export class PersistentAgentPatchQueueStore {
98
145
  await appendJsonlMany(path.join(this.artifactDir, AGENT_PATCH_QUEUE_EVENTS_ARTIFACT), events.filter(Boolean));
99
146
  }
100
147
  }
148
+ function isBugfixKind(kind, id) {
149
+ const text = `${String(kind || '')} ${String(id || '')}`.toLowerCase();
150
+ return /\b(bugfix|fix|bug|regression|broken|failure|crash|error)\b|버그|회귀/.test(text);
151
+ }
152
+ function isRepairKind(kind, id) {
153
+ const text = `${String(kind || '')} ${String(id || '')}`.toLowerCase();
154
+ return /\b(conflict_resolution|repair|conflict|rebase|rollback)\b|수리|충돌/.test(text);
155
+ }
156
+ function validRegressionProof(proof) {
157
+ return Boolean(proof && proof.failed_before === true && proof.passed_after === true && String(proof.test_file || '').trim());
158
+ }
159
+ function changedFilesForEnvelope(envelope) {
160
+ const files = envelope.git_worktree?.changed_files?.length
161
+ ? envelope.git_worktree.changed_files
162
+ : envelope.operations.map((operation) => operation.path);
163
+ return [...new Set(files.map(normalizePath).filter(Boolean))];
164
+ }
165
+ function patchTextForEnvelope(envelope) {
166
+ return envelope.operations.map((operation) => {
167
+ if (operation.diff)
168
+ return operation.diff;
169
+ if (operation.op === 'replace')
170
+ return [
171
+ `--- a/${operation.path}`,
172
+ `+++ b/${operation.path}`,
173
+ `-${operation.search || ''}`,
174
+ `+${operation.replace || ''}`
175
+ ].join('\n');
176
+ if (operation.op === 'write')
177
+ return [
178
+ `--- /dev/null`,
179
+ `+++ b/${operation.path}`,
180
+ ...String(operation.content || '').split(/\r?\n/).map((line) => `+${line}`)
181
+ ].join('\n');
182
+ return '';
183
+ }).join('\n');
184
+ }
185
+ function normalizePath(value) {
186
+ return String(value || '').replace(/\\/g, '/').replace(/^\.\/+/, '');
187
+ }
188
+ function safeName(value) {
189
+ return String(value || 'patch').replace(/[^A-Za-z0-9_.-]+/g, '-').slice(0, 160);
190
+ }
191
+ function errorMessage(err) {
192
+ return err instanceof Error ? err.message : String(err);
193
+ }
101
194
  //# sourceMappingURL=agent-patch-queue-store.js.map
@@ -6,6 +6,7 @@ export class InMemoryAgentPatchQueue {
6
6
  enqueue(input, context = {}) {
7
7
  const envelope = normalizeAgentPatchEnvelope(input);
8
8
  const validation = validateAgentPatchEnvelope(envelope);
9
+ const preflightViolations = Array.isArray(context.preflight_violations) ? context.preflight_violations.map(String) : [];
9
10
  const now = new Date().toISOString();
10
11
  const leaseId = envelope.lease_id || envelope.lease_proof?.lease_id;
11
12
  const entry = {
@@ -19,8 +20,9 @@ export class InMemoryAgentPatchQueue {
19
20
  ...(leaseId ? { lease_id: leaseId } : {}),
20
21
  write_paths: envelope.operations.map((operation) => operation.path),
21
22
  envelope,
22
- status: validation.ok ? 'pending' : 'rejected',
23
- violations: validation.violations,
23
+ status: validation.ok && preflightViolations.length === 0 ? 'pending' : 'rejected',
24
+ violations: [...validation.violations, ...preflightViolations],
25
+ ...(context.preflight_reports ? { quality_reports: context.preflight_reports } : {}),
24
26
  created_at: now,
25
27
  updated_at: now
26
28
  };
@@ -97,7 +99,8 @@ export function buildAgentPatchOwnershipLedger(entries) {
97
99
  status: entry.status,
98
100
  created_at: entry.created_at,
99
101
  updated_at: entry.updated_at,
100
- violations: [...entry.violations]
102
+ violations: [...entry.violations],
103
+ quality_reports: entry.quality_reports || null
101
104
  }));
102
105
  }
103
106
  //# sourceMappingURL=agent-patch-queue.js.map
@@ -31,6 +31,11 @@ export function normalizeAgentPatchEnvelope(input) {
31
31
  ...(input?.rationale ? { rationale: String(input.rationale) } : {}),
32
32
  ...(input?.verification_hint ? { verification_hint: normalizeHint(input.verification_hint) } : {}),
33
33
  ...(input?.rollback_hint ? { rollback_hint: normalizeHint(input.rollback_hint) } : {}),
34
+ ...(input?.cochange_acknowledged === undefined ? {} : { cochange_acknowledged: Boolean(input.cochange_acknowledged) }),
35
+ ...(input?.cochange_acknowledged_reason === undefined ? {} : { cochange_acknowledged_reason: String(input.cochange_acknowledged_reason) }),
36
+ ...(input?.regression_proof ? { regression_proof: normalizeRegressionProof(input.regression_proof) } : {}),
37
+ ...(input?.repair_hypothesis && typeof input.repair_hypothesis === 'object' ? { repair_hypothesis: input.repair_hypothesis } : {}),
38
+ ...(input?.tournament && typeof input.tournament === 'object' ? { tournament: input.tournament } : {}),
34
39
  operations: Array.isArray(input?.operations) ? input.operations.map(normalizeOperation) : []
35
40
  };
36
41
  }
@@ -135,6 +140,14 @@ function normalizeHint(input) {
135
140
  ...(input?.notes === undefined ? {} : { notes: String(input.notes) })
136
141
  };
137
142
  }
143
+ function normalizeRegressionProof(input) {
144
+ return {
145
+ ...(input?.test_file === undefined ? {} : { test_file: String(input.test_file) }),
146
+ ...(input?.failed_before === undefined ? {} : { failed_before: Boolean(input.failed_before) }),
147
+ ...(input?.passed_after === undefined ? {} : { passed_after: Boolean(input.passed_after) }),
148
+ ...(input?.output_digest === undefined ? {} : { output_digest: String(input.output_digest) })
149
+ };
150
+ }
138
151
  function pathAllowedByLease(operationPath, allowedPaths) {
139
152
  const rel = normalizePatchPath(operationPath);
140
153
  return allowedPaths.map(normalizePatchPath).some((allowed) => rel === allowed || rel.startsWith(`${allowed}/`));
@@ -129,6 +129,11 @@ export async function writeAgentProofEvidence(root, input) {
129
129
  narutoWorkGraph
130
130
  });
131
131
  const changedFileLeaseBlockers = readOnlyNoWriteLeaseMode ? [] : agentChangedFileLeaseViolations(input.results || [], input.partition?.leases || []);
132
+ const schedulerBackfillSatisfied = !scheduler
133
+ || Number(scheduler.expected_backfill_count || 0) <= Number(scheduler.backfill_count || 0)
134
+ || (scheduler.pending_queue_drained === true
135
+ && Number(scheduler.active_slot_count || 0) === 0
136
+ && Number(scheduler.max_observed_active_slots || 0) >= Number(scheduler.target_active_slots || 0));
132
137
  const blockers = [
133
138
  ...(lifecycle.ok ? [] : ['agent_lifecycle_not_all_closed']),
134
139
  ...(lifecycle.ok ? [] : lifecycle.open_sessions.map((id) => 'session_open:' + id)),
@@ -144,7 +149,7 @@ export async function writeAgentProofEvidence(root, input) {
144
149
  ...(scheduler && scheduler.pending_count > 0 && scheduler.active_slot_count === 0 ? ['scheduler_pending_queue_without_active_sessions'] : []),
145
150
  ...(scheduler && scheduler.pending_queue_drained !== true ? ['scheduler_pending_queue_not_drained'] : []),
146
151
  ...(scheduler && Number(scheduler.active_slot_count || 0) !== 0 ? ['scheduler_active_slots_not_zero_at_finalization'] : []),
147
- ...(scheduler && Number(scheduler.expected_backfill_count || 0) > Number(scheduler.backfill_count || 0) ? ['scheduler_backfill_count_below_expected'] : []),
152
+ ...(schedulerBackfillSatisfied ? [] : ['scheduler_backfill_count_below_expected']),
148
153
  ...(scheduler && Number(scheduler.total_work_items || 0) >= Number(scheduler.target_active_slots || 0) && Number(scheduler.max_observed_active_slots || 0) !== Number(scheduler.target_active_slots || 0) ? ['scheduler_max_observed_active_slots_mismatch'] : []),
149
154
  ...(taskGraph && !taskGraphMatchesCliOptions ? ['task_graph_cli_options_mismatch'] : []),
150
155
  ...(workQueue && taskGraph && !workQueueMatchesTaskGraph ? ['work_queue_task_graph_mismatch'] : []),
@@ -23,6 +23,10 @@ export async function runFakeAgent(agent, slice, opts = {}) {
23
23
  unverified: ['fake backend does not prove real parallel execution'],
24
24
  writes: [],
25
25
  ...(patchEnvelopes.length ? { patch_envelopes: patchEnvelopes } : {}),
26
+ ...(isBugfixSlice(slice) && patchEnvelopes.length ? { regression_proof: fixtureRegressionProof(slice) } : {}),
27
+ ...(isRepairSlice(slice) && patchEnvelopes.length ? { repair_hypothesis: fixtureRepairHypothesis(slice) } : {}),
28
+ ...(slice?.work_item_kind ? { work_item_kind: String(slice.work_item_kind) } : {}),
29
+ ...(slice?.tournament_group_id ? { tournament: fixtureTournament(slice) } : {}),
26
30
  source_intelligence_refs: agent.source_intelligence_refs || null,
27
31
  goal_mode_ref: agent.goal_mode_ref || null,
28
32
  ...(opts.emitFollowUpWorkItems ? { follow_up_work_items: [buildFixtureFollowUp(agent, slice)] } : {}),
@@ -94,10 +98,46 @@ export function buildFixturePatchEnvelopes(agent, slice, opts = {}) {
94
98
  path: file,
95
99
  content: `patched by ${String(agent.id)} for ${String(slice?.id || 'fixture')}\n`
96
100
  }],
101
+ ...(isBugfixSlice(slice) ? { regression_proof: fixtureRegressionProof(slice) } : {}),
102
+ ...(isRepairSlice(slice) ? { repair_hypothesis: fixtureRepairHypothesis(slice) } : {}),
103
+ ...(slice?.tournament_group_id ? { tournament: fixtureTournament(slice) } : {}),
97
104
  rationale: 'Fixture patch envelope emitted by fake backend for a leased write task.',
98
105
  verification_hint: { node_id: verificationNodeId, expected_status: 'applied_hashes_recorded' },
99
106
  rollback_hint: { node_id: rollbackNodeId, strategy: 'restore content_before or delete newly created file' }
100
107
  };
101
108
  });
102
109
  }
110
+ function isBugfixSlice(slice) {
111
+ const text = `${String(slice?.work_item_kind || '')} ${String(slice?.title || '')}`.toLowerCase();
112
+ return /\b(bugfix|fix|bug|regression|broken|failure|crash|error)\b|버그|회귀/.test(text);
113
+ }
114
+ function isRepairSlice(slice) {
115
+ const text = `${String(slice?.work_item_kind || '')} ${String(slice?.title || '')}`.toLowerCase();
116
+ return /\b(conflict_resolution|repair|conflict|rebase|rollback)\b|수리|충돌/.test(text);
117
+ }
118
+ function fixtureRegressionProof(slice) {
119
+ return {
120
+ test_file: String(slice?.target_paths?.[0] || slice?.write_paths?.[0] || 'fixture.test.js'),
121
+ failed_before: true,
122
+ passed_after: true,
123
+ output_digest: 'fixture-regression-proof'
124
+ };
125
+ }
126
+ function fixtureRepairHypothesis(slice) {
127
+ return {
128
+ failure: 'fixture repair target',
129
+ hypotheses: [{ cause: 'fixture conflict', evidence_for: 'simulated failed worker', evidence_against: 'none' }],
130
+ chosen: 'fixture conflict',
131
+ minimal_probe: String(slice?.id || 'fixture')
132
+ };
133
+ }
134
+ function fixtureTournament(slice) {
135
+ return {
136
+ schema: 'sks.solution-tournament-candidate.v1',
137
+ group_id: String(slice.tournament_group_id),
138
+ candidate_index: Number(slice.tournament_candidate_index || 1),
139
+ candidate_count: Number(slice.tournament_candidate_count || 1),
140
+ approach: String(slice.approach_directive || '')
141
+ };
142
+ }
103
143
  //# sourceMappingURL=agent-runner-fake.js.map
@@ -27,6 +27,7 @@ export function validateAgentWorkerResult(result) {
27
27
  session_id: String(result?.session_id || 'unknown'),
28
28
  persona_id: String(result?.persona_id || result?.agent_id || 'unknown'),
29
29
  task_slice_id: String(result?.task_slice_id || 'unknown'),
30
+ ...(result?.work_item_kind === undefined ? {} : { work_item_kind: String(result.work_item_kind) }),
30
31
  status: guard.ok ? (result?.status === undefined ? 'done' : String(result.status)) : 'blocked',
31
32
  backend: result?.backend || 'fake',
32
33
  summary: String(result?.summary || ''),
@@ -52,6 +53,10 @@ export function validateAgentWorkerResult(result) {
52
53
  ...(result?.model_authored_patch_envelopes === undefined ? {} : { model_authored_patch_envelopes: Boolean(result.model_authored_patch_envelopes) }),
53
54
  ...(result?.fixture_patch_envelopes === undefined ? {} : { fixture_patch_envelopes: Boolean(result.fixture_patch_envelopes) }),
54
55
  ...(result?.no_patch_reason === undefined ? {} : { no_patch_reason: result.no_patch_reason }),
56
+ ...(result?.machine_feedback === undefined ? {} : { machine_feedback: result.machine_feedback }),
57
+ ...(result?.regression_proof === undefined ? {} : { regression_proof: result.regression_proof }),
58
+ ...(result?.repair_hypothesis === undefined ? {} : { repair_hypothesis: result.repair_hypothesis }),
59
+ ...(result?.tournament === undefined ? {} : { tournament: result.tournament }),
55
60
  ...(result?.source_intelligence_refs === undefined ? {} : { source_intelligence_refs: result.source_intelligence_refs }),
56
61
  ...(result?.goal_mode_ref === undefined ? {} : { goal_mode_ref: result.goal_mode_ref }),
57
62
  ...(result?.follow_up_work_items === undefined ? {} : { follow_up_work_items: followUps.accepted }),
@@ -69,6 +74,12 @@ export function validateAgentWorkerResult(result) {
69
74
  normalized.blockers.push(...patchEnvelopeValidation.blockers.map((issue) => 'patch_envelope_invalid:' + issue));
70
75
  normalized.verification = { status: 'failed', checks: [...normalized.verification.checks, 'agent-patch-envelope-schema'] };
71
76
  }
77
+ const protocolBlockers = qualityProtocolBlockers(normalized);
78
+ if (protocolBlockers.length) {
79
+ normalized.status = 'blocked';
80
+ normalized.blockers.push(...protocolBlockers);
81
+ normalized.verification = { status: 'failed', checks: [...normalized.verification.checks, 'agent-worker-quality-protocol'] };
82
+ }
72
83
  const readOnlyOrNoopWithoutPatch = acceptsNoPatchReadOnlyOrNoop(result?.no_patch_reason);
73
84
  if (patchEnvelopeValidation.envelopes.length === 0 && (normalized.writes.length > 0 || (!readOnlyOrNoopWithoutPatch && normalized.changed_files.length > 0))) {
74
85
  normalized.status = 'blocked';
@@ -83,6 +94,24 @@ export function validateAgentWorkerResult(result) {
83
94
  }
84
95
  return normalized;
85
96
  }
97
+ function qualityProtocolBlockers(result) {
98
+ const writesPatch = result.writes.length > 0 || result.changed_files.length > 0 || Boolean(result.patch_envelopes?.length);
99
+ if (!writesPatch)
100
+ return [];
101
+ const kind = String(result.work_item_kind || result.naruto_runtime?.work_item_kind || '').toLowerCase();
102
+ const text = [kind, result.task_slice_id].join(' ').toLowerCase();
103
+ const proof = result.regression_proof || result.patch_envelopes?.find((envelope) => envelope.regression_proof)?.regression_proof || null;
104
+ const repair = result.repair_hypothesis || result.patch_envelopes?.find((envelope) => envelope.repair_hypothesis)?.repair_hypothesis || null;
105
+ const blockers = [];
106
+ if ((kind === 'bugfix' || /\b(fix|bug|regression|broken|failure|crash|error)\b|버그|회귀/.test(text)) && !validRegressionProof(proof))
107
+ blockers.push('tdd_evidence_missing');
108
+ if ((kind === 'conflict_resolution' || /\b(repair|conflict|rebase|rollback)\b|수리|충돌/.test(text)) && !repair)
109
+ blockers.push('repair_without_hypothesis');
110
+ return blockers;
111
+ }
112
+ function validRegressionProof(proof) {
113
+ return Boolean(proof && proof.failed_before === true && proof.passed_after === true && String(proof.test_file || '').trim());
114
+ }
86
115
  export function agentWorkerPipelineContract() {
87
116
  return {
88
117
  schema: 'sks.agent-worker-pipeline.v1',
@@ -97,7 +126,7 @@ export function agentWorkerPipelineContract() {
97
126
  required_for_write_tasks: true,
98
127
  envelope_schema: 'sks.agent-patch-envelope.v1',
99
128
  metadata: ['agent_id', 'session_id', 'slot_id', 'generation_index', 'lease_id_or_lease_proof'],
100
- optional_hints: ['rationale', 'verification_hint', 'rollback_hint']
129
+ optional_hints: ['rationale', 'verification_hint', 'rollback_hint', 'cochange_acknowledged_reason', 'regression_proof', 'repair_hypothesis']
101
130
  }
102
131
  };
103
132
  }
@@ -111,7 +140,12 @@ function normalizePatchEnvelopes(value) {
111
140
  ...(raw?.generation_index === undefined ? {} : { generation_index: Number(raw.generation_index) }),
112
141
  ...(raw?.task_slice_id === undefined ? {} : { task_slice_id: String(raw.task_slice_id) }),
113
142
  ...(raw?.verification_hint === undefined ? {} : { verification_hint: raw.verification_hint }),
114
- ...(raw?.rollback_hint === undefined ? {} : { rollback_hint: raw.rollback_hint })
143
+ ...(raw?.rollback_hint === undefined ? {} : { rollback_hint: raw.rollback_hint }),
144
+ ...(raw?.cochange_acknowledged === undefined ? {} : { cochange_acknowledged: Boolean(raw.cochange_acknowledged) }),
145
+ ...(raw?.cochange_acknowledged_reason === undefined ? {} : { cochange_acknowledged_reason: String(raw.cochange_acknowledged_reason) }),
146
+ ...(raw?.regression_proof === undefined ? {} : { regression_proof: raw.regression_proof }),
147
+ ...(raw?.repair_hypothesis === undefined ? {} : { repair_hypothesis: raw.repair_hypothesis }),
148
+ ...(raw?.tournament === undefined ? {} : { tournament: raw.tournament })
115
149
  })) : [];
116
150
  const blockers = envelopes.flatMap((envelope, index) => {
117
151
  const validation = validateAgentPatchEnvelope(envelope);
@@ -112,6 +112,8 @@ function buildWorkerPrompt(slice) {
112
112
  `Write-capable slice. write_paths=${JSON.stringify(writePaths)}.`,
113
113
  'Return at least one patch_envelopes item with source "model_authored".',
114
114
  `Use a write operation for ${JSON.stringify(writePaths[0])}, allowed_paths equal to write_paths, lease_proof.protected_path_check "passed", and non-empty verification_hint and rollback_hint.`,
115
+ 'Impact-scan, machine-feedback, diff-quality, and compiled mistake rules run before queue acceptance; exported signature changes must include cochanged callers or a concrete cochange_acknowledged_reason.',
116
+ 'For bugfix work, add a regression test first and include regression_proof with failed_before true and passed_after true. For repair work, include repair_hypothesis before patching.',
115
117
  'For patch envelope runtime ids you cannot know yet, use numeric 0 or empty strings; SKS will bind actual worker and Codex child process ids from the process report.',
116
118
  'Set changed_files and writes consistently with the patch envelope operation.'
117
119
  ].join('\n')
@@ -327,7 +327,6 @@ function startWorkerProgressTelemetry(input) {
327
327
  status: 'running',
328
328
  backend: input.backend,
329
329
  serviceTier: input.serviceTier,
330
- progress: { done: tick, total: 0, label: 'heartbeat' },
331
330
  artifacts: [input.heartbeatRel],
332
331
  logTail: `backend running ${tick}`
333
332
  }).catch(() => undefined);
@@ -10,6 +10,8 @@ import { normalizeAgentPatchEnvelope } from './agent-patch-schema.js';
10
10
  import { runCodexTask } from '../codex-control/codex-control-plane.js';
11
11
  import { CODEX_AGENT_WORKER_RESULT_SCHEMA_ID, codexAgentWorkerResultSchema } from '../codex-control/schemas/agent-worker-result.schema.js';
12
12
  import { leanEngineeringCompactText, leanPolicyReference } from '../lean-engineering-policy.js';
13
+ import { readLbHealth } from '../codex-lb/codex-lb-env.js';
14
+ import { categoryForWorkerRole, modelRouteReason, routeModel } from '../provider/model-router.js';
13
15
  export const NATIVE_WORKER_BACKEND_ROUTER_SCHEMA = 'sks.native-worker-backend-router.v1';
14
16
  export async function runNativeWorkerBackendRouter(input) {
15
17
  const root = path.resolve(input.agentRoot);
@@ -23,6 +25,7 @@ export async function runNativeWorkerBackendRouter(input) {
23
25
  let patchEnvelopes = [];
24
26
  let proofLevel = 'blocked';
25
27
  let outputLastMessagePath = null;
28
+ let modelRouting = null;
26
29
  if (!input.guard?.ok) {
27
30
  result = validateAgentWorkerResult(blockedResult(input, ['native_cli_worker_recursion_guard_missing']));
28
31
  }
@@ -91,6 +94,7 @@ export async function runNativeWorkerBackendRouter(input) {
91
94
  }
92
95
  else if (backend === 'codex-sdk' || backend === 'zellij' || backend === 'local-llm') {
93
96
  const localPreferred = backend === 'local-llm';
97
+ modelRouting = await resolveWorkerModelRouting(input);
94
98
  const sdkTask = await runCodexTask({
95
99
  route: String(input.intake.route || '$Agent'),
96
100
  tier: 'worker',
@@ -101,6 +105,10 @@ export async function runNativeWorkerBackendRouter(input) {
101
105
  sessionId: String(input.agent.session_id || ''),
102
106
  cwd: String(input.intake.cwd || root),
103
107
  prompt: buildWorkerPrompt(input.slice),
108
+ model: modelRouting.choice.model,
109
+ reasoningEffort: modelRouting.choice.reasoning,
110
+ modelReasoningEffort: modelRouting.choice.reasoning,
111
+ serviceTier: modelRouting.choice.serviceTier,
104
112
  inputFiles: input.intake.input_files || [],
105
113
  inputImages: input.intake.input_images || [],
106
114
  outputSchemaId: CODEX_AGENT_WORKER_RESULT_SCHEMA_ID,
@@ -112,6 +120,8 @@ export async function runNativeWorkerBackendRouter(input) {
112
120
  read_only: !hasWriteLease(input.slice, input.intake),
113
121
  allowed_paths: writePaths(input.slice, input.intake),
114
122
  write_paths: writePaths(input.slice, input.intake),
123
+ model_route_reason: modelRouting.reason,
124
+ service_tier: modelRouting.choice.serviceTier,
115
125
  user_confirmed_full_access: false,
116
126
  mad_sks_authorized: input.intake.mad_sks_authorized === true || process.env.SKS_MAD_SKS_ACTIVE === '1'
117
127
  },
@@ -143,6 +153,10 @@ export async function runNativeWorkerBackendRouter(input) {
143
153
  structured_output_valid: sdkTask.structuredOutputValid,
144
154
  worker_result_path: sdkTask.workerResultPath,
145
155
  patch_envelope_path: sdkTask.patchEnvelopePath || null,
156
+ model: modelRouting.choice.model,
157
+ model_reasoning_effort: modelRouting.choice.reasoning,
158
+ service_tier: modelRouting.choice.serviceTier,
159
+ model_route_reason: modelRouting.reason,
146
160
  blockers: sdkTask.blockers
147
161
  };
148
162
  childReports = [sdkReport];
@@ -219,7 +233,11 @@ export async function runNativeWorkerBackendRouter(input) {
219
233
  proof_level: proofLevel,
220
234
  lean_engineering_policy: leanPolicyReference(),
221
235
  fast_mode: input.fastModePolicy.fast_mode,
222
- service_tier: input.fastModePolicy.service_tier,
236
+ service_tier: modelRouting?.choice.serviceTier || input.fastModePolicy.service_tier,
237
+ model: modelRouting?.choice.model || input.agent.model || null,
238
+ model_reasoning_effort: modelRouting?.choice.reasoning || input.agent.model_reasoning_effort || null,
239
+ model_route_reason: modelRouting?.reason || null,
240
+ model_route_category: modelRouting?.category || null,
223
241
  sdk_thread_id: childReports.find((report) => report?.sdk_thread_id)?.sdk_thread_id || null,
224
242
  sdk_run_id: childReports.find((report) => report?.sdk_run_id)?.sdk_run_id || null,
225
243
  stream_event_count: Number(childReports.find((report) => report?.stream_event_count)?.stream_event_count || 0),
@@ -239,6 +257,39 @@ export async function runNativeWorkerBackendRouter(input) {
239
257
  patchEnvelopes
240
258
  };
241
259
  }
260
+ async function resolveWorkerModelRouting(input) {
261
+ const category = categoryForWorkerRole(String(input.agent?.role || input.slice?.role || input.slice?.type || input.agent?.persona_id || 'executor'));
262
+ const lbHealth = await readLbHealth().catch(() => null);
263
+ const explicitModel = String(process.env.SKS_WORKER_MODEL || process.env.SKS_AGENT_MODEL || '').trim();
264
+ const explicitReasoning = normalizeModelReasoning(process.env.SKS_WORKER_REASONING || process.env.SKS_WORKER_MODEL_REASONING || '');
265
+ const explicitTier = normalizeServiceTier(process.env.SKS_WORKER_SERVICE_TIER || process.env.SKS_SERVICE_TIER || '');
266
+ const routed = explicitModel
267
+ ? {
268
+ model: explicitModel,
269
+ reasoning: explicitReasoning || normalizeModelReasoning(input.agent?.model_reasoning_effort) || 'medium',
270
+ serviceTier: explicitTier || input.fastModePolicy.service_tier || 'fast'
271
+ }
272
+ : await routeModel(category, { lbHealth });
273
+ return {
274
+ category,
275
+ choice: routed,
276
+ explicit: Boolean(explicitModel),
277
+ lb_health: lbHealth,
278
+ reason: modelRouteReason(category, routed, {
279
+ explicit: Boolean(explicitModel),
280
+ quotaLow: lbHealth?.quota_low === true,
281
+ degraded: lbHealth?.degraded_models || []
282
+ })
283
+ };
284
+ }
285
+ function normalizeModelReasoning(value) {
286
+ const text = String(value || '').toLowerCase();
287
+ return text === 'low' || text === 'medium' || text === 'high' || text === 'xhigh' ? text : null;
288
+ }
289
+ function normalizeServiceTier(value) {
290
+ const text = String(value || '').toLowerCase();
291
+ return text === 'standard' ? 'standard' : text === 'fast' || text === 'priority' ? 'fast' : null;
292
+ }
242
293
  async function maybeAutoSelectOllamaBackend(backend, input) {
243
294
  if (backend !== 'codex-sdk')
244
295
  return backend;
@@ -286,6 +337,12 @@ function buildWorkerPrompt(slice) {
286
337
  write.length
287
338
  ? `Write-capable slice. Return JSON matching ${CODEX_AGENT_WORKER_RESULT_SCHEMA_ID}; include patch_envelopes for write_paths=${JSON.stringify(write)}.`
288
339
  : `Read-only slice. Return JSON matching ${CODEX_AGENT_WORKER_RESULT_SCHEMA_ID}; do not report pre-existing repository dirtiness as changed_files.`,
340
+ write.length
341
+ ? 'Quality gates run before queue acceptance: impact-scan requires cochanged callers for exported signature changes, machine-feedback runs type/lint/related tests, diff-quality blocks dead exports, and compiled mistake rules block repeated mistakes.'
342
+ : '',
343
+ write.length
344
+ ? 'If this is a bugfix, create a regression test first and include regression_proof with failed_before true and passed_after true. For repair work, include repair_hypothesis before patching.'
345
+ : '',
289
346
  leanEngineeringCompactText(),
290
347
  'Required JSON fields: status, summary, findings, changed_files, patch_envelopes, verification, rollback_notes, blockers.'
291
348
  ].join('\n');