Skip to content

Merge dev → main (automated) - #2089

Merged
SorraTheOrc merged 21 commits into
mainfrom
release/dev-to-main-20260927004214
Sep 27, 2026
Merged

SorraTheOrc merged 21 commits into
mainfrom
release/dev-to-main-20260927004214

Conversation

@SorraTheOrc

Copy link
Copy Markdown
Member

Automated release created by ship skill.\n\nIncludes CHANGELOG.md with work-item summaries from this release.

pi added 21 commits September 24, 2026 11:34
…patch spawn path (WL-0MT50S9JW001DHME)

- DowntimeSpawnConfig gains a genuine freeSlots signal; the Phase 2
  parallelism decision is PARALLELISM=2 iff cheap mode AND slotBudget>=2
  AND freeSlots>=2 AND a single-flight budget (cap===1). Unbounded (0)
  and >=2 budgets keep '1' (fixes the prior cap=0 misread); the total
  slotBudget is no longer used as an availability proxy.
- The downtime worker derives spawnConfig from the polled status (free
  slots, total slots) plus the mode-switch worker's last known mode and
  threads it through every dispatch path to deps.spawnAgentPane.
- createDowntimeDeps forwards it to spawnDowntimePane ->
  buildDowntimeSpawnOptions; ModeSwitchWorker exposes getLastKnownMode().
- Tests cover the decision table, spawn/config forwarding, tick
  derivation, unknown-mode fallback, and getLastKnownMode.

AC5 (measured outcome) is delegated to operational follow-up
WL-0MUFD33FT0080OD5.
… gate in header

Make "idle slots but no dispatches" diagnosable from logs and the header.

- downtime-log.ts: extend CoordinationLogEntry with additive decision fields
  (reason/freeSlots/totalSlots/ownerPresent/runningPanes/contentionDepth) and
  add readCoordinationLogEntries() (fail-safe reader).
- downtime-worker.ts: record a kind:'decision'/operation:'no-dispatch' entry in
  .worklog/downtime-coordination.log when a polled tick is refused by the
  slot-owned or contention gates, or by an actionable dispatch outcome
  (no-candidate/review-queue-hold/code-freeze/audit-in-flight/wl-error/...).
  Identical repeated refusals are rate-limited to one line per 10 minutes
  (DOWNTIME_DECISION_LOG_MIN_INTERVAL_MS, <=6/hour); a reason change logs
  immediately. Expose readonly blockReason (short header token) via
  decisionHeaderToken(). New optional deps.recordDecision for tests.
- worklist.ts: renderDowntimeStatus shows `[downtime held: <token>]` when a
  gate blocked the last tick, distinct from the idle/busy labels.
- Tests: decision-log AC1-AC3, header-token mapping, and header render tests.
…ses don't block free slots

Replace the false "~180 s lease" assumption with the real cross-repo contract.

