* fix(minions): honest attempt accounting + cooperative abort-honoring + per-handler timeouts (#1737) - Wall-clock and stall dead-letter paths now increment attempts_made (terminal, no retry — wall-clock fires at 2x cumulative timeout; retrying non-idempotent embed/subagent work would duplicate side effects). Surface stalled_counter in jobs get so 'started 3 / stalled 2 / attempts 0' reads true instead of looking like broken accounting. - Thread AbortSignal through embed-backfill/autopilot-cycle -> runPhaseEmbed -> runEmbedCore -> embedAll(Stale)/embedPage, checking it on BOTH --stale and --all paths and between embed batches. A timed-out embed phase now bails within a batch, so the cycle finally releases gbrain_cycle_locks instead of running the full 10-15 min after the job was killed (the daily cycle-wedge). New shared src/core/abort-check.ts (isAborted/throwIfAborted/anySignal). - Per-handler default wall-clock budget (handler-timeouts.ts) stamped at submit for long handlers without an explicit timeout_ms. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(minions): queue-scoped DB supervisor singleton + canonical pidfile + doctor max-rss check (#1849) - Acquire a queue-scoped DB lock (tryAcquireDbLock, keyed on the raw DB identity + queue) on supervisor.start(): a second supervisor on the same (db, queue) fails fast with exit 2 regardless of $HOME/--pid-file. Refresh on a dedicated timer; on refresh failure past the threshold, fail SAFE (exit non-zero) before the TTL could lapse and let a second supervisor take over. Release on shutdown. - Canonical default pidfile keyed on brain id (currentBrainId, config-only, no DB connect) so two brains under one HOME no longer share supervisor.pid. - doctor: new supervisor_singleton check surfaces the effective --max-rss (from the started audit event) and warns when the lock holder's (host,pid) differs from the local pidfile — comparing host+pid, not bare pid. Registered in doctor-categories. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(agent-voice): topic-aware persona context via server-resolved topicId (#1851) Summon Mars/Venus into a specific conversation topic so they boot already knowing the recent thread. A per-topic call link carries only topicId (+ optional display topicName); the server resolves the recent-conversation context from the brain (topics/<topicId>.md) — topic CONTENT is never accepted over the wire (that would be prompt injection + a leak into URLs/referrers/logs). topicId is a strict slug with a path-traversal guard. New '# Topic Context' prompt slot injected after the persona body so identity-first ordering still wins; persona identity unchanged. No topicId -> generic behavior. Contract doc + persona skill docs updated. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: file #1737 slot-reservation fair-scheduling follow-up TODO (F7) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: bump version and changelog (v0.42.29.0) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: sync KEY_FILES.md for v0.42.29.0 minions + abort wave (#1737, #1849) Fold the #1737/#1849 behavior into the existing per-file entries and add the two new core files, keeping the doc at current-state truth: - queue.ts: honest attempt accounting on wall-clock + stall dead-letter paths; defaultTimeoutMsFor stamping at submit. - supervisor.ts: queue-scoped DB singleton lock (supervisorLockId, classifySupervisorSingleton, LOCK_LOST, refresh-fail-safe, brain-id pidfile, max_rss_mb audit). - worker-registry.ts: currentDbIdentity(). - New entries: src/core/abort-check.ts, src/core/minions/handler-timeouts.ts. - New doctor.ts extension: supervisor_singleton check. - cycle.ts / embed.ts extensions: AbortSignal threading note. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
agent-voice — reference bundle
This directory is a reference, not a runtime gbrain dependency. The gbrain compiled binary does NOT load anything under recipes/agent-voice/code/, recipes/agent-voice/skills/, or recipes/agent-voice/tests/. Those exist to be COPIED into the operator's host agent repo via gbrain integrations install agent-voice --target <host-repo>.
The paradigm
| Aspect | Legacy skillpack (local-managed) |
Reference skillpack (copy-into-host-repo) |
|---|---|---|
| Where code lives | ~/.gbrain/skills/<name>/ |
<host-repo>/services/voice-agent/ |
| Who owns edits | gbrain (managed block) | Operator (host repo) |
| Update path | Overwrite or skip | Diff-and-propose against manifest hashes |
| Resolver registration | ~/.gbrain/skills/RESOLVER.md |
<host-repo>/RESOLVER.md or AGENTS.md |
| Identity of updates | gbrain pushes | Operator pulls per release cadence |
| Bisect / blame / history | gbrain's git history | Operator's host repo git history |
The install_kind discriminator in the recipe frontmatter routes between the two paths inside gbrain integrations install.
Sibling-directory convention (for future copy-into-host-repo recipes)
This recipe pioneers the layout future copy-into-host-repo recipes should follow:
recipes/<name>.md # registered entrypoint (loader sees this)
recipes/<name>/
├── README.md # paradigm doc; gbrain-side only (not copied)
├── package.json # top-of-bundle; copied to <host>/services/<name>/package.json
├── code/ # copied to <host>/services/<name>/code/
├── tests/ # copied to <host>/services/<name>/tests/
│ ├── unit/
│ ├── e2e/
│ └── evals/
├── skills/ # copied to <host>/skills/<skill-name>/
├── install/ # gbrain-side only; install metadata
│ ├── manifest.json # src → target map + per-file SHA-256
│ ├── refresh-algorithm.md # diff-and-propose semantics
│ └── post-install-hint.md # next-step prompts for the install agent
└── docs/ # any extra docs the operator should see post-install
Three rules for new recipes following this shape:
-
Topology preservation. Tests at
recipes/<name>/tests/unit/x.test.mjsMUST import code via../../code/.... That relative path is preserved when copied to<host>/services/<name>/tests/unit/x.test.mjs→<host>/services/<name>/code/.... The installer is NOT in the business of rewriting imports. -
PII guard scope. Every file under
recipes/<name>/is scanned byscripts/check-no-pii-in-agent-voice.sh(or future equivalent), PLUS the top-levelrecipes/<name>.md. The single source of truth for the blocklist is a JSON file inside the bundle. -
install_kind: copy-into-host-repoin frontmatter routes to the copy path. Without it, the recipe defaults tolocal-managed(legacy~/.gbrain/skills/install).
Files (gbrain-side only — NOT copied to host)
README.md— this doc.install/manifest.json— declares src → target paths + permissions; the install command computes SHA-256 at copy time.install/refresh-algorithm.md— the diff-and-propose contract.install/post-install-hint.md— what the install agent prints when copy completes.code/lib/personas/private-name-blocklist.json— privacy guard source of truth (read by the shipped guard script and by host-side prompt-shape tests).code/lib/personas/context-builder.contract.md— API the operator implements for live brain context.
Files (in bundle = code/ + tests/ + skills/ + package.json) — copied to host repo
The install subcommand reads install/manifest.json and copies each listed file to its target path under the host repo. SHA-256s computed at copy time get persisted into <host>/services/<name>/.gbrain-source.json so --refresh can do three-way classification (unchanged-identical / unchanged-stale / locally-modified) without re-walking the entire bundle.
Reading order
If you're new to this paradigm and want to understand the moving parts:
recipes/agent-voice.md— the registered recipe, top-level explainer + install steps.- This file — the paradigm + sibling-directory convention.
install/manifest.json— the literal src → target file map.install/refresh-algorithm.md— what--refreshdoes and how to extend it.code/tools.mjs— the load-bearing trust-boundary code (D14-A read-only allow-list).code/lib/personas/mars.mjs+code/lib/personas/venus.mjs— the persona prompts.code/public/call.html— the WebRTC client with?test=1instrumentation.code/server.mjs— the minimal voice server.