refactor: extract agent bounded context + fix SSE comments + cleanup unused

- Extract src/lib/agent/ bounded context with types, registry, messaging
- Add comments_count to BeadIssue for SSE comment detection
- Create batch endpoints for mail/reservations APIs
- Add memory validation to session-preflight
- Remove unused empty dirs (mockup, sessions, timeline)
- Move stashes to docs/references, gitignore them
This commit is contained in:
zenchantlive 2026-03-04 22:06:40 -08:00
parent 6f41c4af31
commit 18fbafdce4
34 changed files with 62714 additions and 1970 deletions

View file

@ -315,6 +315,8 @@ Stop and correct if you are about to:
Swarm composition, molecule operations, worker dispatch patterns.
- `references/missions-realtime.md`:
Real-time/watcher/event troubleshooting.
- `references/creating-beads.md`:
Creating epics, tasks, subtasks with proper naming, dependencies, and workflow.
## Bottom Line

View file

@ -1,7 +1,31 @@
# Project Driver Template
# project.md — BeadBoard Driver Session Cache
Use this template to create `project.md` at the target repository root.
The first agent in a repo should create this file; later agents must read and update it before work.
This file is maintained by agents. A new agent reads this first.
If the Environment Status table shows all `pass`, skip straight to Step 2 of the runbook.
Only re-run a check if its row says `fail` or `unknown`, or if you hit an actual error.
---
## Environment Status Cache
Last updated: YYYY-MM-DD by `<agent-bead-id>`
| Component | Status | Version / Detail | Verified |
|-----------|--------|-----------------|---------|
| `bd` on PATH | `unknown` | | |
| `bb` on PATH | `unknown` | | |
| `.beads` db exists | `unknown` | | |
| `mail.delegate` configured | `unknown` | | |
| `session-preflight` | `unknown` | | |
| `bb agent` registered | `unknown` | `BB_AGENT=` | |
| Tests last run | `unknown` | | |
**Status values:** `pass` · `fail` · `unknown` · `skip` (not applicable to this project)
**Rule:** If every row is `pass` → skip Step 1 entirely and go straight to Step 2.
If any row is `fail` or `unknown` → run only that check, update this table, continue.
---
## Project Identity
@ -10,68 +34,33 @@ The first agent in a repo should create this file; later agents must read and up
- Primary language/runtime:
- Primary package manager:
## Tooling Baseline (Global Installs)
## Tooling Baseline
Record what is already installed on this machine so later agents do not re-check unnecessarily.
- `bd` installed and on PATH: yes/no
- `bb` or `beadboard` installed and on PATH: yes/no
- Detection commands used (with date):
- Notes on shell/platform quirks (WSL/Windows/macOS/Linux):
- `bd` installed and on PATH: yes/no — version:
- `bb` installed and on PATH: yes/no — version:
- Detection commands used:
- Shell/platform: (e.g. WSL2/bash, macOS/zsh, Windows/PowerShell)
## BeadBoard/Communication Setup
- Mail delegate command configured:
- `bd config set mail.delegate "node <abs-path>/skills/beadboard-driver/scripts/bb-mail-shim.mjs"`
- Agent identity env var policy:
- Preferred: `BB_AGENT=<agent-id>`
- Fallback: `BD_ACTOR=<agent-id>`
- Delegate validation status:
- `node skills/beadboard-driver/scripts/ensure-bb-mail-configured.mjs` pass/fail
- Session preflight status:
- `node skills/beadboard-driver/scripts/session-preflight.mjs` pass/fail
- `.beads` database: exists/created on YYYY-MM-DD via `bd init`
- Mail delegate: `bd config set mail.delegate "node <abs-path>/scripts/bb-mail-shim.mjs"` — configured YYYY-MM-DD
- Agent identity policy: `export BB_AGENT=<role-name>` (set fresh each session in Step 2)
- `session-preflight` last pass: YYYY-MM-DD
## Agent State + Heartbeat Policy
- Agent bead naming convention for this repo:
- Required state transitions (spawning -> running -> working -> stuck/done/stopped):
- Heartbeat cadence during active work (recommended 30-120s):
- Stuck escalation timeout before user ping:
## Swarm / Formula Defaults
- Primary epic/swarm pattern used by this repo:
- Formula/proto id(s) commonly used (if any):
- Preferred swarm command flow (`bd swarm validate/create/status` etc.):
- Agent bead naming: `bb-<role-name>` (e.g. `bb-silver-scribe`)
- Required state transitions: `spawning → running → working → stuck/done/stopped`
- Heartbeat: LLM agents heartbeat at turn start + before long commands; daemon agents every 5 min
## Command Baseline
- Install command:
- Build command:
- Typecheck command:
- Lint command:
- Test command:
- Smoke command (optional):
## Verification Policy Overrides
- Required gates for this project:
- Known slow gates and timeout guidance:
- Evidence format expected in bead notes:
## Scope and Safety
- Forbidden commands/actions for this repo:
- Paths requiring reservation before edits:
- External systems requiring human approval:
- Secret handling guidance:
## Coordination Defaults
- Default handoff style:
- Blocker escalation policy:
- ACK expectations for `HANDOFF`/`BLOCKED`:
- Reservation conflict policy (`--takeover-stale` rules):
- Install:
- Build:
- Typecheck:
- Lint:
- Test:
## Known Workarounds
@ -80,24 +69,12 @@ Document only stable, repeatable workarounds.
1. Trigger:
- Symptom:
- Workaround:
- Verification:
- Owner:
- Verified:
2. Trigger:
- Symptom:
- Workaround:
- Verification:
- Owner:
## Session Log (append-only)
## Session Closeout Checklist
Each agent appends one line when they update this file:
- [ ] Bead status/assignee updated
- [ ] Verification commands executed and recorded
- [ ] Artifacts attached/linked
- [ ] Memory review performed
- [ ] Follow-up beads created (if needed)
- [ ] `project.md` updated with any new environment facts
## Change Log
- YYYY-MM-DD: Initial `project.md` created from template.
| Date | Agent | What changed |
|------|-------|-------------|
| YYYY-MM-DD | `<agent-bead-id>` | Initial project.md created |

