@dzhechkov/harness-core 0.8.35 → 0.8.37

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 (135) hide show
  1. package/.dz-manifest.json +224 -104
  2. package/README.md +335 -10
  3. package/dist/agentdb-index.d.ts +87 -7
  4. package/dist/agentdb-index.d.ts.map +1 -1
  5. package/dist/agentdb-index.js +416 -57
  6. package/dist/agentdb-index.js.map +1 -1
  7. package/dist/apply-leg.d.ts +57 -1
  8. package/dist/apply-leg.d.ts.map +1 -1
  9. package/dist/apply-leg.js +450 -52
  10. package/dist/apply-leg.js.map +1 -1
  11. package/dist/codex-hooks-assets.d.ts.map +1 -1
  12. package/dist/codex-hooks-assets.js +67 -5
  13. package/dist/codex-hooks-assets.js.map +1 -1
  14. package/dist/codex-hooks.d.ts +13 -1
  15. package/dist/codex-hooks.d.ts.map +1 -1
  16. package/dist/codex-hooks.js +13 -1
  17. package/dist/codex-hooks.js.map +1 -1
  18. package/dist/codex-rollouts.d.ts +118 -0
  19. package/dist/codex-rollouts.d.ts.map +1 -0
  20. package/dist/codex-rollouts.js +297 -0
  21. package/dist/codex-rollouts.js.map +1 -0
  22. package/dist/cost-ledger.d.ts +56 -4
  23. package/dist/cost-ledger.d.ts.map +1 -1
  24. package/dist/cost-ledger.js +176 -20
  25. package/dist/cost-ledger.js.map +1 -1
  26. package/dist/cross-family-control.d.ts +345 -0
  27. package/dist/cross-family-control.d.ts.map +1 -0
  28. package/dist/cross-family-control.js +802 -0
  29. package/dist/cross-family-control.js.map +1 -0
  30. package/dist/debt-ratchet.d.ts +53 -0
  31. package/dist/debt-ratchet.d.ts.map +1 -0
  32. package/dist/debt-ratchet.js +107 -0
  33. package/dist/debt-ratchet.js.map +1 -0
  34. package/dist/embedding-config.d.ts +42 -0
  35. package/dist/embedding-config.d.ts.map +1 -1
  36. package/dist/embedding-config.js +106 -10
  37. package/dist/embedding-config.js.map +1 -1
  38. package/dist/feature-adr-checkpoints.d.ts +6 -0
  39. package/dist/feature-adr-checkpoints.d.ts.map +1 -1
  40. package/dist/feature-adr-checkpoints.js +29 -0
  41. package/dist/feature-adr-checkpoints.js.map +1 -1
  42. package/dist/feature-adr-decision-recall.d.ts +2 -2
  43. package/dist/feature-adr-decision-recall.d.ts.map +1 -1
  44. package/dist/feature-adr-decision-recall.js +5 -3
  45. package/dist/feature-adr-decision-recall.js.map +1 -1
  46. package/dist/feature-adr-envelope.d.ts +96 -0
  47. package/dist/feature-adr-envelope.d.ts.map +1 -0
  48. package/dist/feature-adr-envelope.js +183 -0
  49. package/dist/feature-adr-envelope.js.map +1 -0
  50. package/dist/feature-adr-routing.d.ts +64 -0
  51. package/dist/feature-adr-routing.d.ts.map +1 -1
  52. package/dist/feature-adr-routing.js +122 -2
  53. package/dist/feature-adr-routing.js.map +1 -1
  54. package/dist/feature-adr-stage-canon.d.ts +79 -0
  55. package/dist/feature-adr-stage-canon.d.ts.map +1 -0
  56. package/dist/feature-adr-stage-canon.js +117 -0
  57. package/dist/feature-adr-stage-canon.js.map +1 -0
  58. package/dist/index.d.ts +23 -12
  59. package/dist/index.d.ts.map +1 -1
  60. package/dist/index.js +15 -7
  61. package/dist/index.js.map +1 -1
  62. package/dist/loop-blobs.generated.js +4 -4
  63. package/dist/loop-blobs.generated.js.map +1 -1
  64. package/dist/mutation-gate.d.ts +51 -0
  65. package/dist/mutation-gate.d.ts.map +1 -1
  66. package/dist/mutation-gate.js +295 -0
  67. package/dist/mutation-gate.js.map +1 -1
  68. package/dist/operations.d.ts +1 -0
  69. package/dist/operations.d.ts.map +1 -1
  70. package/dist/operations.js +18 -2
  71. package/dist/operations.js.map +1 -1
  72. package/dist/publish.d.ts +59 -7
  73. package/dist/publish.d.ts.map +1 -1
  74. package/dist/publish.js +205 -32
  75. package/dist/publish.js.map +1 -1
  76. package/dist/qe-bridge.d.ts.map +1 -1
  77. package/dist/qe-bridge.js +4 -2
  78. package/dist/qe-bridge.js.map +1 -1
  79. package/dist/qe-findings.d.ts +107 -0
  80. package/dist/qe-findings.d.ts.map +1 -0
  81. package/dist/qe-findings.js +417 -0
  82. package/dist/qe-findings.js.map +1 -0
  83. package/dist/recap.d.ts +1 -1
  84. package/dist/recap.d.ts.map +1 -1
  85. package/dist/recap.js +4 -2
  86. package/dist/recap.js.map +1 -1
  87. package/dist/release-line.d.ts +16 -0
  88. package/dist/release-line.d.ts.map +1 -1
  89. package/dist/release-line.js +31 -0
  90. package/dist/release-line.js.map +1 -1
  91. package/dist/round.d.ts +74 -1
  92. package/dist/round.d.ts.map +1 -1
  93. package/dist/round.js +112 -4
  94. package/dist/round.js.map +1 -1
  95. package/dist/run-records.d.ts +60 -0
  96. package/dist/run-records.d.ts.map +1 -1
  97. package/dist/run-records.js +244 -2
  98. package/dist/run-records.js.map +1 -1
  99. package/dist/score.d.ts +44 -1
  100. package/dist/score.d.ts.map +1 -1
  101. package/dist/score.js +78 -5
  102. package/dist/score.js.map +1 -1
  103. package/dist/vector-tier.d.ts +34 -3
  104. package/dist/vector-tier.d.ts.map +1 -1
  105. package/dist/vector-tier.js +105 -14
  106. package/dist/vector-tier.js.map +1 -1
  107. package/package.json +2 -2
  108. package/sbom.json +403 -103
  109. package/src/agentdb-index.ts +423 -60
  110. package/src/apply-leg.ts +469 -50
  111. package/src/codex-hooks-assets.ts +67 -5
  112. package/src/codex-hooks.ts +13 -1
  113. package/src/codex-rollouts.ts +374 -0
  114. package/src/cost-ledger.ts +232 -24
  115. package/src/cross-family-control.ts +960 -0
  116. package/src/debt-ratchet.ts +143 -0
  117. package/src/embedding-config.ts +131 -10
  118. package/src/feature-adr-checkpoints.ts +29 -0
  119. package/src/feature-adr-decision-recall.ts +6 -4
  120. package/src/feature-adr-envelope.ts +242 -0
  121. package/src/feature-adr-routing.ts +139 -2
  122. package/src/feature-adr-stage-canon.ts +141 -0
  123. package/src/index.ts +66 -7
  124. package/src/loop-blobs.generated.ts +4 -4
  125. package/src/mutation-gate.ts +316 -0
  126. package/src/operations.ts +18 -3
  127. package/src/publish.ts +247 -30
  128. package/src/qe-bridge.ts +4 -2
  129. package/src/qe-findings.ts +463 -0
  130. package/src/recap.ts +10 -3
  131. package/src/release-line.ts +32 -0
  132. package/src/round.ts +165 -6
  133. package/src/run-records.ts +282 -2
  134. package/src/score.ts +115 -6
  135. package/src/vector-tier.ts +127 -14