- downtime-worker.ts: add LOCAL_DISPATCH_LEASE_MAX_SECONDS = 1500 (the
  llm-manager proxy's adaptive local_dispatch_lease_max_seconds default) and
  rewrite the slotOwned comment to state the lease is adaptive and can be held
  for a whole generation (observed 836 s -> 0). Document that the dispatcher
  does not wait out leases; the per-slot gate is what stops a dispatched pane's
  own lease blocking other free unowned slots.
- docs/dev/downtime-dispatcher.md: record the proxy lease contract and the
  cross-repo llm-manager dependency so the assumption cannot drift silently.
- Tests: pin the constant; a max-TTL unknown/operator lease on a count-based
  single slot still refuses (no optimistic expiry); a max-TTL lease held by a
  dispatched pane does not block the free unowned slots (per-slot).
The worklist plugin crashed at startup (prefix+l):

  ReferenceError: Cannot access 'modeSwitchWorker' before initialization
      at Object.config (index.ts:1611)
      at createDowntimeWorker (downtime-worker.ts:4821)
      at main (index.ts:1583)

createDowntimeWorker calls config() SYNCHRONOUSLY during construction (the
durable-disable marker check), but index.ts's config closure referenced the
`modeSwitchWorker` const declared AFTER the downtime worker -> temporal dead
zone. Introduced by 6188a90 (added the mode field) + 88a1445 (moved the
mode-switch worker after the downtime worker).

Fix: read the last-known mode through the existing `modeSwitchHolder`, which is
declared before the downtime worker. It is empty at construction (conservative
undefined -> PARALLELISM=1) and populated immediately after, so later ticks read
the real mode. No TDZ.

Adds packages/herdr/src/plugin-startup.test.ts: an end-to-end startup guard that
runs `npx tsx src/index.ts` and asserts it reaches the TUI without the init
error (verified red before the fix, green after).
…; count-based spare capacity

Fixes the live stall where the llama-proxy serves slots:[] + slots_stale:true
while reporting valid counts.

A1 — stale/empty slots are not per-slot identity:
- LlamaStatus gains slots_stale?: boolean; parseLlamaStatus parses it
  (fail-closed null on a malformed value) and an empty slots array is kept
  but treated as "no per-slot detail", not "zero free slots".
- usablePerSlotSlots() returns null for absent/empty/stale slots, used by
  evaluateIdle and tick() so per-slot mode is never entered on unusable data
  and the count-based path (available_slots) is used instead.

A2 — conservative count-based spare capacity:
- With 0 < N < total and no usable per-slot identity, a held lease no longer
  blocks dispatch into the proxy-reported spare capacity. ownerLeaseCount()
  and leaseReserve() reserve one slot per lease only when the owner may be
  idle (local_active_query !== true), so an idle-but-leased slot counted as
  available is never dispatched onto. Single-slot and N<=0/N>=total keep the
  strict fail-closed gate.
- tick() mirrors the relaxation in the slotOwned gate so the idle gate and the
  ownership gate cannot disagree.

Tests: AC1 (live payload dispatches), AC2 (multi-slot spare + lease), AC3
(single-slot + lease still refuses), AC4 (slots_stale contract + empty/stale
fallback). Verified red before the fix, green after. Docs updated.
…build

downtime-worker.ts referenced the PaneLifecycleKind type in the closePane
dependency signature without importing it, so npm run build in packages/herdr
failed with TS2304 while the root build (which excludes packages/herdr) stayed
green. Add the type import and a regression guard test that type-checks the
herdr project with tsc --noEmit, so an unimported type can no longer ship
unnoticed.
The error messages for --fields invalid-field errors were listing 'id'
among valid fields, but id is always included in output regardless of
request. Users should not need to request it.

Fixes:
- packages/shared/src/fields.ts: filter 'id' from VALID_FIELDS in error
- src/commands/list.ts: same for list command error
- src/commands/search.ts: same for search command error
- packages/shared/src/fields.test.ts: add test verifying id is excluded
- tests/cli/fields-integration.test.ts: update test expectations

Bug reported by manual audit: 'Remove id from the help text provided
when invalid fields are detected.'
Users naturally write `--fields id, title` with a space after the comma.
The shell splits that into separate argv tokens, so Commander assigned only
"id," to --fields and treated the following tokens as positional arguments
(the trailing one silently dropped as an excess argument). The projection
then appeared to return only the first requested field.

normalizeFieldsArgv() merges the continuation tokens back into the --fields
value when (and only when) the accumulated value ends with a comma, so a
complete --fields list followed by a positional search/query term is left
untouched. Applied before Commander parses argv in src/cli.ts and mirrored
in the in-process test harness.

The -f shorthand is shared with --file on export/import/migrate/sync/doctor,
so it is only treated as --fields for list/search.

Tests: unit tests for normalizeFieldsArgv; integration tests for the
space-separated form on both wl list and wl search, including a positional
query after a complete --fields value.
@SorraTheOrc
SorraTheOrc merged commit 56855bc into main Sep 27, 2026
1 of 2 checks passed
@SorraTheOrc
SorraTheOrc deleted the release/dev-to-main-20260927004214 branch September 27, 2026 00:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant