tickmarkr 1.54.0 → 1.55.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.
package/README.md CHANGED
@@ -173,8 +173,56 @@ More levers, all optional and absent-safe (absent config keeps default behavior)
173
173
  the routed model's window
174
174
  - `routing.sla` — per-shape latency expectations, surfaced as advisory plan lints against the
175
175
  learned performance profile
176
- - `run --quality` — raise every capability floor one band for that run (it never lowers one),
177
- disable exploration, and record the original and raised floor in routing provenance
176
+ - `run --quality` — compatibility alias for `run --mode partner-led` for that run; it disables
177
+ exploration and records the selected mode in routing provenance
178
+
179
+ ## Steering: routing modes, reviewer preferences, and reruns
180
+
181
+ ### Routing modes
182
+
183
+ `routing.mode` is a preset that compiles into floor assignments at config load time. The router never sees the mode itself; it receives resolved floors only.
184
+
185
+ Three routing modes are available:
186
+
187
+ - **`risk-based`** (default): byte-identical to pre-v1.51 routing. Absent `mode` key resolves as risk-based.
188
+ - **`partner-led`**: resolves every non-overridden shape to a `frontier` floor and disables exploration — use when quality is paramount and cost is secondary.
189
+ - **`staff-led`**: lowers each mode default by one tier (e.g., `implement` and `refactor` become `cheap` instead of `mid`) while keeping the preset floor for the integrity set (`plan`, `spec`, `migration`, `ui`) at `frontier`.
190
+
191
+ Explicit `routing.floors` entries beat mode-preset deltas and are linted during plan if they shadow the mode's delta; an explicit integrity floor below `frontier` is also linted. The mode is compiled once at config load and never consulted during routing.
192
+
193
+ ### Review preferences
194
+
195
+ `review.prefer` is an ordered list of reviewer seats for the cross-vendor code-review gate. Entries are matched by diversity (never the same vendor or model as the original worker), and routing reorders the available channels only — it does not admit unauthed or denied channels.
196
+
197
+ ```yaml
198
+ review:
199
+ prefer: [codex, kimi] # bare adapter: inherits model from the routed channel
200
+ prefer: [codex:gpt-5.6-sol, kimi:kimi-code/k3] # adapter:model explicit
201
+ prefer: [codex, kimi:kimi-code/k3] # mixed: bare and explicit
202
+ ```
203
+
204
+ **Grammar**: review prefer entries may name a bare adapter (inheriting the model from the current channel) or an explicit `adapter:model` pair. Bare adapters rank every diversity-eligible channel for that adapter; explicit pairs rank one diversity-eligible channel.
205
+
206
+ ### Consult preferences
207
+
208
+ `consult.prefer` is a ranked failover list of seats for escalations on deadlock or gate stalls. Unlike review, a consult seat has no channel to inherit a model from, so entries **must be explicit `adapter:model` pairs**.
209
+
210
+ ```yaml
211
+ consult:
212
+ adapter: claude-code
213
+ model: fable
214
+ prefer: [codex:gpt-5.6-sol, kimi:kimi-code/k3] # adapter:model ONLY
215
+ ```
216
+
217
+ The daemon walks the preference list to the first live adapter, then the pinned `consult.adapter:model` pair as the final fallback. Failed or unparseable verdicts fall to the next entry.
218
+
219
+ **Grammar**: consult prefer entries require `adapter:model` form — a bare adapter name is invalid and fails config load. Every entry must declare both the adapter and the model because a consult seat runs independently with no channel context.
220
+
221
+ ### Rerun control: --supersedes
222
+
223
+ `tickmarkr run --supersedes <prior-runId>` marks the current run as a rerun of a prior engagement. The current task graph is used for the rerun; compile it fresh first if the spec changed. The prior runId is recorded in the new journal, and the prior journal records the successor, for audit trails and change attribution.
224
+
225
+ Use this when you modify the spec or worker logic and want to mark an intentional rerun while preserving the relationship in both run journals.
178
226
 
179
227
  ## Model scoping and auth detection
180
228
 
@@ -77,6 +77,19 @@ const MODEL_PROBE_PROMPT = "Reply with exactly OK and nothing else.";
77
77
  const MODEL_PROBE_TIMEOUT_MS = 60000;
78
78
  // Auth-words only match when tied to a failure word; bare "auth"/"OAuth"/"authored" never fail (v1.27 T2).
79
79
  const AUTH_FAILURE_RE = /\b4\d\d\b|\bauth(?:entication|orization)?\s+(?:error|failed|failure|denied)|unauthori[sz]ed|forbidden|access denied|credit(?:s)?\s+(?:exhausted|error|denied)/i;
80
+ const PROBE_REASON_CAP = 240;
81
+ // v1.55 T3: the tail must open at a word boundary — a mid-word slice ("odel 'grok-…'") reads as
82
+ // corruption in operator-facing diagnostics. Input is already space-normalized, so " " is the only
83
+ // boundary. A tail that is one unbroken token keeps its mid-word cut rather than storing nothing.
84
+ function reasonTail(output) {
85
+ if (output.length <= PROBE_REASON_CAP)
86
+ return output;
87
+ const tail = output.slice(-PROBE_REASON_CAP);
88
+ if (output[output.length - PROBE_REASON_CAP - 1] === " ")
89
+ return tail;
90
+ const sp = tail.indexOf(" ");
91
+ return sp === -1 ? tail : tail.slice(sp + 1);
92
+ }
80
93
  function probeFailure(code, stdout, stderr, timedOut, timeoutMs = MODEL_PROBE_TIMEOUT_MS) {
81
94
  // SIGKILL-timeout is not exit-1: report the budget, never the masked kill code (v1.27 T1).
82
95
  if (timedOut)
@@ -85,7 +98,7 @@ function probeFailure(code, stdout, stderr, timedOut, timeoutMs = MODEL_PROBE_TI
85
98
  // OBS-72: TAIL, not head — the error lands at the END of CLI output; a head slice stores only the
86
99
  // startup banner and hid the real "Not inside a trusted directory" failure for a day.
87
100
  return code !== 0 || QUOTA_RE.test(output) || AUTH_FAILURE_RE.test(output)
88
- ? output.slice(-240) || `probe exited ${code}`
101
+ ? reasonTail(output) || `probe exited ${code}`
89
102
  : undefined;
90
103
  }
91
104
  export const pendingAutoPreferKey = Symbol.for("tickmarkr.pendingAutoPrefer");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tickmarkr",
3
- "version": "1.54.0",
3
+ "version": "1.55.0",
4
4
  "description": "Spec in, verified work out.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -50,6 +50,10 @@ Use one of:
50
50
 
51
51
  After sending, **confirm delivery** by reading the target pane and verifying the message landed (input empty, agent status `working`, or notification acknowledged). Never report "briefed" or "relayed" without read-back confirmation.
52
52
 
53
+ ## Dedicated consultant tab rule
54
+
55
+ When spawning consultants (agents gathering synthesis input for decisions like SCOPER analysis or architectural reviews), create them in a DEDICATED tab separate from the ORCHESTRATOR tab. This ensures that when the orchestrator stands down, the consultant panes persist and their assessments remain available for review and reference.
56
+
53
57
  ## Stand-down (mission end and retirement)
54
58
 
55
59
  - **Orchestrator, on terminal state** (green, failed, or parked), after the record commit and operator notification: stop every monitor and background task you started, print one final stand-down line, and leave NOTHING queued in your input box. A finished session with an armed watcher or pre-filled input is a loaded gun — a retired v1.40 orchestrator sat idle with "merge … tag, publish" unsent in its input; one stray Enter would have shipped a duplicate release.