package/src/index.ts CHANGED
@@ -203,6 +203,9 @@ export {
203
203
  mirrorPatternsToVector,
204
204
  backfillVectorMirror,
205
205
  mergeHybridHits,
206
+ compareHybridHits,
207
+ evidenceRank,
208
+ orderHitsForReRank,
206
209
  recallHybrid,
207
210
  teachGuard,
208
211
  vectorTierStatus,
@@ -224,6 +227,7 @@ export type {
224
227
  HybridRecall,
225
228
  HybridRecallMode,
226
229
  HybridHit,
230
+ HybridOrderKey,
227
231
  RankedPattern,
228
232
  VectorServiceOptions,
229
233
  VectorTierStatus,
@@ -311,10 +315,10 @@ export {
311
315
  segmentRun,
312
316
  } from './eta.js';
313
317
  export type { CheckpointObservation, EtaEstimate, EtaInput, IncompleteCoverageSample, RunSegment, StageDurationSample, StageSample } from './eta.js';
314
- export { indexPatternsToAgentdb, resolveAgentdbPath, searchAgentdbPatterns, listAgentdbDzIds, resolveAgentdbEmbedder, resetAgentdbEmbedderCache, getAgentdbEmbedderCacheStats, cosineSimilarity, importVectorsToAgentdb, reindexAgentdbRows, bumpAgentdbUses, clearAgentdbQuarantine, deleteAgentdbByDzIds, readAgentdbRowsByTaskType, DZ_OWNED_TASK_TYPES, ensureAgentdbSchema, readStoreGeneration, bumpStoreGeneration } from './agentdb-index.js';
318
+ export { indexPatternsToAgentdb, resolveAgentdbPath, searchAgentdbPatterns, listAgentdbDzIds, resolveAgentdbEmbedder, resolveStoreEmbedDtype, resetAgentdbEmbedderCache, getAgentdbEmbedderCacheStats, cosineSimilarity, importVectorsToAgentdb, reindexAgentdbRows, bumpAgentdbUses, clearAgentdbQuarantine, deleteAgentdbByDzIds, readAgentdbRowsByTaskType, DZ_OWNED_TASK_TYPES, ensureAgentdbSchema, readStoreGeneration, bumpStoreGeneration, resolveTransformersModule } from './agentdb-index.js';
315
319
  export type { AgentdbSearchHit, AgentdbSearchResult, AgentdbImportRow } from './agentdb-index.js';
316
- export { DEFAULT_EMBED_MODEL, LEGACY_EMBED_MODEL, DEFAULT_EMBED_DIM, KNOWN_EMBED_DIMS, resolveEmbedModel, readEmbedManifest, writeEmbedManifest, embedManifestPath, legacyEmbedManifest } from './embedding-config.js';
317
- export type { EmbedModelConfig, EmbedModelSource, EmbedManifest } from './embedding-config.js';
320
+ export { DEFAULT_EMBED_MODEL, LEGACY_EMBED_MODEL, DEFAULT_EMBED_DIM, KNOWN_EMBED_DIMS, KNOWN_EMBED_DTYPES, resolveEmbedModel, readEmbedManifest, writeEmbedManifest, embedManifestPath, legacyEmbedManifest, currentEmbedManifest, guardEmbedSpace, snapshotEmbedManifest } from './embedding-config.js';
321
+ export type { EmbedModelConfig, EmbedModelSource, EmbedManifest, EmbedDtype } from './embedding-config.js';
318
322
  export { putBookKnowledge, queryBookKnowledge, bookKbPath } from './book-kb.js';
319
323
  export type { BookKU, BookKUHit } from './book-kb.js';
320
324
  export { applyReadonlyPragmas, classifySqliteReadFailure, warnOnce } from './sqlite-read-helpers.js';
@@ -500,10 +504,30 @@ export {
500
504
  decideRecordWrite,
501
505
  decideReadBack,
502
506
  recordVerdictLine,
507
+ parseModelSpec,
503
508
  } from './run-records.js';
509
+ export {
510
+ ENVELOPE_SCHEMA,
511
+ TASK_KINDS,
512
+ PRIORITIES as ENVELOPE_PRIORITIES,
513
+ TIERS as ENVELOPE_TIERS,
514
+ buildExperimentEnvelope,
515
+ validateExperimentEnvelope,
516
+ } from './feature-adr-envelope.js';
517
+ export type {
518
+ TaskKind,
519
+ EnvelopePriority,
520
+ EnvelopeTier,
521
+ ExperimentEnvelope,
522
+ ExperimentEnvelopeArms,
523
+ ExperimentEnvelopeChosen,
524
+ ExperimentEnvelopePolicy,
525
+ ExperimentEnvelopeEvaluator,
526
+ BuildExperimentEnvelopeInput,
527
+ } from './feature-adr-envelope.js';
504
528
  export { decidePublishSigning, decidePostSigningVerification, decideSignableSet, publishSigningLine, signableSetLine } from './publish-signing.js';
505
529
  export type { PublishSigningVerdict, PublishSigningDecision, SignableSetDecision } from './publish-signing.js';
506
- export type { RecordKind, RecordVerdict, RecordDecision } from './run-records.js';
530
+ export type { RecordKind, RecordVerdict, RecordDecision, LedgerEnrichInput, LedgerPriceEntry, ParsedModelSpec } from './run-records.js';
507
531
  export type { AmendmentAmbiguity, AmendmentRow, AmendmentVerdict, AmendmentResolution, AmendmentOutcome, AmendmentDecision, PlanCoverageGap } from './amendment-trace.js';
508
532
  // contract-checklist (ADR-001): pure extraction, canonical rendering, typed report parsing, and
509
533
  // exact per-item verification. Filesystem discovery/containment stays in harness-cli.
@@ -730,7 +754,8 @@ export type {
730
754
  ChainDefectAges,
731
755
  ChainedJournal,
732
756
  } from './event-chain.js';
733
- export { decideProvenance, environmentCanMintProvenance, publishArgv, discoverPackages, publishPackages, bumpPatch, compareVersions, findUnpackagedSkills, findUnpublishedWorkspaceFloors, rewriteWorkspaceSpecs, orderByDependencies, syncReadmeVersion, isChangelogEntryLine, changelogRegion } from './publish.js';
757
+ export { decideProvenance, environmentCanMintProvenance, publishArgv, discoverPackages, publishPackages, bumpPatch, compareVersions, findUnpackagedSkills, findUnpublishedWorkspaceFloors, rewriteWorkspaceSpecs, orderByDependencies, syncReadmeVersion, isChangelogEntryLine, changelogRegion, planReadmeVersionSync } from './publish.js';
758
+ export type { ReadmeVersionSyncPlan, ReadmeSyncRewrite } from './publish.js';
734
759
  export { RELEASE_LINE_RE, findReleaseLine, rewriteReleaseLine } from './release-line.js';
735
760
  export * from './course-staleness.js';
736
761
  export { fetchAllDownloads } from './downloads.js';
@@ -906,6 +931,14 @@ export {
906
931
  // p16-non-js-portability: the gate-script search chain's operator note (ADR-002/AM-7) and the
907
932
  // dzBin absolutization (ADR-003). Named for the same reason as the three above.
908
933
  refusalNoteFor,
934
+ shellQuote,
935
+ planBackupCmd,
936
+ planRestoreCmd,
937
+ planArchiveBackupCmd,
938
+ planSnapshotCmd,
939
+ snapshotBlock,
940
+ snapshotNumber,
941
+ parsePlanSnapshot,
909
942
  normalizeDzBin,
910
943
  // qe-bridge-claude: the bridge's path/slug hygiene reuses these rather than minting a second
911
944
  // definition of "safe" (ADR-001 D5-A).
@@ -973,6 +1006,7 @@ export type {
973
1006
  ParsedBaselineCapture,
974
1007
  ParsedLandingSignal,
975
1008
  PlanGateVerdict,
1009
+ PlanSnapshot,
976
1010
  PlanGateCmdOpts,
977
1011
  CodexReviewCommandInput,
978
1012
  CodexReviewCommandResult,
@@ -1049,6 +1083,25 @@ export type {
1049
1083
  WorkflowRunRecord,
1050
1084
  WorkflowStageEntry,
1051
1085
  } from './cost-ledger.js';
1086
+ // Canonical stage taxonomy (feature measurement-integrity, ADR-001 D1).
1087
+ export { CANONICAL_STAGES, STAGE_LABEL_RULES, canonicalStage } from './feature-adr-stage-canon.js';
1088
+ export type {
1089
+ CanonicalStage,
1090
+ KnownStageResult,
1091
+ StageCanonResult,
1092
+ StageLabelRule,
1093
+ UnknownStageResult,
1094
+ } from './feature-adr-stage-canon.js';
1095
+ // Codex rollout-log reader (feature measurement-integrity, ADR-001 D3).
1096
+ export { matchCodexRollouts, parseCodexRollout } from './codex-rollouts.js';
1097
+ export type {
1098
+ CodexRollout,
1099
+ CodexRolloutMatch,
1100
+ CodexRolloutMatchWindow,
1101
+ CodexRolloutParseError,
1102
+ CodexRolloutTotals,
1103
+ CodexRolloutTurn,
1104
+ } from './codex-rollouts.js';
1052
1105
  export type {
1053
1106
  ClaudeUsageModel,
1054
1107
  UsageCalibrationChange,
@@ -1149,6 +1202,7 @@ export {
1149
1202
  // Run-process scorecard (feature dz-score, Reading C) — scores the DISCIPLINE of a feature-adr run
1150
1203
  // from its artifacts and folds immutable receipts into a chained aggregate. Descriptive-only,
1151
1204
  // permanently: neither the single-run score nor the aggregate gates.
1205
+ export * from './qe-findings.js';
1152
1206
  export * from './score.js';
1153
1207
  export * from './recap.js';
1154
1208
  export * from './provenance.js';
@@ -1263,8 +1317,13 @@ export * from './run-registry.js';
1263
1317
 
1264
1318
  export { JOURNAL_KINDS, formatLine, parseLine, selectWindow, appendWitnessed } from './journal.js';
1265
1319
  export type { JournalKind, JournalEvent, JournalLine, JournalIo } from './journal.js';
1266
- export { openRound, closeRound, listRounds } from './round.js';
1267
- export type { RoundState, RoundExecState, RoundLedgerRow } from './round.js';
1320
+ export { openRound, closeRound, listRounds, validateClosedRoundLedgerRow } from './round.js';
1321
+ export type { RoundState, RoundExecState, RoundLedgerRow, RoundReviewSidecar } from './round.js';
1268
1322
  export { parseCodexTokens, classifyRoundExecOutcome, buildRoundExecRow } from './round-exec.js';
1269
1323
  export type { RoundExecOutcome, RoundExecLedgerRow } from './round-exec.js';
1270
1324
  export * from './run-cleanup.js';
1325
+
1326
+ export * from './cross-family-control.js';
1327
+
1328
+ export { debtRatchetVerdict, parsePinnedCeiling, ceilingUnreadableMessage } from './debt-ratchet.js';
1329
+ export type { DebtRatchetArgs, DebtRatchetVerdict, PinnedCeiling } from './debt-ratchet.js';
@@ -61,11 +61,11 @@ export const BLOBS: Record<string, LoopBlob> = {
61
61
  "training-pairs": {
62
62
  name: "training-pairs",
63
63
  version: "1.2.0",
64
- contentHash: "7c836995b72f0b8fc69074cf6bb7b8a58fac609e4f5c365c82b8745c655da39c",
64
+ contentHash: "e80c53755e8c2b0e08d3dabcd49cdfffb2283a5b3f650c06b76196936979caf6",
65
65
  sourcePath: "packages/@dzhechkov/harness-core/src/feature-adr-checkpoints.ts",
66
66
  requires: ["checkpoints"],
67
67
  exports: ["TRAINPAIR_SCHEMA_VERSION","TRAINPAIR_MAX_IO_CHARS","trainingPairFamily","trainingPairPath","TRAINPAIR_PRIVACY_NOTE","buildTrainingPair","serializeTrainingPair","trainingPairAppendCmd","decideCaptureMode","captureFailureRecord","trainingPairBackfillCmd","TP_BACKFILL_OK","TP_BACKFILL_SKIP"],
68
- code: "function decideCaptureMode(opts) {\n if (!opts.enabled)\n return 'skip-disabled';\n if (!Number.isInteger(opts.recordCount) || opts.recordCount <= 0)\n return 'skip-empty';\n return opts.resumed ? 'backfill' : 'capture';\n}\nfunction captureFailureRecord(stage, mode, reason, detail) {\n const normalizedStage = typeof stage === 'string' && stage.trim() !== '' ? stage : 'unknown';\n const normalizedMode = mode === 'capture' || mode === 'backfill' || mode === 'skip-disabled' || mode === 'skip-empty'\n ? mode\n : null;\n const normalizedReason = reason === 'threw' || reason === 'unserializable' || reason === 'unverified' || reason === 'backfill-unverified' || reason === 'empty-output'\n ? reason\n : 'threw';\n let normalizedDetail = null;\n if (detail !== null && detail !== undefined) {\n try {\n const text = String(detail);\n if (text !== '')\n normalizedDetail = text.length > 500 ? text.slice(0, 500) + '…' : text;\n }\n catch {\n normalizedDetail = null;\n }\n }\n return { stage: normalizedStage, mode: normalizedMode, reason: normalizedReason, detail: normalizedDetail };\n}\nconst TRAINPAIR_SCHEMA_VERSION = 'fa-trainpair-3';\nconst TRAINPAIR_MAX_IO_CHARS = 48000;\nfunction trainingPairFamily(spec) {\n return /codex|gpt|openai/i.test(String(spec ?? '')) ? 'codex' : 'claude';\n}\nfunction trainingPairPath(slug, stage) {\n return '.dz/fa-training/' + slug + '/' + stage + '.jsonl';\n}\nconst TRAINPAIR_PRIVACY_NOTE = \"feature-adr TRAINING PAIRS (backlog 70e0f083): per-stage SFT records - STAGE INPUT (full prompt/context) -> STAGE OUTPUT (artifact/result) -> EVALUATION (QE grade + injected lessons) with model+family provenance; one JSONL file per stage per slug. PRIVACY: pairs may contain TARGET-REPO CODE and full prompts. This directory is NOT gitignored yet by explicit owner decision - review contents before sharing or publishing anything that embeds it. ts is the CAPTURE time. On a record with captureMode: 'backfill' that is the RECONSTRUCTION time, NOT the stage's observation time — the original stage's timing lives in that run's .fa-state checkpoint.\";\nconst TP_PROFILE_MARKER_START = '<!-- dz:profile:start -->';\nconst TP_PROFILE_MARKER_END = '<!-- dz:profile:end -->';\nconst TP_PROFILE_REDACTED = '[dz:profile REDACTED]';\nfunction redactProfileBlock(text) {\n if (typeof text !== 'string' || text === '')\n return typeof text === 'string' ? text : '';\n let out = '';\n let rest = text;\n for (;;) {\n const start = rest.indexOf(TP_PROFILE_MARKER_START);\n if (start === -1)\n return out + rest;\n out += rest.slice(0, start) + TP_PROFILE_REDACTED;\n const end = rest.indexOf(TP_PROFILE_MARKER_END, start + TP_PROFILE_MARKER_START.length);\n if (end === -1)\n return out;\n rest = rest.slice(end + TP_PROFILE_MARKER_END.length);\n }\n}\nfunction coerceText(v) {\n if (typeof v === 'string')\n return v;\n if (v === null || v === undefined)\n return '';\n try {\n const s = JSON.stringify(v);\n return typeof s === 'string' ? s : String(v);\n }\n catch {\n return String(v);\n }\n}\nfunction normalizeTrainingPairBudget(raw) {\n try {\n if (raw === undefined)\n return { primary: 'claude', claude: 'normal', codex: 'normal', preset: 'unset' };\n if (raw === null || typeof raw !== 'object')\n return null;\n const value = raw;\n const primary = value.primary;\n const claude = value.claude;\n const codex = value.codex;\n if (primary !== 'claude' && primary !== 'codex')\n return null;\n if (claude !== 'normal' && claude !== 'eco')\n return null;\n if (codex !== 'normal' && codex !== 'eco')\n return null;\n if (value.preset === 'unset')\n return { primary, claude, codex, preset: 'unset' };\n let preset = 'custom';\n if (claude === 'normal' && codex === 'normal')\n preset = 'normal';\n else if (claude === 'eco' && codex === 'eco')\n preset = 'eco';\n else if (claude === 'eco' && codex === 'normal')\n preset = 'hybrid';\n return { primary, claude, codex, preset };\n }\n catch {\n return null;\n }\n}\nfunction buildTrainingPair(opts) {\n let input = redactProfileBlock(coerceText(opts.input));\n let output = redactProfileBlock(coerceText(opts.output));\n let truncated = null;\n if (input.length + output.length > TRAINPAIR_MAX_IO_CHARS) {\n truncated = { inputChars: input.length, outputChars: output.length, inputHash: fnv1a64(input), outputHash: fnv1a64(output) };\n const half = Math.floor(TRAINPAIR_MAX_IO_CHARS / 2);\n let inKeep = input.length;\n let outKeep = output.length;\n if (outKeep <= half)\n inKeep = TRAINPAIR_MAX_IO_CHARS - outKeep;\n else if (inKeep <= half)\n outKeep = TRAINPAIR_MAX_IO_CHARS - inKeep;\n else {\n inKeep = half;\n outKeep = TRAINPAIR_MAX_IO_CHARS - half;\n }\n if (inKeep < input.length)\n input = input.slice(0, inKeep) + '\\n…[TRUNCATED ' + (truncated.inputChars - inKeep) + ' chars — full-text fnv1a64=' + truncated.inputHash + ']';\n if (outKeep < output.length)\n output = output.slice(0, outKeep) + '\\n…[TRUNCATED ' + (truncated.outputChars - outKeep) + ' chars — full-text fnv1a64=' + truncated.outputHash + ']';\n }\n const ev = opts.evaluation || {};\n const pv = opts.provenance || {};\n return {\n schema: TRAINPAIR_SCHEMA_VERSION,\n slug: opts.slug,\n stage: opts.stage,\n ts: opts.ts === undefined ? null : opts.ts,\n input,\n output,\n evaluation: {\n grade: typeof ev.grade === 'string' && ev.grade.trim() !== '' ? ev.grade : null,\n gradedBy: typeof ev.gradedBy === 'string' && ev.gradedBy !== '' ? ev.gradedBy : null,\n lessonsInjected: Array.isArray(ev.lessonsInjected) ? ev.lessonsInjected.filter((s) => typeof s === 'string' && s !== '') : [],\n },\n provenance: {\n model: typeof pv.model === 'string' && pv.model !== '' ? pv.model : 'unknown',\n family: pv.family === 'claude' || pv.family === 'codex' ? pv.family : trainingPairFamily(pv.model),\n role: typeof pv.role === 'string' && pv.role !== '' ? pv.role : 'unknown',\n tokens: typeof pv.tokens === 'number' && Number.isFinite(pv.tokens) ? pv.tokens : null,\n minutes: typeof pv.minutes === 'number' && Number.isFinite(pv.minutes) ? pv.minutes : null,\n },\n budgetMode: normalizeTrainingPairBudget(opts.budgetMode),\n truncated,\n captureMode: opts.captureMode === 'backfill' ? 'backfill' : 'capture',\n resumed: opts.resumed === true,\n };\n}\nfunction serializeTrainingPair(pair) {\n try {\n const line = JSON.stringify(pair);\n return typeof line === 'string' ? line : null;\n }\n catch {\n return null;\n }\n}\nfunction trainingPairAppendCmd(repoAbs, slug, stage, line) {\n const dirAbs = repoAbs + '/.dz/fa-training/' + slug;\n const readmeAbs = repoAbs + '/.dz/fa-training/README.md';\n const fileAbs = dirAbs + '/' + stage + '.jsonl';\n return ('mkdir -p ' + shellQuote(dirAbs) +\n ' && { [ -f ' + shellQuote(readmeAbs) + ' ] || printf \\'%s\\\\n\\' ' + shellQuote(TRAINPAIR_PRIVACY_NOTE) + ' > ' + shellQuote(readmeAbs) + '; }' +\n \" && printf '%s\\\\n' \" + shellQuote(line) + ' >> ' + shellQuote(fileAbs));\n}\nconst TP_BACKFILL_OK = 'TP-BACKFILL-OK';\nconst TP_BACKFILL_SKIP = 'TP-BACKFILL-SKIP';\nconst TP_BACKFILL_DUP = 'TP-BACKFILL-DUP';\nfunction trainingPairBackfillCmd(repoAbs, slug, stage, lines, markKey) {\n if (typeof repoAbs !== 'string' || repoAbs === '')\n return null;\n if (typeof slug !== 'string' || slug === '')\n return null;\n if (typeof stage !== 'string' || stage === '')\n return null;\n if (!Array.isArray(lines) || lines.length === 0 || !lines.every(line => typeof line === 'string' && line !== ''))\n return null;\n const dirAbs = repoAbs + '/.dz/fa-training/' + slug;\n const readmeAbs = repoAbs + '/.dz/fa-training/README.md';\n const fileAbs = dirAbs + '/' + stage + '.jsonl';\n const markDir = repoAbs + '/.dz/fa-training/.backfill-marks';\n const markStage = stage.replace(/\\.\\./g, '_').replace(/\\//g, '_');\n const resolvedMarkKey = markKey === undefined ? fnv1a64(stage + '\\0' + lines.join('\\n')) : markKey;\n const markPath = markDir + '/' + markStage + '-' + resolvedMarkKey;\n const appends = lines\n .map(line => \"printf '%s\\\\n' \" + shellQuote(line) + ' >> ' + shellQuote(fileAbs))\n .join(' && ');\n return ('mkdir -p ' + shellQuote(dirAbs) +\n ' && { [ -f ' + shellQuote(readmeAbs) + ' ] || printf \\'%s\\\\n\\' ' + shellQuote(TRAINPAIR_PRIVACY_NOTE) + ' > ' + shellQuote(readmeAbs) + '; }' +\n ' && mkdir -p ' + shellQuote(markDir) +\n ' && if mkdir ' + shellQuote(markPath) + ' 2>/dev/null; then ' +\n 'if [ -f ' + shellQuote(fileAbs) + ' ]; then echo ' + shellQuote(TP_BACKFILL_SKIP) +\n '; else { ' + appends + ' && echo ' + shellQuote(TP_BACKFILL_OK) + '; } || { rmdir ' + shellQuote(markPath) + ' 2>/dev/null; false; }; fi' +\n '; else echo ' + shellQuote(TP_BACKFILL_DUP) + '; fi');\n}",
68
+ code: "function decideCaptureMode(opts) {\n if (!opts.enabled)\n return 'skip-disabled';\n if (!Number.isInteger(opts.recordCount) || opts.recordCount <= 0)\n return 'skip-empty';\n return opts.resumed ? 'backfill' : 'capture';\n}\nfunction captureFailureRecord(stage, mode, reason, detail) {\n const normalizedStage = typeof stage === 'string' && stage.trim() !== '' ? stage : 'unknown';\n const normalizedMode = mode === 'capture' || mode === 'backfill' || mode === 'skip-disabled' || mode === 'skip-empty'\n ? mode\n : null;\n const normalizedReason = reason === 'threw' || reason === 'unserializable' || reason === 'unverified' || reason === 'backfill-unverified' || reason === 'empty-output'\n ? reason\n : 'threw';\n let normalizedDetail = null;\n if (detail !== null && detail !== undefined) {\n try {\n const text = String(detail);\n if (text !== '')\n normalizedDetail = text.length > 500 ? text.slice(0, 500) + '…' : text;\n }\n catch {\n normalizedDetail = null;\n }\n }\n return { stage: normalizedStage, mode: normalizedMode, reason: normalizedReason, detail: normalizedDetail };\n}\nconst TRAINPAIR_SCHEMA_VERSION = 'fa-trainpair-3';\nconst TRAINPAIR_MAX_IO_CHARS = 48000;\nfunction trainingPairFamily(spec) {\n return /codex|gpt|openai/i.test(String(spec ?? '')) ? 'codex' : 'claude';\n}\nfunction trainingPairPath(slug, stage) {\n return '.dz/fa-training/' + slug + '/' + stage + '.jsonl';\n}\nconst TRAINPAIR_PRIVACY_NOTE = \"feature-adr TRAINING PAIRS (backlog 70e0f083): per-stage SFT records - STAGE INPUT (full prompt/context) -> STAGE OUTPUT (artifact/result) -> EVALUATION (QE grade + injected lessons) with model+family provenance; one JSONL file per stage per slug. PRIVACY: pairs may contain TARGET-REPO CODE and full prompts. This directory is NOT gitignored yet by explicit owner decision - review contents before sharing or publishing anything that embeds it. ts is the CAPTURE time. On a record with captureMode: 'backfill' that is the RECONSTRUCTION time, NOT the stage's observation time — the original stage's timing lives in that run's .fa-state checkpoint.\";\nconst TP_PROFILE_MARKER_START = '<!-- dz:profile:start -->';\nconst TP_PROFILE_MARKER_END = '<!-- dz:profile:end -->';\nconst TP_PROFILE_REDACTED = '[dz:profile REDACTED]';\nfunction redactProfileBlock(text) {\n if (typeof text !== 'string' || text === '')\n return typeof text === 'string' ? text : '';\n let out = '';\n let rest = text;\n for (;;) {\n const start = rest.indexOf(TP_PROFILE_MARKER_START);\n if (start === -1)\n return out + rest;\n out += rest.slice(0, start) + TP_PROFILE_REDACTED;\n const end = rest.indexOf(TP_PROFILE_MARKER_END, start + TP_PROFILE_MARKER_START.length);\n if (end === -1)\n return out;\n rest = rest.slice(end + TP_PROFILE_MARKER_END.length);\n }\n}\nfunction coerceText(v) {\n if (typeof v === 'string')\n return v;\n if (v === null || v === undefined)\n return '';\n try {\n const s = JSON.stringify(v);\n return typeof s === 'string' ? s : String(v);\n }\n catch {\n return String(v);\n }\n}\nfunction normalizeTrainingPairBudget(raw) {\n try {\n if (raw === undefined)\n return { primary: 'claude', claude: 'normal', codex: 'normal', preset: 'unset' };\n if (raw === null || typeof raw !== 'object')\n return null;\n const value = raw;\n const primary = value.primary;\n const claude = value.claude;\n const codex = value.codex;\n if (primary !== 'claude' && primary !== 'codex')\n return null;\n if (claude !== 'normal' && claude !== 'eco')\n return null;\n if (codex !== 'normal' && codex !== 'eco')\n return null;\n if (value.preset === 'unset')\n return { primary, claude, codex, preset: 'unset' };\n let preset = 'custom';\n if (claude === 'normal' && codex === 'normal')\n preset = 'normal';\n else if (claude === 'eco' && codex === 'eco')\n preset = 'eco';\n else if (claude === 'eco' && codex === 'normal')\n preset = 'hybrid';\n return { primary, claude, codex, preset };\n }\n catch {\n return null;\n }\n}\nfunction normalizeTrainingPairEnvelope(raw) {\n if (raw === null || raw === undefined || typeof raw !== 'object' || Array.isArray(raw))\n return null;\n const v = raw;\n if (v.schema !== 1)\n return null;\n if (typeof v.runId !== 'string' || v.runId.trim() === '')\n return null;\n if (v.arms === null || typeof v.arms !== 'object')\n return null;\n if (v.chosen === null || typeof v.chosen !== 'object')\n return null;\n if (v.policy === null || typeof v.policy !== 'object')\n return null;\n if (v.evaluator === null || typeof v.evaluator !== 'object')\n return null;\n return raw;\n}\nfunction buildTrainingPair(opts) {\n let input = redactProfileBlock(coerceText(opts.input));\n let output = redactProfileBlock(coerceText(opts.output));\n let truncated = null;\n if (input.length + output.length > TRAINPAIR_MAX_IO_CHARS) {\n truncated = { inputChars: input.length, outputChars: output.length, inputHash: fnv1a64(input), outputHash: fnv1a64(output) };\n const half = Math.floor(TRAINPAIR_MAX_IO_CHARS / 2);\n let inKeep = input.length;\n let outKeep = output.length;\n if (outKeep <= half)\n inKeep = TRAINPAIR_MAX_IO_CHARS - outKeep;\n else if (inKeep <= half)\n outKeep = TRAINPAIR_MAX_IO_CHARS - inKeep;\n else {\n inKeep = half;\n outKeep = TRAINPAIR_MAX_IO_CHARS - half;\n }\n if (inKeep < input.length)\n input = input.slice(0, inKeep) + '\\n…[TRUNCATED ' + (truncated.inputChars - inKeep) + ' chars — full-text fnv1a64=' + truncated.inputHash + ']';\n if (outKeep < output.length)\n output = output.slice(0, outKeep) + '\\n…[TRUNCATED ' + (truncated.outputChars - outKeep) + ' chars — full-text fnv1a64=' + truncated.outputHash + ']';\n }\n const ev = opts.evaluation || {};\n const pv = opts.provenance || {};\n return {\n schema: TRAINPAIR_SCHEMA_VERSION,\n slug: opts.slug,\n stage: opts.stage,\n ts: opts.ts === undefined ? null : opts.ts,\n input,\n output,\n evaluation: {\n grade: typeof ev.grade === 'string' && ev.grade.trim() !== '' ? ev.grade : null,\n gradedBy: typeof ev.gradedBy === 'string' && ev.gradedBy !== '' ? ev.gradedBy : null,\n lessonsInjected: Array.isArray(ev.lessonsInjected) ? ev.lessonsInjected.filter((s) => typeof s === 'string' && s !== '') : [],\n },\n provenance: {\n model: typeof pv.model === 'string' && pv.model !== '' ? pv.model : 'unknown',\n family: pv.family === 'claude' || pv.family === 'codex' ? pv.family : trainingPairFamily(pv.model),\n role: typeof pv.role === 'string' && pv.role !== '' ? pv.role : 'unknown',\n tokens: typeof pv.tokens === 'number' && Number.isFinite(pv.tokens) ? pv.tokens : null,\n minutes: typeof pv.minutes === 'number' && Number.isFinite(pv.minutes) ? pv.minutes : null,\n },\n budgetMode: normalizeTrainingPairBudget(opts.budgetMode),\n truncated,\n captureMode: opts.captureMode === 'backfill' ? 'backfill' : 'capture',\n resumed: opts.resumed === true,\n envelope: normalizeTrainingPairEnvelope(opts.envelope),\n };\n}\nfunction serializeTrainingPair(pair) {\n try {\n const line = JSON.stringify(pair);\n return typeof line === 'string' ? line : null;\n }\n catch {\n return null;\n }\n}\nfunction trainingPairAppendCmd(repoAbs, slug, stage, line) {\n const dirAbs = repoAbs + '/.dz/fa-training/' + slug;\n const readmeAbs = repoAbs + '/.dz/fa-training/README.md';\n const fileAbs = dirAbs + '/' + stage + '.jsonl';\n return ('mkdir -p ' + shellQuote(dirAbs) +\n ' && { [ -f ' + shellQuote(readmeAbs) + ' ] || printf \\'%s\\\\n\\' ' + shellQuote(TRAINPAIR_PRIVACY_NOTE) + ' > ' + shellQuote(readmeAbs) + '; }' +\n \" && printf '%s\\\\n' \" + shellQuote(line) + ' >> ' + shellQuote(fileAbs));\n}\nconst TP_BACKFILL_OK = 'TP-BACKFILL-OK';\nconst TP_BACKFILL_SKIP = 'TP-BACKFILL-SKIP';\nconst TP_BACKFILL_DUP = 'TP-BACKFILL-DUP';\nfunction trainingPairBackfillCmd(repoAbs, slug, stage, lines, markKey) {\n if (typeof repoAbs !== 'string' || repoAbs === '')\n return null;\n if (typeof slug !== 'string' || slug === '')\n return null;\n if (typeof stage !== 'string' || stage === '')\n return null;\n if (!Array.isArray(lines) || lines.length === 0 || !lines.every(line => typeof line === 'string' && line !== ''))\n return null;\n const dirAbs = repoAbs + '/.dz/fa-training/' + slug;\n const readmeAbs = repoAbs + '/.dz/fa-training/README.md';\n const fileAbs = dirAbs + '/' + stage + '.jsonl';\n const markDir = repoAbs + '/.dz/fa-training/.backfill-marks';\n const markStage = stage.replace(/\\.\\./g, '_').replace(/\\//g, '_');\n const resolvedMarkKey = markKey === undefined ? fnv1a64(stage + '\\0' + lines.join('\\n')) : markKey;\n const markPath = markDir + '/' + markStage + '-' + resolvedMarkKey;\n const appends = lines\n .map(line => \"printf '%s\\\\n' \" + shellQuote(line) + ' >> ' + shellQuote(fileAbs))\n .join(' && ');\n return ('mkdir -p ' + shellQuote(dirAbs) +\n ' && { [ -f ' + shellQuote(readmeAbs) + ' ] || printf \\'%s\\\\n\\' ' + shellQuote(TRAINPAIR_PRIVACY_NOTE) + ' > ' + shellQuote(readmeAbs) + '; }' +\n ' && mkdir -p ' + shellQuote(markDir) +\n ' && if mkdir ' + shellQuote(markPath) + ' 2>/dev/null; then ' +\n 'if [ -f ' + shellQuote(fileAbs) + ' ]; then echo ' + shellQuote(TP_BACKFILL_SKIP) +\n '; else { ' + appends + ' && echo ' + shellQuote(TP_BACKFILL_OK) + '; } || { rmdir ' + shellQuote(markPath) + ' 2>/dev/null; false; }; fi' +\n '; else echo ' + shellQuote(TP_BACKFILL_DUP) + '; fi');\n}",
69
69
  },
70
70
  "model-resolver": {
71
71
  name: "model-resolver",
@@ -88,11 +88,11 @@ export const BLOBS: Record<string, LoopBlob> = {
88
88
  "codex-dispatch": {
89
89
  name: "codex-dispatch",
90
90
  version: "1.0.0",
91
- contentHash: "f203268da376fa993ce61d38b8fc6a24db4ac1e3edb6b90662f86ae4351a30e7",
91
+ contentHash: "dc50f313088cfcea9ec3c95051f73ba6311a8d4d9f962d638901e6f0187bdd32",
92
92
  sourcePath: "packages/@dzhechkov/harness-core/src/feature-adr-routing.ts",
93
93
  requires: [],
94
94
  exports: ["codexDispatchMode","codexExecPlan","needsCodeLandedBarrier","decideCodeLanding"],
95
- code: "const CODE_LANDING_PIPELINE_PREFIXES = ['features/', '.dz/', '.agentic-qe/', 'roam/'];\nfunction codeLandingEmptySignal(seconds) {\n return 'changed=0 after ' + seconds + 's — genuinely not landed';\n}\nfunction needsCodeLandedBarrier(coderUsed) {\n return coderUsed === 'codex' || coderUsed === 'codex-fallback';\n}\nfunction stripCodeLandingPath(path) {\n let p = String(path || '').trim().replace(/\\\\/g, '/');\n while (p.indexOf('./') === 0)\n p = p.slice(2);\n return p.replace(/\\/+/g, '/');\n}\nfunction classifyCodeLandingPathReject(path) {\n const p = stripCodeLandingPath(path);\n if (!p)\n return 'empty-after-strip';\n if (p[0] === '/')\n return 'absolute-path';\n if (p === '..' || p.indexOf('../') === 0 || p.indexOf('/../') >= 0 || p.endsWith('/..'))\n return 'traversal';\n if (/[\\0\\r\\n\\t \"'\\x60$;&|<>*?()[\\]{}!]/.test(p))\n return 'not-a-path';\n if (p.endsWith('/'))\n return 'not-a-path';\n for (const prefix of CODE_LANDING_PIPELINE_PREFIXES) {\n const bare = prefix.slice(0, -1);\n if (p === bare || p.indexOf(prefix) === 0)\n return 'pipeline-artifact-path';\n }\n return null;\n}\nfunction normalizeCodeLandingPath(path) {\n return classifyCodeLandingPathReject(path) === null ? stripCodeLandingPath(path) : '';\n}\nfunction filterPollableCodePaths(paths) {\n const out = [];\n const seen = new Set();\n for (const path of paths || []) {\n const normalized = normalizeCodeLandingPath(path);\n if (!normalized || seen.has(normalized))\n continue;\n seen.add(normalized);\n out.push(normalized);\n }\n return out;\n}\nfunction isNewlyChanged(path, baseline, currentHashes) {\n if (!baseline || !baseline.ok)\n return false;\n let recorded = null;\n for (const entry of baseline.entries) {\n if (entry.path === path) {\n recorded = entry.hash;\n break;\n }\n }\n if (recorded === null)\n return true;\n const now = currentHashes ? currentHashes[path] : undefined;\n if (now === undefined)\n return false;\n return now !== recorded;\n}\nfunction decideCodeLanding(snapshot) {\n const maxWaitMs = Math.max(0, snapshot.maxWaitMs);\n const elapsedMs = Math.max(0, snapshot.elapsedMs);\n const elapsedSeconds = Math.floor(elapsedMs / 1000);\n const expectedPaths = filterPollableCodePaths(snapshot.expectedPaths);\n const changedPaths = filterPollableCodePaths(snapshot.changedEntries.map(function (entry) { return entry.path; }));\n if (expectedPaths.length === 0) {\n return {\n status: 'inconclusive',\n reason: 'empty-plan-block',\n changed: 0,\n elapsedMs: elapsedMs,\n elapsedSeconds: elapsedSeconds,\n expectedPaths: expectedPaths,\n matchedExpectedPaths: [],\n changedPaths: changedPaths,\n predicate: 'no-expected-targets',\n qeSignalLine: 'CODEX-LANDING-SIGNAL status=inconclusive predicate=no-expected-targets reason=empty-plan-block',\n };\n }\n const baseline = snapshot.baseline;\n if (!baseline || !baseline.ok) {\n const reason = baseline && baseline.reason ? baseline.reason : 'no-baseline';\n return {\n status: 'inconclusive',\n reason: reason,\n changed: 0,\n elapsedMs: elapsedMs,\n elapsedSeconds: elapsedSeconds,\n expectedPaths: expectedPaths,\n matchedExpectedPaths: [],\n changedPaths: changedPaths,\n predicate: 'newly-changed',\n qeSignalLine: 'CODEX-LANDING-SIGNAL status=inconclusive predicate=newly-changed reason=' + reason,\n };\n }\n const changed = new Set(changedPaths);\n const matchedExpectedPaths = expectedPaths.filter(function (path) {\n return changed.has(path) && isNewlyChanged(path, baseline, snapshot.currentHashes);\n });\n if (matchedExpectedPaths.length > 0) {\n return {\n status: 'landed',\n changed: matchedExpectedPaths.length,\n elapsedMs: elapsedMs,\n elapsedSeconds: elapsedSeconds,\n expectedPaths: expectedPaths,\n matchedExpectedPaths: matchedExpectedPaths,\n changedPaths: changedPaths,\n predicate: 'newly-changed',\n qeSignalLine: 'CODEX-LANDING-SIGNAL status=landed changed=' +\n matchedExpectedPaths.length +\n ' after=' +\n elapsedSeconds +\n 's predicate=newly-changed matched=' +\n matchedExpectedPaths.join(','),\n };\n }\n if (elapsedMs < maxWaitMs) {\n return {\n status: 'not-yet-flushed',\n changed: 0,\n elapsedMs: elapsedMs,\n elapsedSeconds: elapsedSeconds,\n expectedPaths: expectedPaths,\n matchedExpectedPaths: [],\n changedPaths: changedPaths,\n predicate: 'empty-before-timeout',\n qeSignalLine: 'CODEX-LANDING-SIGNAL status=not-yet-flushed changed=0 after ' + elapsedSeconds + 's — not yet flushed',\n };\n }\n const terminalSeconds = Math.ceil(maxWaitMs / 1000);\n return {\n status: 'genuinely-not-landed',\n changed: 0,\n elapsedMs: elapsedMs,\n elapsedSeconds: terminalSeconds,\n expectedPaths: expectedPaths,\n matchedExpectedPaths: [],\n changedPaths: changedPaths,\n predicate: 'empty-after-timeout',\n qeSignalLine: 'CODEX-LANDING-SIGNAL status=genuinely-not-landed ' + codeLandingEmptySignal(terminalSeconds),\n };\n}\nconst WRAPPER_STAGES = { code: 1, plan: 1 };\nfunction codexDispatchMode(stage) {\n return WRAPPER_STAGES[stage] ? 'wrapper' : 'exec';\n}\nconst CODEX_EXEC_PROMPT_CEILING_CHARS = 24000;\nfunction codexExecPlan(input) {\n if (codexDispatchMode(input.stage) === 'wrapper') {\n return { mode: 'wrapper', reason: 'deliverable is a file written out-of-band' };\n }\n if (!input.probedId) {\n return { mode: 'claude', reason: 'no codex model id answered the probe' };\n }\n if (input.stage === 'qe' && input.scoped !== true) {\n return {\n mode: 'claude',\n reason: 'qe prompt is not SCOPED — an unscoped codex exec QE buys reconnaissance, not review ' +\n '(MEASURED 2026-08-21: 19038 chars, 280s, exit 124, no verdict)',\n };\n }\n if (input.promptChars > CODEX_EXEC_PROMPT_CEILING_CHARS) {\n return {\n mode: 'claude',\n reason: 'prompt is ' +\n input.promptChars +\n ' chars, over the ' +\n CODEX_EXEC_PROMPT_CEILING_CHARS +\n '-char codex exec ceiling (it would stall)',\n };\n }\n return { mode: 'exec', reason: 'codex exec on ' + input.probedId };\n}\nconst SCOPED_QE_MAX_FILES = 3;\nconst SCOPED_QE_MAX_QUESTIONS = 4;\nconst SCOPED_QE_MAX_PATH_CHARS = 200;\nconst SCOPED_QE_MAX_QUESTION_CHARS = 200;\nfunction scopedQePrompt(input) {\n const o = input || {};\n const rawFiles = Array.isArray(o.files) ? o.files : [];\n const files = [];\n for (const f of rawFiles) {\n const s = String(f === undefined || f === null ? '' : f).trim();\n if (s === '')\n continue;\n if (files.indexOf(s) !== -1)\n continue;\n files.push(s.slice(0, SCOPED_QE_MAX_PATH_CHARS));\n if (files.length >= SCOPED_QE_MAX_FILES)\n break;\n }\n if (files.length === 0)\n return '';\n const rawQuestions = Array.isArray(o.questions) ? o.questions : [];\n const questions = [];\n for (const q of rawQuestions) {\n const s = String(q === undefined || q === null ? '' : q).trim().replace(/\\s+/g, ' ');\n if (s === '')\n continue;\n questions.push(s.slice(0, SCOPED_QE_MAX_QUESTION_CHARS));\n if (questions.length >= SCOPED_QE_MAX_QUESTIONS)\n break;\n }\n if (questions.length === 0) {\n questions.push('Is this change correct, and does the test named by its ADR actually DISCRIMINATE (would it fail if the protection were deleted)?');\n }\n const slug = String(o.slug === undefined || o.slug === null ? '' : o.slug).trim().slice(0, 60);\n let out = 'Read ONLY these files: ' + files.join(', ') + '. Do NOT open any other file and do NOT explore the repository.';\n if (slug !== '')\n out += ' They are the changed files of feature ' + slug + '.';\n out += '\\n\\nAnswer these ' + questions.length + ' questions about them:\\n';\n for (let i = 0; i < questions.length; i++)\n out += i + 1 + '. ' + questions[i] + '\\n';\n out += '\\nFinish with a single final line: Grade: <A|B|C|D>';\n return out;\n}",
95
+ code: "const CODE_LANDING_PIPELINE_PREFIXES = ['features/', '.dz/', '.agentic-qe/', 'roam/'];\nfunction codeLandingEmptySignal(seconds) {\n return 'changed=0 after ' + seconds + 's — genuinely not landed';\n}\nfunction needsCodeLandedBarrier(coderUsed) {\n return coderUsed === 'codex' || coderUsed === 'codex-fallback';\n}\nfunction stripCodeLandingPath(path) {\n let p = String(path || '').trim().replace(/\\\\/g, '/');\n while (p.indexOf('./') === 0)\n p = p.slice(2);\n return p.replace(/\\/+/g, '/');\n}\nfunction classifyCodeLandingPathReject(path) {\n const p = stripCodeLandingPath(path);\n if (!p)\n return 'empty-after-strip';\n if (p[0] === '/')\n return 'absolute-path';\n if (p === '..' || p.indexOf('../') === 0 || p.indexOf('/../') >= 0 || p.endsWith('/..'))\n return 'traversal';\n if (/[\\0\\r\\n\\t \"'\\x60$;&|<>*?()[\\]{}!]/.test(p))\n return 'not-a-path';\n if (p.endsWith('/'))\n return 'not-a-path';\n for (const prefix of CODE_LANDING_PIPELINE_PREFIXES) {\n const bare = prefix.slice(0, -1);\n if (p === bare || p.indexOf(prefix) === 0)\n return 'pipeline-artifact-path';\n }\n return null;\n}\nfunction normalizeCodeLandingPath(path) {\n return classifyCodeLandingPathReject(path) === null ? stripCodeLandingPath(path) : '';\n}\nfunction filterPollableCodePaths(paths) {\n const out = [];\n const seen = new Set();\n for (const path of paths || []) {\n const normalized = normalizeCodeLandingPath(path);\n if (!normalized || seen.has(normalized))\n continue;\n seen.add(normalized);\n out.push(normalized);\n }\n return out;\n}\nfunction isNewlyChanged(path, baseline, currentHashes) {\n if (!baseline || !baseline.ok)\n return false;\n let recorded = null;\n for (const entry of baseline.entries) {\n if (entry.path === path) {\n recorded = entry.hash;\n break;\n }\n }\n if (recorded === null)\n return true;\n const now = currentHashes ? currentHashes[path] : undefined;\n if (now === undefined)\n return false;\n return now !== recorded;\n}\nfunction decideCodeLanding(snapshot) {\n const maxWaitMs = Math.max(0, snapshot.maxWaitMs);\n const elapsedMs = Math.max(0, snapshot.elapsedMs);\n const elapsedSeconds = Math.floor(elapsedMs / 1000);\n const expectedPaths = filterPollableCodePaths(snapshot.expectedPaths);\n const changedPaths = filterPollableCodePaths(snapshot.changedEntries.map(function (entry) { return entry.path; }));\n if (expectedPaths.length === 0) {\n return {\n status: 'inconclusive',\n reason: 'empty-plan-block',\n changed: 0,\n elapsedMs: elapsedMs,\n elapsedSeconds: elapsedSeconds,\n expectedPaths: expectedPaths,\n matchedExpectedPaths: [],\n changedPaths: changedPaths,\n predicate: 'no-expected-targets',\n qeSignalLine: 'CODEX-LANDING-SIGNAL status=inconclusive predicate=no-expected-targets reason=empty-plan-block',\n };\n }\n const baseline = snapshot.baseline;\n if (!baseline || !baseline.ok) {\n const reason = baseline && baseline.reason ? baseline.reason : 'no-baseline';\n return {\n status: 'inconclusive',\n reason: reason,\n changed: 0,\n elapsedMs: elapsedMs,\n elapsedSeconds: elapsedSeconds,\n expectedPaths: expectedPaths,\n matchedExpectedPaths: [],\n changedPaths: changedPaths,\n predicate: 'newly-changed',\n qeSignalLine: 'CODEX-LANDING-SIGNAL status=inconclusive predicate=newly-changed reason=' + reason,\n };\n }\n const changed = new Set(changedPaths);\n const matchedExpectedPaths = expectedPaths.filter(function (path) {\n return changed.has(path) && isNewlyChanged(path, baseline, snapshot.currentHashes);\n });\n if (matchedExpectedPaths.length > 0) {\n return {\n status: 'landed',\n changed: matchedExpectedPaths.length,\n elapsedMs: elapsedMs,\n elapsedSeconds: elapsedSeconds,\n expectedPaths: expectedPaths,\n matchedExpectedPaths: matchedExpectedPaths,\n changedPaths: changedPaths,\n predicate: 'newly-changed',\n qeSignalLine: 'CODEX-LANDING-SIGNAL status=landed changed=' +\n matchedExpectedPaths.length +\n ' after=' +\n elapsedSeconds +\n 's predicate=newly-changed matched=' +\n matchedExpectedPaths.join(','),\n };\n }\n if (elapsedMs < maxWaitMs) {\n return {\n status: 'not-yet-flushed',\n changed: 0,\n elapsedMs: elapsedMs,\n elapsedSeconds: elapsedSeconds,\n expectedPaths: expectedPaths,\n matchedExpectedPaths: [],\n changedPaths: changedPaths,\n predicate: 'empty-before-timeout',\n qeSignalLine: 'CODEX-LANDING-SIGNAL status=not-yet-flushed changed=0 after ' + elapsedSeconds + 's — not yet flushed',\n };\n }\n const terminalSeconds = Math.ceil(maxWaitMs / 1000);\n return {\n status: 'genuinely-not-landed',\n changed: 0,\n elapsedMs: elapsedMs,\n elapsedSeconds: terminalSeconds,\n expectedPaths: expectedPaths,\n matchedExpectedPaths: [],\n changedPaths: changedPaths,\n predicate: 'empty-after-timeout',\n qeSignalLine: 'CODEX-LANDING-SIGNAL status=genuinely-not-landed ' + codeLandingEmptySignal(terminalSeconds),\n };\n}\nconst WRAPPER_STAGES = { code: 1, plan: 1 };\nfunction codexDispatchMode(stage) {\n return WRAPPER_STAGES[stage] ? 'wrapper' : 'exec';\n}\nconst CODEX_EXEC_PROMPT_CEILING_CHARS = 24000;\nfunction codexExecPlan(input) {\n if (codexDispatchMode(input.stage) === 'wrapper') {\n return { mode: 'wrapper', reason: 'deliverable is a file written out-of-band' };\n }\n if (!input.probedId) {\n return { mode: 'claude', reason: 'no codex model id answered the probe' };\n }\n if (input.stage === 'qe' && input.scoped !== true) {\n return {\n mode: 'claude',\n reason: 'qe prompt is not SCOPED — an unscoped codex exec QE buys reconnaissance, not review ' +\n '(MEASURED 2026-08-21: 19038 chars, 280s, exit 124, no verdict)',\n };\n }\n if (input.promptChars > CODEX_EXEC_PROMPT_CEILING_CHARS) {\n return {\n mode: 'claude',\n reason: 'prompt is ' +\n input.promptChars +\n ' chars, over the ' +\n CODEX_EXEC_PROMPT_CEILING_CHARS +\n '-char codex exec ceiling (it would stall)',\n };\n }\n return { mode: 'exec', reason: 'codex exec on ' + input.probedId };\n}\nconst SCOPED_QE_MAX_FILES = 3;\nconst SCOPED_QE_MAX_QUESTIONS = 4;\nconst SCOPED_QE_MAX_PATH_CHARS = 200;\nconst SCOPED_QE_MAX_QUESTION_CHARS = 200;\nfunction scopedQePrompt(input) {\n const o = input || {};\n const rawFiles = Array.isArray(o.files) ? o.files : [];\n const files = [];\n for (const f of rawFiles) {\n const s = String(f === undefined || f === null ? '' : f).trim();\n if (s === '')\n continue;\n if (files.indexOf(s) !== -1)\n continue;\n files.push(s.slice(0, SCOPED_QE_MAX_PATH_CHARS));\n if (files.length >= SCOPED_QE_MAX_FILES)\n break;\n }\n if (files.length === 0)\n return '';\n const rawQuestions = Array.isArray(o.questions) ? o.questions : [];\n const questions = [];\n for (const q of rawQuestions) {\n const s = String(q === undefined || q === null ? '' : q).trim().replace(/\\s+/g, ' ');\n if (s === '')\n continue;\n questions.push(s.slice(0, SCOPED_QE_MAX_QUESTION_CHARS));\n if (questions.length >= SCOPED_QE_MAX_QUESTIONS)\n break;\n }\n if (questions.length === 0) {\n questions.push('Is this change correct, and does the test named by its ADR actually DISCRIMINATE (would it fail if the protection were deleted)?');\n }\n const slug = String(o.slug === undefined || o.slug === null ? '' : o.slug).trim().slice(0, 60);\n let out = 'Read ONLY these files: ' + files.join(', ') + '. Do NOT open any other file and do NOT explore the repository.';\n if (slug !== '')\n out += ' They are the changed files of feature ' + slug + '.';\n out += '\\n\\nAnswer these ' + questions.length + ' questions about them:\\n';\n for (let i = 0; i < questions.length; i++)\n out += i + 1 + '. ' + questions[i] + '\\n';\n out += '\\nFinish with two final lines: Grade: <A|B|C|D> and QE-VERDICT: <same>';\n return out;\n}",
96
96
  },
97
97
  "challenge-panel": {
98
98
  name: "challenge-panel",
@@ -72,6 +72,322 @@ export function buildMutationTestCommand(
72
72
  };
73
73
  }
74
74
 
75
+ /**
76
+ * mutation-gate-inject-tokens FR-1..FR-3 (D1-D4b, fix-round 1 F1-F3): inject `--maxWorkers=<n>`
77
+ * into EVERY `vitest run` segment of a (possibly compound) test command, token-scoped — not the
78
+ * whole-command substring append that used to (a) cap only the FIRST `vitest run` in `a && b` (D1),
79
+ * (b) let an existing-flag SUBSTRING check be fooled by an unrelated `--maxWorkers` living inside a
80
+ * quoted argument or another command's own flags (D2, D3), and (c) stack a second `--maxWorkers` on
81
+ * top of a user's own `--max-workers=<n>` (D4) instead of deferring to it.
82
+ *
83
+ * Segmentation splits on `&&`/`||`/`;`/`|` that are OUTSIDE single/double quotes, POSIX-escape aware
84
+ * (fix-round 1, F1 — the round-1 Codex review's HIGH finding): outside single quotes a `\` makes the
85
+ * NEXT character literal (so `\"` cannot open/close a double-quoted span, and `\&` cannot be mistaken
86
+ * for an operator); inside double quotes a `\` escapes at least `\"` and `\\` (so `-t "a \" && b"` stays
87
+ * ONE segment — the escaped quote does not close the string, so the `&&` inside it is never treated as
88
+ * a real terminator); inside single quotes nothing is special, matching POSIX. This is the fix for the
89
+ * exploit the verdict named: `npx vitest run -t "a \" && b" --maxWorkers=1` used to be mis-split into
90
+ * two pseudo-segments (the existing flag ending up in the "wrong" one), stacking a second flag.
91
+ *
92
+ * Within each segment, `vitest run` is recognised only in COMMAND POSITION (fix-round 1, F2 — the
93
+ * round-1 Codex review's other HIGH finding): the first word token, after skipping any leading bare
94
+ * `NAME=value` assignments and at most one runner-prefix chain (`npx`, `pnpm exec`, `pnpm dlx`, `yarn`,
95
+ * `bunx`, or `env`/`cross-env` followed by more assignments), must have a BASENAME of `vitest`,
96
+ * `vitest.cmd`, `vitest.mjs` or `vitest.js` (a full path like `node_modules/.bin/vitest` counts — only
97
+ * the basename is compared), immediately followed by the literal token `run`. `echo vitest run` and
98
+ * `node wrapper.js vitest run` are therefore NOT vitest commands (`echo`/`node` is not an allowed
99
+ * prefix and is not itself a vitest basename) — the old scan matched `vitest`+`run` ANYWHERE in the
100
+ * segment and would have mutated both.
101
+ *
102
+ * An existing ceiling flag is detected per DECODED token (fix-round 1, F3 — MEDIUM finding: the old
103
+ * substring/regex checks compared RAW tokens, so a quoted `"--maxWorkers=1"` was invisible, and the
104
+ * regex additionally accepted unclaimed spellings like `--maxworkers`/`--max-Workers`) via
105
+ * `/^--(?:maxWorkers|max-workers)(?:=.*)?$/` — matches exactly `--maxWorkers=1`, `--maxWorkers 1` (the
106
+ * bare flag token, value in the next token), `--max-workers=1`, `--max-workers 1`; does NOT match
107
+ * `--maxWorkersFoo=9` (D3) or `--maxworkers`/`--max-Workers` (not the two claimed spellings). The scan
108
+ * stops at a standalone `--` token (F3): everything after it is positional per POSIX, so a `--maxWorkers`
109
+ * living there is a positional argument to vitest's OWN test-name filter, not a flag naming the ceiling.
110
+ * When a real flag is present, the segment is left untouched (the user's explicit choice wins, D4);
111
+ * when absent, ` --maxWorkers=<n>` is inserted immediately after the `run` token's RAW source span
112
+ * (never the decoded one — insertion always preserves the original quoting of everything else).
113
+ */
114
+ export interface InjectVitestWorkerCeilingResult {
115
+ /** the command with the ceiling injected into every eligible vitest segment. */
116
+ readonly cmd: string;
117
+ /** how many segments were recognised as `vitest run` (0 for a non-vitest command). */
118
+ readonly vitestSegments: number;
119
+ /** how many of those segments actually got `--maxWorkers=<n>` inserted (excludes ones that already named it). */
120
+ readonly injected: number;
121
+ /** segments where `vitest run` was found only by the LOOSE token-pair fallback (command position
122
+ * unrecognised) — the CLI reports these so an odd wrapper shape is visible, not silent. */
123
+ readonly looseSegments: number;
124
+ }
125
+
126
+ interface RawSegment {
127
+ readonly text: string;
128
+ /** the operator that ended this segment (`&&`/`||`/`;`/`|`), or `''` for the last segment. */
129
+ readonly terminator: string;
130
+ }
131
+
132
+ /**
133
+ * Split on unquoted `&&`/`||`/`;`/`|`, preserving each segment's own text (incl. surrounding
134
+ * whitespace). POSIX-escape aware (fix-round 1, F1): outside quotes `\` makes the next character
135
+ * literal (so it can neither open a quote nor start an operator); inside double quotes `\"` and `\\`
136
+ * are recognised escapes that do NOT close the string; inside single quotes nothing is escaped.
137
+ */
138
+ function splitUnquotedSegments(cmd: string): RawSegment[] {
139
+ const segments: RawSegment[] = [];
140
+ let segStart = 0;
141
+ let inSingle = false;
142
+ let inDouble = false;
143
+ let i = 0;
144
+ while (i < cmd.length) {
145
+ const ch = cmd[i];
146
+ if (inSingle) {
147
+ if (ch === "'") inSingle = false;
148
+ i += 1;
149
+ continue;
150
+ }
151
+ if (inDouble) {
152
+ if (ch === '"') { inDouble = false; i += 1; continue; }
153
+ if (ch === '\\') {
154
+ const next = cmd[i + 1];
155
+ // at least \" and \\ (F1's floor) — an escaped quote must not close the double-quoted span.
156
+ if (next === '"' || next === '\\') { i += 2; continue; }
157
+ i += 1;
158
+ continue;
159
+ }
160
+ i += 1;
161
+ continue;
162
+ }
163
+ if (ch === "'") {
164
+ inSingle = true;
165
+ i += 1;
166
+ continue;
167
+ }
168
+ if (ch === '"') {
169
+ inDouble = true;
170
+ i += 1;
171
+ continue;
172
+ }
173
+ if (ch === '\\') {
174
+ // Outside any quote, POSIX makes the character AFTER `\` literal — skip both so it can never
175
+ // be mis-read as a quote-open or an operator boundary.
176
+ i += cmd[i + 1] !== undefined ? 2 : 1;
177
+ continue;
178
+ }
179
+ const terminatorMatch = /^(&&|\|\||;|\|)/.exec(cmd.slice(i));
180
+ if (terminatorMatch) {
181
+ segments.push({ text: cmd.slice(segStart, i), terminator: terminatorMatch[0] });
182
+ i += terminatorMatch[0].length;
183
+ segStart = i;
184
+ continue;
185
+ }
186
+ i += 1;
187
+ }
188
+ segments.push({ text: cmd.slice(segStart), terminator: '' });
189
+ return segments;
190
+ }
191
+
192
+ interface SegmentToken {
193
+ /** decoded value — quotes stripped, at-least-\"/\\ escapes resolved (F1, F3: comparisons use this). */
194
+ readonly value: string;
195
+ /** RAW source end offset within the segment text — insertion always splices at a raw offset. */
196
+ readonly end: number;
197
+ }
198
+
199
+ function isWhitespaceChar(ch: string | undefined): boolean {
200
+ return ch !== undefined && /\s/u.test(ch);
201
+ }
202
+
203
+ /**
204
+ * Whitespace-delimited tokens with POSIX-ish quote/escape DECODING (fix-round 1, F1/F3): a quoted
205
+ * span (single or double) still forms one token with its surrounding unquoted parts (POSIX word
206
+ * concatenation — `'it''s'` decodes to the single token `its`), but `token.value` now holds the
207
+ * DECODED text (quotes removed, `\"`/`\\` resolved inside double quotes, `\<char>` resolved to
208
+ * `<char>` outside any quote, single-quoted content kept verbatim) while `token.end` keeps the RAW
209
+ * source offset so `injectIntoSegment` can still splice into the ORIGINAL text unchanged elsewhere.
210
+ */
211
+ function tokenizeSegment(text: string): SegmentToken[] {
212
+ const tokens: SegmentToken[] = [];
213
+ let i = 0;
214
+ while (i < text.length) {
215
+ while (i < text.length && isWhitespaceChar(text[i])) i += 1;
216
+ if (i >= text.length) break;
217
+ const start = i;
218
+ let value = '';
219
+ let inSingle = false;
220
+ let inDouble = false;
221
+ while (i < text.length) {
222
+ const ch = text[i];
223
+ if (ch === undefined) break;
224
+ if (inSingle) {
225
+ if (ch === "'") { inSingle = false; i += 1; continue; }
226
+ value += ch;
227
+ i += 1;
228
+ continue;
229
+ }
230
+ if (inDouble) {
231
+ if (ch === '"') { inDouble = false; i += 1; continue; }
232
+ if (ch === '\\') {
233
+ const next = text[i + 1];
234
+ if (next === '"' || next === '\\') { value += next; i += 2; continue; }
235
+ // not one of the two claimed double-quote escapes: the backslash is literal (F1's floor).
236
+ value += ch;
237
+ i += 1;
238
+ continue;
239
+ }
240
+ value += ch;
241
+ i += 1;
242
+ continue;
243
+ }
244
+ if (ch === "'") { inSingle = true; i += 1; continue; }
245
+ if (ch === '"') { inDouble = true; i += 1; continue; }
246
+ if (ch === '\\') {
247
+ const next = text[i + 1];
248
+ // POSIX line continuation (lead delta after Codex r2, MEDIUM): `\<newline>` (and `\<CR><LF>`)
249
+ // is REMOVED by the shell, never a literal — a token must not swallow a newline as its value.
250
+ if (next === '\n' || (next === '\r' && text[i + 2] === '\n')) {
251
+ i += next === '\n' ? 2 : 3;
252
+ // an EMPTY token so far means the continuation sat between words: skip the whitespace
253
+ // that follows so the next word starts a real token instead of an empty one.
254
+ if (value.length === 0) { while (i < text.length && isWhitespaceChar(text[i]!)) i += 1; }
255
+ continue;
256
+ }
257
+ if (next !== undefined) { value += next; i += 2; continue; }
258
+ value += ch; // trailing lone backslash: nothing to escape, keep it literal.
259
+ i += 1;
260
+ continue;
261
+ }
262
+ if (isWhitespaceChar(ch)) break;
263
+ value += ch;
264
+ i += 1;
265
+ }
266
+ tokens.push({ value, end: i });
267
+ }
268
+ return tokens;
269
+ }
270
+
271
+ /** strictly `--maxWorkers` or `--max-workers`, with or without `=…` (fix-round 1, F3: no other spelling). */
272
+ const MAX_WORKERS_TOKEN = /^--(?:maxWorkers|max-workers)(?:=.*)?$/u;
273
+
274
+ /** basenames vitest's own bin may resolve to; a leading path (POSIX or Windows-style) is stripped. */
275
+ const VITEST_BASENAMES = new Set(['vitest', 'vitest.cmd', 'vitest.mjs', 'vitest.js']);
276
+
277
+ /** allowed single-token runner prefixes that may precede the vitest executable. */
278
+ const SINGLE_TOKEN_PREFIXES = new Set(['npx', 'yarn', 'bunx']);
279
+
280
+ /** `env`/`cross-env` may be followed by more `NAME=value` assignments before the real command. */
281
+ const ENV_STYLE_PREFIXES = new Set(['env', 'cross-env']);
282
+
283
+ /** `NAME=value` — a bare shell-style assignment token, decoded value. */
284
+ const ASSIGNMENT_TOKEN = /^[A-Za-z_][A-Za-z0-9_]*=/u;
285
+
286
+ function basenameOf(path: string): string {
287
+ const normalised = path.replace(/\\/g, '/');
288
+ const idx = normalised.lastIndexOf('/');
289
+ return idx === -1 ? normalised : normalised.slice(idx + 1);
290
+ }
291
+
292
+ /**
293
+ * Find `vitest run` in COMMAND POSITION (fix-round 1, F2): the first word token after (a) any
294
+ * leading bare `NAME=value` assignments, then (b) AT MOST ONE recognised runner-prefix chain
295
+ * (`npx` / `yarn` / `bunx` — one token; `pnpm exec` / `pnpm dlx` — two tokens; `env` / `cross-env` —
296
+ * one token, itself followed by zero or more further assignments) must have a basename in
297
+ * `VITEST_BASENAMES`, and the token right after it must be the literal `run`. Returns the INDEX of
298
+ * that `run` token, or null when this segment is not a vitest-run command. `echo vitest run` and
299
+ * `node wrapper.js vitest run` correctly return null: `echo`/`node` are neither an allowed prefix
300
+ * nor a vitest basename, so the scan never advances past them.
301
+ */
302
+ function findVitestRunCommandIndex(tokens: readonly SegmentToken[]): number | null {
303
+ let idx = 0;
304
+ // Lead delta after Codex r2 (HIGH): the prefix chain is ITERATIVE and OPTION-TOLERANT — a runner
305
+ // prefix may carry its own dash-options (`npx --yes`, `pnpm exec --silent`) and prefixes may chain
306
+ // (`env CI=1 npx vitest run`). The previous single-step grammar returned null for both and left
307
+ // such commands UNCAPPED — a regression against the substring era this feature replaced.
308
+ for (;;) {
309
+ while (idx < tokens.length && ASSIGNMENT_TOKEN.test(tokens[idx]!.value)) idx += 1;
310
+ const head = tokens[idx]?.value;
311
+ if (head === undefined) break;
312
+ if (SINGLE_TOKEN_PREFIXES.has(head)) {
313
+ idx += 1;
314
+ } else if (head === 'pnpm' && (tokens[idx + 1]?.value === 'exec' || tokens[idx + 1]?.value === 'dlx')) {
315
+ idx += 2;
316
+ } else if (ENV_STYLE_PREFIXES.has(head)) {
317
+ idx += 1;
318
+ continue; // assignments after env/cross-env are consumed by the loop head
319
+ } else {
320
+ break;
321
+ }
322
+ while (idx < tokens.length && tokens[idx]!.value.startsWith('-')) idx += 1; // the prefix's own options
323
+ }
324
+ const exe = tokens[idx];
325
+ const runToken = tokens[idx + 1];
326
+ if (exe === undefined || runToken === undefined) return null;
327
+ if (!VITEST_BASENAMES.has(basenameOf(exe.value))) return null;
328
+ if (runToken.value !== 'run') return null;
329
+ return idx + 1;
330
+ }
331
+
332
+ /**
333
+ * True when a `--maxWorkers`/`--max-workers` token names the ceiling ANYWHERE before a standalone
334
+ * `--` (fix-round 1, F3): a `--` marks POSIX end-of-options, so anything naming the flag AFTER it is
335
+ * a positional argument (e.g. vitest's own test-name filter), never the ceiling flag itself.
336
+ */
337
+ function hasMaxWorkersFlag(tokens: readonly SegmentToken[]): boolean {
338
+ for (const token of tokens) {
339
+ if (token.value === '--') return false;
340
+ if (MAX_WORKERS_TOKEN.test(token.value)) return true;
341
+ }
342
+ return false;
343
+ }
344
+
345
+ /**
346
+ * LOOSE fallback (lead delta after Codex r2, HIGH): when the strict command-position grammar finds
347
+ * nothing, look for a `<vitest-basename> run` token pair ANYWHERE in the segment. The ceiling exists
348
+ * to keep a full-suite run from taking the machine down (0bb74d66); an unrecognised wrapper shape
349
+ * must degrade to the substring-era behaviour (capped, reported as `loose`), never to an uncapped
350
+ * run. The price — `echo vitest run` also gets the flag — is named in the result so the CLI can say
351
+ * it out loud, and is a harmless extra argument to a non-vitest command.
352
+ */
353
+ function findVitestRunLooseIndex(tokens: readonly SegmentToken[]): number | null {
354
+ for (let i = 0; i + 1 < tokens.length; i += 1) {
355
+ if (VITEST_BASENAMES.has(basenameOf(tokens[i]!.value)) && tokens[i + 1]!.value === 'run') return i + 1;
356
+ }
357
+ return null;
358
+ }
359
+
360
+ function injectIntoSegment(text: string, maxWorkers: number): { readonly text: string; readonly isVitest: boolean; readonly injected: boolean; readonly loose: boolean } {
361
+ const tokens = tokenizeSegment(text);
362
+ const strictIdx = findVitestRunCommandIndex(tokens);
363
+ const looseIdx = strictIdx === null ? findVitestRunLooseIndex(tokens) : null;
364
+ const runTokenIdx = strictIdx ?? looseIdx;
365
+ const loose = strictIdx === null && looseIdx !== null;
366
+ if (runTokenIdx === null) return { text, isVitest: false, injected: false, loose: false };
367
+ if (hasMaxWorkersFlag(tokens)) return { text, isVitest: true, injected: false, loose };
368
+ const runToken = tokens[runTokenIdx];
369
+ // unreachable defensively: runTokenIdx was derived from a valid index into `tokens` above.
370
+ if (runToken === undefined) return { text, isVitest: true, injected: false, loose };
371
+ const insertAt = runToken.end;
372
+ const injectedText = `${text.slice(0, insertAt)} --maxWorkers=${maxWorkers}${text.slice(insertAt)}`;
373
+ return { text: injectedText, isVitest: true, injected: true, loose };
374
+ }
375
+
376
+ export function injectVitestWorkerCeiling(testCmd: string, maxWorkers: number): InjectVitestWorkerCeilingResult {
377
+ const segments = splitUnquotedSegments(testCmd);
378
+ let vitestSegments = 0;
379
+ let injected = 0;
380
+ let looseSegments = 0;
381
+ const rebuilt = segments.map((segment) => {
382
+ const result = injectIntoSegment(segment.text, maxWorkers);
383
+ if (result.isVitest) vitestSegments += 1;
384
+ if (result.injected) injected += 1;
385
+ if (result.loose) looseSegments += 1;
386
+ return result.text + segment.terminator;
387
+ }).join('');
388
+ return { cmd: rebuilt, vitestSegments, injected, looseSegments };
389
+ }
390
+
75
391
  export interface MutationRegistry {
76
392
  /** optional suite command override for the whole registry (default `npm test`). */
77
393
  readonly testCommand?: string;
package/src/operations.ts CHANGED
@@ -1893,8 +1893,19 @@ export function probeHookLiveness(
1893
1893
  command: string,
1894
1894
  payload: string,
1895
1895
  opts: { readonly cwd?: string; readonly env?: Readonly<Record<string, string>>; readonly timeoutMs?: number } = {},
1896
- ): { readonly status: number | null; readonly stdout: string; readonly stderr: string } {
1896
+ ): { readonly status: number | null; readonly stdout: string; readonly stderr: string; readonly groupKillAttempted: boolean } {
1897
1897
  const shell = process.env['SHELL'] ?? '/bin/sh';
1898
+ // Fix round 1 (apply-leg-never-silent, HIGH-1): a caller previously had to INFER "was the group
1899
+ // kill sent" by reading this function's source — a regression removing or bypassing the
1900
+ // `process.kill(-pid, ...)` call below would silently invalidate that inference. `groupKillAttempted`
1901
+ // is the OBSERVABLE fact instead: true exactly when this call reached the point of attempting the
1902
+ // kill syscall (`res.pid` was a real positive pid), false when it never got that far (e.g. the
1903
+ // spawn itself never produced a pid). It does NOT claim the signal found a live recipient — ESRCH
1904
+ // ("group already gone", the common successful-exit case) still counts as "sent": the syscall was
1905
+ // issued, its target simply no longer existed. That is a SEPARATE fact from whether the grandchild
1906
+ // is actually dead by the time a caller checks — see `probeApplyLeg`'s AM-5 test for the
1907
+ // kill-sent-vs-death-observed split this field exists to make possible.
1908
+ let groupKillAttempted = false;
1898
1909
  try {
1899
1910
  // AM-5 (fix round 1, apply-leg-never-silent): `detached: true` puts the shell in its OWN
1900
1911
  // process GROUP (pgid === its own pid) instead of sharing the caller's — `spawnSync`'s own
@@ -1925,11 +1936,15 @@ export function probeHookLiveness(
1925
1936
  process.kill(-res.pid, 'SIGKILL');
1926
1937
  } catch {
1927
1938
  /* group already gone */
1939
+ } finally {
1940
+ // set right after the process.kill(-pid, 'SIGKILL') attempt (HIGH-1 lead decision): reached
1941
+ // regardless of ESRCH, because ESRCH means "no recipient", not "syscall not issued".
1942
+ groupKillAttempted = true;
1928
1943
  }
1929
1944
  }
1930
- return { status: res.status, stdout: res.stdout ?? '', stderr: res.stderr ?? '' };
1945
+ return { status: res.status, stdout: res.stdout ?? '', stderr: res.stderr ?? '', groupKillAttempted };
1931
1946
  } catch (err) {
1932
- return { status: null, stdout: '', stderr: String((err as Error)?.message ?? err) };
1947
+ return { status: null, stdout: '', stderr: String((err as Error)?.message ?? err), groupKillAttempted };
1933
1948
  }
1934
1949
  }
1935
1950