View file

@ -44,7 +44,7 @@ Blocked condition:
```bash
bd agent state bb-silver-scribe stuck
bb agent send --from silver-scribe --to cobalt-ridge --bead beadboard-123 --category BLOCKED --subject "Waiting on schema" --body "Need migration direction before continuing."
bd mail send --to cobalt-ridge --bead beadboard-123 --category BLOCKED --subject "Waiting on schema" --body "Need migration direction before continuing."
```
Work completion:
@ -74,14 +74,16 @@ Use `bd agent heartbeat <agent-bead-id>` to refresh `last_activity` without chan
bd agent heartbeat bb-silver-scribe
```
When to heartbeat:
- At least once every 5-10 minutes during long-running work
**Daemon agents (persistent processes):**
- Normal work: every 5 minutes
- High-risk long operations: every 2-3 minutes
- Immediately before long test/build phases
- Immediately after recovering from interruptions
Recommended cadence:
- Normal work: every 5 minutes
- High-risk long operations: every 2-3 minutes
**LLM agents (Claude Code, turn-based):**
- At turn start (when picking up work)
- Immediately before long-running commands
- Inter-turn silence is expected and not a health signal
## Witness Death Timeout
@ -95,6 +97,8 @@ Operational interpretation:
Agent-side rule:
- If you are alive and still executing, heartbeat before anyone has to guess.
> **Current status:** The Witness enforcement layer is not yet running. Heartbeats are recorded in `last_activity` and visible in the BeadBoard dashboard but are not currently auto-enforced. Agents will not be auto-marked `dead`. Daemon implementation is a future epic.
## Slot Operations (Current Work Attachment)
The `hook` slot links an agent bead to the active task bead.
@ -125,7 +129,7 @@ Important slot constraints:
When blocked:
1. Set state to stuck (`bd agent state ... stuck`)
2. Send explicit BLOCKED coordination event (`bb agent send --category BLOCKED ...`)
2. Send explicit BLOCKED coordination event (`bd mail send --category BLOCKED ...`)
3. Keep heartbeat active while waiting
4. Resume with `running`/`working` once unblocked

View file

@ -8,8 +8,12 @@ Day-to-day runbooks use `bd mail` delegation rather than direct low-level agent
- `node skills/beadboard-driver/scripts/session-preflight.mjs`
- `node skills/beadboard-driver/scripts/ensure-bb-mail-configured.mjs`
- `bd create --title="Agent: <role-name>" --description="<agent scope>" --type=task --priority=0 --label="gt:agent,role:<orchestrator|ui|graph|backend|infra>"`
- `bd agent state <agent-bead-id> spawning`
- `bd agent state <agent-bead-id> running`
- `bd agent state <agent-bead-id> spawning` — agent bead created, environment not yet verified
- `bd agent state <agent-bead-id> running` — environment verified, ready to claim work
- `bd agent state <agent-bead-id> working` — work bead claimed, actively executing
- `bd agent state <agent-bead-id> stuck` — blocked, waiting on intervention or response
- `bd agent state <agent-bead-id> done` — work bead closed, all deliverables complete
- `bd agent state <agent-bead-id> stopped` — session ending cleanly
- `bd agent heartbeat <agent-bead-id>`
- `bd agent show <agent-bead-id>`
@ -75,7 +79,7 @@ Delegate setup and validation:
## Environment and Repair Helpers
- `node skills/beadboard-driver/scripts/resolve-bb.mjs`
- `node skills/beadboard-driver/scripts/readiness-report.mjs --checks <json> --artifacts <json>`
- `node skills/beadboard-driver/scripts/diagnose-env.mjs`
- `node skills/beadboard-driver/scripts/heal-common-issues.mjs [--project-root <path>] [--apply] [--fix-git-index-lock]`
- `node {baseDir}/scripts/setup-mail-delegate.mjs` — configure mail.delegate (self-resolves shim path)
- `node {baseDir}/scripts/readiness-report.mjs --checks <json> --artifacts <json>`
- `node {baseDir}/scripts/diagnose-env.mjs`
- `node {baseDir}/scripts/heal-common-issues.mjs [--project-root <path>] [--apply] [--fix-git-index-lock]`

View file

@ -0,0 +1,286 @@
# Creating and Managing Beads
Complete guide for agents on bead creation, naming, dependencies, and workflow.
---
## Bead Naming Format (CRITICAL)
| Level | Format | Example |
|-------|--------|---------|
| **Epic** | `beadboard-<id>` | `beadboard-abc` |
| **Task** | `beadboard-<epic>.x` | `beadboard-abc.1` |
| **Subtask** | `beadboard-<epic>.x.x` | `beadboard-abc.1.2` |
**Rules:**
- `<id>` is auto-generated by `bd create` (alphanumeric, 2-4 chars)
- Task numbers increment sequentially under epic
- Subtask numbers increment under parent task
- **Never skip levels**: epic → task → subtask
---
## Creating Epics
Epics represent high-level features or initiatives.
```bash
bd create \
--title="[EPIC] User Authentication System" \
--description="Implement secure user login/registration with OAuth support" \
--type=epic \
--priority=2 \
--label="feature,auth"
```
**Output:** `Created beadboard-xyz (epic)`
---
## Creating Tasks Under Epics
Tasks are actionable work units under an epic.
```bash
# Task naming convention: epic.1: Description
bd create \
--title="xyz.1: Implement login form UI" \
--description="Build responsive login form with email/password fields" \
--type=task \
--priority=2 \
--parent=beadboard-xyz
```
**Output:** `Created beadboard-xyz.1 (task)`
### Creating Subtasks
```bash
# Subtask naming: epic.task.subtask: Description
bd create \
--title="xyz.1.1: Add form validation" \
--description="Implement client-side validation for email format" \
--type=task \
--priority=1 \
--parent=beadboard-xyz.1
```
---
## Setting Dependencies
### Blocker Dependencies (Execution Order)
Use when one bead must complete before another starts:
```bash
# xyz.1 cannot start until xyz is done
bd dep add beadboard-xyz.1 beadboard-xyz
# xyz.1.2 is blocked by xyz.1.1
bd dep add beadboard-xyz.1.2 beadboard-xyz.1.1
```
### Parent-Child Relationships
Use for semantic grouping (non-blocking):
```bash
# Link child to parent epic
bd dep relate beadboard-xyz beadboard-xyz.1
# Link subtask to task
bd dep relate beadboard-xyz.1 beadboard-xyz.1.1
```
### Viewing Dependencies
```bash
bd dep list beadboard-xyz # Show all dependencies
bd ready # Show unblocked, ready-to-start beads
```
---
## Writing Descriptions
Every bead description MUST include:
```markdown
**Scope:**
- Specific thing to implement
- Another specific requirement
**Out of Scope:**
- Things explicitly not included
- Future work to avoid
**Success Criteria:**
1. Specific measurable outcome
2. Test passes
3. Evidence of completion
```
### Example Description
```bash
bd create --title="xyz.2: API endpoint" --description="Scope:
- POST /api/login endpoint
- JWT token generation
- Rate limiting (5 req/min)
Out of Scope:
- OAuth providers (in xyz.3)
- Password reset flow
Success Criteria:
1. Endpoint returns 200 with valid JWT
2. Returns 401 for invalid credentials
3. Rate limit enforced" --type=task --priority=2 --parent=beadboard-xyz
```
---
## Closing Beads
### Step 1: Update with Evidence
```bash
bd update beadboard-xyz.1 \
--notes "Tests pass: npm test -- --grep 'login'
Files changed:
- src/components/LoginForm.tsx
- src/lib/validation.ts
Coverage: 94%"
```
### Step 2: Close
```bash
bd close beadboard-xyz.1 --reason "Login form implemented, tested, merged to main"
```
**Rule:** Update first, then close. `bd close` does not accept `--notes`.
---
## Complete Example: End-to-End Workflow
### 1. Create Epic
```bash
$ bd create --title="[EPIC] Payment Integration" --description="Add Stripe payment processing" --type=epic --priority=1 --label="feature,payments"
Created beadboard-pmt (epic)
```
### 2. Create Tasks
```bash
$ bd create --title="pmt.1: Stripe account setup" --description="Scope:
- Create Stripe dev account
- Configure webhook endpoint
- Add API keys to vault
Out of Scope:
- Production account (separate epic)
Success Criteria:
1. Test transactions work in sandbox
2. Webhook receives events" --type=task --priority=1 --parent=beadboard-pmt
Created beadboard-pmt.1 (task)
$ bd create --title="pmt.2: Checkout UI" --description="Scope:
- Payment form component
- Card element integration
- Error handling display
Success Criteria:
1. Form submits to Stripe
2. Shows success/failure states" --type=task --priority=2 --parent=beadboard-pmt
Created beadboard-pmt.2 (task)
```
### 3. Set Dependencies
```bash
# pmt.2 blocked by pmt.1 (need Stripe setup first)
$ bd dep add beadboard-pmt.2 beadboard-pmt.1
Added dependency: beadboard-pmt.2 -> beadboard-pmt.1
```
### 4. Check Ready Status
```bash
$ bd ready
beadboard-pmt.1 [task] pmt.1: Stripe account setup
```
Only `pmt.1` is ready—`pmt.2` is blocked.
### 5. Create Subtask
```bash
$ bd create --title="pmt.1.1: Webhook handler" --description="Scope:
- POST /webhooks/stripe endpoint
- Verify Stripe signature
- Update order status
Success Criteria:
1. Signature verification passes
2. Order updated correctly" --type=task --priority=1 --parent=beadboard-pmt.1
Created beadboard-pmt.1.1 (task)
```
### 6. Work and Close
```bash
# After completing pmt.1.1
$ bd update beadboard-pmt.1.1 --notes "Webhook handler implemented
- src/app/api/webhooks/stripe/route.ts
- Tests: npm test webhook.test.ts (3 passing)"
$ bd close beadboard-pmt.1.1 --reason "Webhook handler complete"
# After completing pmt.1
$ bd update beadboard-pmt.1 --notes "Stripe integration complete
- Sandbox account: acct_xxx
- Webhook: /api/webhooks/stripe
- All tests passing"
$ bd close beadboard-pmt.1 --reason "Stripe account setup finished"
# Now pmt.2 becomes unblocked
$ bd ready
beadboard-pmt.2 [task] pmt.2: Checkout UI
```
---
## Quick Reference
| Command | Purpose |
|---------|---------|
| `bd create --title="..." --type=epic` | Create epic |
| `bd create --title="epic.N: ..." --parent=beadboard-xxx` | Create task |
| `bd dep add <blocked> <blocker>` | Set blocker dependency |
| `bd dep relate <parent> <child>` | Set semantic relationship |
| `bd ready` | List unblocked beads |
| `bd update <id> --notes "..."` | Add evidence |
| `bd close <id> --reason "..."` | Complete bead |
---
## Common Mistakes
1. **Wrong naming**: `task-1` instead of `beadboard-abc.1`
2. **Missing parent**: Tasks must have `--parent=beadboard-<epic>`
3. **Closing before updating**: Always `update` first, then `close`
4. **Wrong dep direction**: `bd dep add <blocked> <blocker>` (blocked first!)
5. **Skipping evidence**: Always include command output in `--notes`

View file

@ -13,12 +13,12 @@ This document tracks high-impact coordination and environment failures for the B
## `BB_NOT_FOUND`
- Signal: `resolve-bb.mjs` or `bb-mail-shim.mjs` reports bb command missing.
- Signal: `session-preflight.mjs` or `bb-mail-shim.mjs` reports bb command missing.
- Cause: global BeadBoard CLI not installed, or not discoverable.
- Recovery:
- Install BeadBoard globally (`bb`/`beadboard` on `PATH`).
- Re-run `node skills/beadboard-driver/scripts/resolve-bb.mjs`.
- Re-run preflight.
- Install BeadBoard globally (`bb`/`beadboard` on `PATH`) — see Bootstrap Step C in SKILL.md.
- Run `node {baseDir}/scripts/setup-mail-delegate.mjs` to reconfigure the mail delegate after `bb` is installed.
- Re-run preflight: `node {baseDir}/scripts/session-preflight.mjs`.
## `MAIL_DELEGATE_MISSING` / `BD_MAIL_DELEGATE_NOT_SET`
@ -33,10 +33,12 @@ This document tracks high-impact coordination and environment failures for the B
- Signal: `ensure-bb-mail-configured.mjs` fails contract checks.
- Cause: delegate points to wrong command, missing shim path, or invalid `BB_AGENT` context.
- Recovery:
- Run session preflight to re-apply expected delegate command.
- Set `BB_AGENT` explicitly.
- Validate with `node skills/beadboard-driver/scripts/ensure-bb-mail-configured.mjs`.
- Recovery (in order):
1. Check delegate is set: `bd config get mail.delegate`
2. Verify shim path: the path shown must be absolute and the `bb-mail-shim.mjs` file must exist on disk
3. Reconfigure if wrong/missing: `node {baseDir}/scripts/setup-mail-delegate.mjs`
4. Verify `BB_AGENT` is set: `echo $BB_AGENT` (must be non-empty)
5. Re-run verification: `node {baseDir}/scripts/ensure-bb-mail-configured.mjs` — expected: `ok: true`
## `DOLT_NOT_RUNNING`
@ -72,4 +74,4 @@ This document tracks high-impact coordination and environment failures for the B
- Do not write `.beads/issues.jsonl` directly.
- Do not close beads without fresh evidence.
- Do not bypass invalid `BB_REPO` values; fix configuration first.
- Do not bypass a misconfigured mail delegate; fix configuration with `{baseDir}/scripts/setup-mail-delegate.mjs` first.

View file

@ -7,8 +7,7 @@ This runbook is the minimum lifecycle contract for agents using BeadBoard Driver
1. Run preflight and discovery checks:
```bash
node skills/beadboard-driver/scripts/session-preflight.mjs
node skills/beadboard-driver/scripts/resolve-bb.mjs
node {baseDir}/scripts/session-preflight.mjs
```
2. Create or identify your agent bead first (required before claiming work):
@ -27,7 +26,8 @@ bd agent state <agent-bead-id> running
4. Query hard memory for your domain before claim:
```bash
bd query "label=memory AND label=mem-canonical AND label=mem-hard AND status=closed"
# Select domain: memory-arch | memory-workflow | memory-agent | memory-ux | memory-reliability
bd query "label=memory AND label=mem-canonical AND label=<domain> AND status=closed" --sort updated --reverse
```
## 2) Discover Work and Read Epic Context
@ -100,7 +100,7 @@ bd agent state <agent-bead-id> stuck
2. Coordination signal:
```bash
bb agent send --from <agent-name> --to <target-agent-or-role> --bead <bead-id> --category BLOCKED --subject "<blocker summary>" --body "<what is needed>"
bd mail send --to <target-agent-or-role> --bead <bead-id> --category BLOCKED --subject "<blocker summary>" --body "<what is needed>"
```
3. Keep heartbeat while waiting:

View file

@ -40,6 +40,38 @@ function configureMailDelegate(bdPath, shimPath) {
};
}
function validateMemorySystem(bdPath) {
try {
const result = spawnSync(bdPath, ['query', 'label=mem-canonical,status=closed', '--limit', '5'], {
stdio: 'pipe',
shell: false,
});
if (result.status !== 0) {
return {
validated: false,
reason: 'Failed to query memory system',
memories_found: 0,
};
}
const output = result.stdout?.toString() || '';
const memoryCount = (output.match(/beadboard-/g) || []).length;
return {
validated: true,
memories_found: memoryCount,
note: 'Remember to read memory beads at session start: bd show beadboard-116 beadboard-60a beadboard-zas',
};
} catch (error) {
return {
validated: false,
reason: error instanceof Error ? error.message : String(error),
memories_found: 0,
};
}
}
async function main() {
const shimPath = join(__dirname, 'bb-mail-shim.mjs');
@ -54,13 +86,14 @@ async function main() {
reason: 'Could not find bd in PATH.',
remediation:
process.platform === 'win32'
? 'Primary: npm i -g beadboard. Fallback: powershell -ExecutionPolicy Bypass -File .\\install\\install.ps1. Then ensure bd is available in PATH.'
? 'Primary: npm i -g beadboard. Fallback: powershell -ExecutionPolicy Bypass -File ./install/install.ps1. Then ensure bd is available in PATH.'
: 'Primary: npm i -g beadboard. Fallback: bash ./install/install.sh. Then ensure bd is available in PATH.',
tools: {
bd: { available: false, path: null },
},
bb: null,
mail: null,
memory: null,
},
null,
2,
@ -86,6 +119,7 @@ async function main() {
configured: false,
reason: 'bb not available — mail delegate requires bb agent commands',
},
memory: null,
},
null,
2,
@ -95,6 +129,7 @@ async function main() {
}
const mail = configureMailDelegate(bdPath, shimPath);
const memory = validateMemorySystem(bdPath);
process.stdout.write(
`${JSON.stringify(
@ -106,6 +141,7 @@ async function main() {
},
bb,
mail,
memory,
},
null,
2,
@ -124,6 +160,7 @@ async function main() {
},
bb: null,
mail: null,
memory: null,
},
null,
2,

View file

@ -0,0 +1,95 @@
#!/usr/bin/env node
/**
* setup-mail-delegate.mjs
*
* Configures bd's mail.delegate to point at the bb-mail-shim.mjs bundled
* alongside this script. Uses import.meta.url to resolve the absolute path
* so the caller never needs to know where the skill is installed.
*
* Usage: node {baseDir}/scripts/setup-mail-delegate.mjs
* Output: JSON { ok, configured, delegate } or { ok, error_code, reason }
*/
import { spawnSync } from 'node:child_process';
import { fileURLToPath } from 'node:url';
import { dirname, join } from 'node:path';
import { findCommandInPath } from './lib/driver-lib.mjs';
const __dirname = dirname(fileURLToPath(import.meta.url));
const shimPath = join(__dirname, 'bb-mail-shim.mjs');
const delegateCommand = `node ${shimPath}`;
async function main() {
const dryRun = process.argv.includes('--dry-run');
const bdPath = await findCommandInPath('bd');
if (!bdPath) {
process.stdout.write(
`${JSON.stringify(
{
ok: false,
error_code: 'BD_NOT_FOUND',
reason: 'Could not find bd in PATH. Install with: npm install -g beads-cli',
delegate: null,
},
null,
2,
)}\n`,
);
return;
}
if (dryRun) {
process.stdout.write(
`${JSON.stringify(
{
ok: true,
dry_run: true,
configured: false,
delegate: delegateCommand,
},
null,
2,
)}\n`,
);
return;
}
const result = spawnSync(bdPath, ['config', 'set', 'mail.delegate', delegateCommand], {
stdio: 'pipe',
shell: false,
});
if (result.status !== 0) {
const stderr = result.stderr?.toString().trim() || '';
process.stdout.write(
`${JSON.stringify(
{
ok: false,
error_code: 'BD_CONFIG_FAILED',
reason: stderr || 'bd config set mail.delegate exited non-zero.',
delegate: delegateCommand,
},
null,
2,
)}\n`,
);
return;
}
process.stdout.write(
`${JSON.stringify(
{
ok: true,
configured: true,
delegate: delegateCommand,
},
null,
2,
)}\n`,
);
}
void main();

View file

@ -21,7 +21,7 @@ test('ensure-project-context creates project.md when missing', async () => {
const content = await fs.readFile(path.join(root, 'project.md'), 'utf8');
assert.equal(result.ok, true);
assert.equal(result.created, true);
assert.match(content, /Project Driver Template/);
assert.match(content, /Environment Status Cache/);
} finally {
await fs.rm(root, { recursive: true, force: true });
}

View file

@ -17,6 +17,7 @@ const tests = [
path.join(__dirname, 'diagnose-env.contract.test.mjs'),
path.join(__dirname, 'heal-common-issues.contract.test.mjs'),
path.join(__dirname, 'ensure-project-context.contract.test.mjs'),
path.join(__dirname, 'setup-mail-delegate.contract.test.mjs'),
];
const child = spawn(process.execPath, ['--test', ...tests], {

View file

@ -0,0 +1,67 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
import { execFile } from 'node:child_process';
import { promisify } from 'node:util';
import { fileURLToPath } from 'node:url';
import { dirname } from 'node:path';
const execFileAsync = promisify(execFile);
const __dirname = dirname(fileURLToPath(import.meta.url));
const scriptPath = path.resolve('skills/beadboard-driver/scripts/setup-mail-delegate.mjs');
const expectedShimPath = path.join(__dirname, '..', 'scripts', 'bb-mail-shim.mjs');
async function withTempDir(run) {
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'bb-skill-setupdelegate-'));
try {
await run(root);
} finally {
await fs.rm(root, { recursive: true, force: true });
}
}
test('setup-mail-delegate contract: dry-run resolves absolute shim path', async () => {
await withTempDir(async (root) => {
const { stdout } = await execFileAsync(process.execPath, [scriptPath, '--dry-run'], {
cwd: root,
env: { ...process.env },
});
const result = JSON.parse(stdout);
assert.equal(result.ok, true);
assert.equal(result.dry_run, true);
assert.ok(
result.delegate.includes('bb-mail-shim.mjs'),
`delegate should reference bb-mail-shim.mjs, got: ${result.delegate}`,
);
const resolvedPath = result.delegate.replace(/^node\s+/, '');
assert.ok(
path.isAbsolute(resolvedPath),
`delegate path should be absolute, got: ${resolvedPath}`,
);
assert.equal(
resolvedPath,
expectedShimPath,
`delegate should point to the bundled shim`,
);
});
});
test('setup-mail-delegate contract: BD_NOT_FOUND when bd missing', async () => {
await withTempDir(async (root) => {
const { stdout } = await execFileAsync(process.execPath, [scriptPath], {
cwd: root,
env: {
...process.env,
PATH: '/nonexistent',
},
});
const result = JSON.parse(stdout);
assert.equal(result.ok, false);
assert.equal(result.error_code, 'BD_NOT_FOUND');
});
});