Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions .claude/skills/converge-control/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ Converge has three important layers:
- `tasks/**/TASK.md` — task contracts (id, inputs, outputs, checks).
- `templates/<name>/TASK.md` — runtime spawn templates for dynamic children.
- `skills/<name>/SKILL.md` — **playbook-scoped skills** (the *how* paired with each task's *what*). Also searched at `.claude/skills/` (project-scoped) and `.codex/skills/`.
- `scripts/` — orchestration helpers invoked from task bodies (e.g., to compute `spawn.plan.jsonl` rows).
- `scripts/` — orchestration helpers invoked from task bodies (e.g., to compute `<id>/spawn.yml` invocations from a catalog).
2. **Runtime state** — `.converge/journal/<playbook>/`, `.converge/inventory/<playbook>/`
Execution state, event stream, per-task forensics, and the spawned-task ledger.
3. **Operator surface** — the CLI
Expand Down Expand Up @@ -77,13 +77,17 @@ Exposed as `$CONVERGE_TASK_DIR` to the body. Bodies write evidence there; the fr

| File | What it means |
|---|---|
| `spawn.plan.jsonl` | The spawner/converger body's child manifest. One JSON object per row. Empty file (or missing) = nothing spawned this wave. |
| `spawn.plan.result.jsonl` | Per-row outcome of `converge apply`. `{"ok": true}` per row = clean. Any `{"ok": false}` carries an `errorCode` (e.g., `duplicate-id`, `template-not-found`, `missing-vars`). |
| `spawn/<id>/spawn.yml` | The spawner/converger body's per-child invocation (RFC 0024). Three fields: `template:`, optional `depends_on:`, `params:`. The dir name is the child id. |
| `spawn/<id>/EXPANDED.md` | Framework-rendered template TASK.md with `{{...}}` substituted. Useful when verifying that the body's params produced the intended contract. |
| `spawn/<id>/EVIDENCE.json` | Machine-readable per-child failure detail. Mirrors a row in STATUS.md. |
| `spawn/STATUS.md` | The single AI-facing transparency surface. One `- [x]` / `- [ ]` row per invocation. Failed rows carry a `fix:` block (file + patch). |
| `spawn.plan.jsonl` | (Legacy) the JSONL manifest path. Body authoring rejected with `SPAWN_MANIFEST_AUTHORED_BY_BODY` for new-surface playbooks. Old playbooks that haven't migrated still write here. |
| `spawn.plan.result.jsonl` | (Legacy) per-row outcome of the legacy `converge apply`. |
| `wave.counter` | Current wave number for `mode: converger` tasks. Persists across re-leases so wave state survives crashes. |
| `halt.marker` | The body's explicit "I'm done" signal for a converger. Highest-priority halt signal — overrides `halt_when:` and `wave_check:`. |
| `mode-violation.json` | RFC 0022 contract violation evidence. Read this first when a parent reports `FRONTIER_UNRESOLVED` or refuses to converge. Contains `errorCode`, `declaredMode`, `message`, `fixHint`. |

The directory persists across attempts on purpose — a crashed body's partial manifest survives into the next attempt's repair. See `reference/events.md` for the matching event-stream interpretation (e.g., `SEED_SPAWN`, `FRONTIER_UNRESOLVED`).
The directory persists across attempts on purpose — a crashed body's partial invocations survive into the next attempt's repair. See `reference/events.md` for the matching event-stream interpretation (e.g., `SEED_SPAWN`, `FRONTIER_UNRESOLVED`).

## Primary workflow

Expand Down
8 changes: 4 additions & 4 deletions .claude/skills/converge-control/reference/events.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ A single check passed. Useful when watching a specific node's checks.
```
SEED_SPAWN <parentId> → [<childId>, ...]
```
A `mode: spawner` (or `mode: converger`) parent's `converge apply` call ingested a `spawn.plan.jsonl` manifest, registering child rows in the runtime ledger.
A `mode: spawner` (or `mode: converger`) parent's spawn ingest ran successfully — RFC 0024 `<id>/spawn.yml` invocations (or, for legacy bodies, a `spawn.plan.jsonl` manifest) were expanded against templates and the resulting child rows were registered in the runtime ledger.
→ continue. New nodes will appear in subsequent DAG_LAYER events.

```
Expand Down Expand Up @@ -107,8 +107,8 @@ A dependency cycle was found during compilation. The DAG is invalid.
```
FRONTIER_UNRESOLVED <nodeId>
```
A `mode: spawner` (or `mode: converger`) parent was expected to spawn children but produced no children. Either the body wrote an empty `spawn.plan.jsonl`, or `converge apply` rejected every row (see `spawn.plan.result.jsonl` for per-row `errorCode`), or the body crashed before writing the manifest.
→ inspect `$CONVERGE_TASK_DIR/spawn.plan.{jsonl,result.jsonl}` and `$CONVERGE_TASK_DIR/mode-violation.json` for the contract violation code (e.g. `spawner-missing-manifest`, `spawner-empty-manifest`, `spawner-apply-failed`). Fix the body or the manifest rows; re-run.
A `mode: spawner` (or `mode: converger`) parent was expected to spawn children but produced no children. Either the body wrote no `<id>/spawn.yml` invocations (and no legacy `spawn.plan.jsonl`), or every invocation was rejected during preview (see `$CONVERGE_SPAWN_DIR/STATUS.md` for per-child failure rows with `fix:` blocks), or the body crashed before writing any invocation file.
→ inspect `$CONVERGE_SPAWN_DIR/STATUS.md` (RFC 0024 transparency surface), `$CONVERGE_TASK_DIR/mode-violation.json` (contract violation code e.g. `spawner-missing-manifest`, `spawner-empty-manifest`, `spawner-apply-failed`), and — for unmigrated playbooks — `$CONVERGE_TASK_DIR/spawn.plan.{jsonl,result.jsonl}`. Fix the body or the offending `spawn.yml` files; re-run.

```
INPUT_MISSING <nodeId> <path>
Expand Down Expand Up @@ -156,7 +156,7 @@ Run was interrupted (SIGTERM, process kill). `runstate.json` was saved — resum
| `NODE_COMPLETE cached` | Nothing — it's a cache hit |
| `NODE_FAIL <id>` | Read FEEDBACK.md / LEARN.md, diagnose |
| `CYCLE_DETECTED` | Fix `depends_on` edges, re-compile |
| `FRONTIER_UNRESOLVED` | Read `mode-violation.json` + `spawn.plan.result.jsonl` in `$CONVERGE_TASK_DIR`; fix the manifest or the body |
| `FRONTIER_UNRESOLVED` | Read `STATUS.md` in `$CONVERGE_SPAWN_DIR` and `mode-violation.json` in `$CONVERGE_TASK_DIR`; fix the offending `<id>/spawn.yml` or the body |
| `INPUT_MISSING` | Check upstream node status, fix path |
| `CHECK_FAIL` once then `NODE_COMPLETE` on retry | Self-recovered — continue |
| `CHECK_FAIL` repeating across all attempts | Diagnose — load `troubleshooting/playbook.md` |
Expand Down
27 changes: 15 additions & 12 deletions .claude/skills/converge-control/troubleshooting/playbook.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,27 +122,27 @@ A node can't start because its declared `inputs:` file doesn't exist. The file w
```
NODE_FAIL <spawnerId> spawner-apply-failed
```
A `mode: spawner` body wrote a `spawn.plan.jsonl`, but `converge apply` rejected rows with `errorCode: "template-not-found"` — the referenced template path doesn't exist.
A `mode: spawner` body wrote one or more `<id>/spawn.yml` invocations (or, for legacy bodies, rows in `spawn.plan.jsonl`), but expansion rejected the invocation with `template-not-found` — the named template doesn't exist under `templates/<name>/`.

**Root cause:** When migrating or copying a playbook, template directories were missed.

**Fix recipe:**

1. Read `$CONVERGE_TASK_DIR/spawn.plan.result.jsonl` to see which row's `template:` couldn't be resolved.
1. Read `$CONVERGE_SPAWN_DIR/STATUS.md` (RFC 0024) to see which `- [ ]` row carries `template-not-found` and which file to edit; or, for legacy bodies, `$CONVERGE_TASK_DIR/spawn.plan.result.jsonl`.

2. Locate the missing template in a known-good source:
```bash
find <source-playbook>/templates/ -name 'TASK.md'
find <source-playbook>/templates/ -maxdepth 2 -name 'TASK.md'
```

3. Copy the template tree into the target playbook:
3. Copy the template tree into the target playbook (the directory should contain `TASK.md` + `PARAMS.yml` + optional `EXAMPLES.yml`):
```bash
cp -r <source-playbook>/templates/<name> <target-playbook>/templates/<name>
```

4. Re-run the parent; the spawner body will re-apply the manifest (idempotent for already-applied rows).
4. Re-run the parent; the spawner body will re-emit the invocations (re-runs with byte-identical `spawn.yml` are no-ops).

**Verification:** `spawn.plan.result.jsonl` shows `ok: true` for every row. `SEED_SPAWN` event appears in the stream and the children execute.
**Verification:** `STATUS.md` shows `- [x]` for every row (or `spawn.plan.result.jsonl` shows `ok: true` per row for legacy bodies). `SEED_SPAWN` event appears in the stream and the children execute.

---

Expand Down Expand Up @@ -305,24 +305,27 @@ FRONTIER_UNRESOLVED <nodeId>
```
A `mode: spawner` (or `mode: converger`) parent was expected to spawn children, but the DAG shows zero child nodes. The corresponding `$CONVERGE_TASK_DIR/mode-violation.json` typically reports one of: `spawner-missing-manifest`, `spawner-empty-manifest`, `spawner-row-count`, or `spawner-apply-failed`.

**Root cause:** Either (a) the body didn't write `spawn.plan.jsonl`, (b) the manifest is empty but `spawn.min_children: 1` (or higher) is declared, (c) `converge apply` rejected every row (see `spawn.plan.result.jsonl` for per-row `errorCode`), or (d) the input catalog the body reads is empty/missing.
**Root cause:** Either (a) the body wrote no `<id>/spawn.yml` invocations (and no legacy `spawn.plan.jsonl`), (b) the body wrote zero invocations but `spawn.min_children: 1` (or higher) is declared, (c) every invocation was rejected during preview (template-not-found, missing-required-param, unknown-param, param-type-mismatch — see `STATUS.md` for the per-child `fix:` blocks), or (d) the input catalog the body reads is empty/missing.

**Fix recipe:**

1. Inspect the violation:
```bash
cat "$CONVERGE_TASK_DIR/mode-violation.json"
cat "$CONVERGE_TASK_DIR/spawn.plan.jsonl" 2>/dev/null || echo '(no manifest)'
cat "$CONVERGE_TASK_DIR/spawn.plan.result.jsonl" 2>/dev/null || echo '(no apply result)'
ls "$CONVERGE_SPAWN_DIR"/ 2>/dev/null || echo '(no spawn dir)'
cat "$CONVERGE_SPAWN_DIR/STATUS.md" 2>/dev/null || echo '(no STATUS.md — body wrote nothing under spawn/)'
# Legacy fallback for unmigrated playbooks:
cat "$CONVERGE_TASK_DIR/spawn.plan.jsonl" 2>/dev/null || echo '(no legacy manifest)'
cat "$CONVERGE_TASK_DIR/spawn.plan.result.jsonl" 2>/dev/null || echo '(no legacy apply result)'
```
2. Check whatever the body reads (catalog file, API response, etc.):
```bash
cat <catalog-path> | jq 'length' # or equivalent
```
3. Run the body's command manually (`bash -x <body>`) to see why the manifest isn't produced.
4. Fix the input or the body. The framework will re-apply on the next run; `applyManifest` is idempotent for already-applied rows.
3. Run the body's command manually (`bash -x <body>`) to see why no invocations are produced.
4. Fix the input or the body. The framework will re-apply on the next run; byte-identical `spawn.yml` content is a no-op.

**Verification:** `mode-violation.json` is absent. `spawn.plan.result.jsonl` shows `ok: true` for every row. `SEED_SPAWN` events appear during run showing the expected child count.
**Verification:** `mode-violation.json` is absent. `STATUS.md` shows `- [x]` for every row (or `spawn.plan.result.jsonl` shows `ok: true` per row for legacy bodies). `SEED_SPAWN` events appear during run showing the expected child count.

---

Expand Down
2 changes: 1 addition & 1 deletion .claude/skills/converge-development/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ Full observability surface: **`reference/observability.md`**.
| Stale paths, missing inputs from user playbook | user-shape | wrong skill; route to **`converge-control`** |
| DAG runner crashes / unhandled exception during execution | framework | continue to step 5 |
| Runstate corruption (node status flip-flops, fingerprint mismatch cascade) | framework | continue to step 5 |
| `mode: spawner` apply fails despite valid `spawn.plan.jsonl` | framework | continue to step 5 |
| `mode: spawner` apply fails despite valid `<id>/spawn.yml` invocations (or, for legacy playbooks, a valid `spawn.plan.jsonl`) | framework | continue to step 5 |
| agentfn provider throws on a valid response | framework | continue to step 5 |
| Node retries without progress (same CHECK_FAIL across attempts) | framework | continue to step 5 |
| Fingerprint caching broken (unchanged node re-executed unnecessarily) | framework | continue to step 5 |
Expand Down
25 changes: 16 additions & 9 deletions .claude/skills/converge-development/reference/framework-map.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,18 +125,25 @@ The CLI binary is `packages/cli/dist/index.js`. The runtime entry from the binar
- **Reproduce against:** `examples/test-resume`, examples with spawner children (e.g. `examples/test-seeding`)
- **Watch:** `journal/<playbook>/runstate.json`, `journal/<playbook>/tasks/<taskId>/status.json`

### Task mode dispatch (dynamic child spawning — RFC 0021/0022)
- **Source:** `packages/core/src/task/spawn/apply.ts` — `applyManifest()`; ingests `spawn.plan.jsonl`, upserts `tasks.jsonl`.
- **Dispatch:** `packages/core/src/navigator/core/actions/execution/{run-spawner,run-converger,run-gateway}.ts` — per-mode action handlers; `run-executor.ts` branches on `unit.mode`.
- **Contracts:** `packages/core/src/task/mode/{schema,validator,converger,inference}.ts` — RFC 0022 cross-field validation, post-body validator, wave loop, back-compat inference.
### Task mode dispatch (dynamic child spawning — RFC 0021/0022/0024)
- **Source:** `packages/core/src/task/spawn/`
- `templates.ts` — load `templates/<name>/` (TASK.md + PARAMS.yml + optional EXAMPLES.yml); RFC 0024.
- `discover.ts` — scan `<execDir>/spawn/<id>/spawn.yml` invocations; RFC 0024.
- `expand.ts` — param validation + `{{...}}` interpolation; produces `SpawnRow`s for the legacy applier.
- `strays.ts` — anti-goal locks: `SPAWN_TASKMD_AUTHORED_BY_BODY`, `SPAWN_MANIFEST_AUTHORED_BY_BODY`.
- `status.ts` — `STATUS.md` writer (the single AI-facing transparency surface).
- `ingest.ts` — preview→apply orchestrator stitching the above together.
- `apply.ts` — `applyManifest()`; legacy JSONL ingest, still used as internal IR by the new pipeline.
- **Dispatch:** `packages/core/src/navigator/core/actions/execution/{run-spawner,run-converger,run-gateway}.ts` — per-mode action handlers; `run-executor.ts` branches on `unit.mode`. `run-spawner` runs `ingestSpawnDir()` when `<execDir>/spawn/<id>/spawn.yml` files exist (RFC 0024) and falls back to `applyManifest()` for legacy `spawn.plan.jsonl` bodies.
- **Contracts:** `packages/core/src/task/mode/{schema,validator,converger,inference}.ts` — RFC 0022 cross-field validation, post-body validator (accepts RFC 0024 invocations + legacy manifest + imperative `converge spawn` children), wave loop, back-compat inference.
- **Symptoms:**
- `mode: spawner` body runs but children don't appear (check `spawn.plan.result.jsonl` for `ok:false` rows)
- `mode: spawner` body runs but children don't appear: read `$CONVERGE_SPAWN_DIR/STATUS.md` for per-child `- [ ]` rows with `fix:` blocks, or `spawn.plan.result.jsonl` for legacy bodies.
- Children spawn but parent rollup never fires (the post-task `convergeSpawnerParents` sweep in `run/index.ts`)
- Manifest produces duplicate ids across iterations (`errorCode: "duplicate-id"`)
- Invocations produce duplicate ids across iterations (`errorCode: "duplicate-id"` — delete the child dir to force re-spawn).
- `mode: converger` exceeds `max_waves` without halt (`errorCode: "converger-max-waves"`)
- Migration error: legacy `seed: { mode: cli }` / `seeds:` / `from_seed:` in a TASK.md — parser throws pointing at RFC 0021/0022
- **Reproduce against:** `tests/test-seeding` (basic spawner), `tests/test-queue-pattern` (incremental converger), `tests/test-financial-deep-research` (multi-level)
- **Watch:** `converge list`, `$CONVERGE_TASK_DIR/spawn.plan.{jsonl,result.jsonl}`, `journal/<playbook>/runstate.json`, `inventory/<playbook>/tasks.jsonl`, and `$CONVERGE_TASK_DIR/mode-violation.json` on contract violations.
- Migration error: legacy `seed: { mode: cli }` / `seeds:` / `from_seed:` in a TASK.md — parser throws pointing at RFC 0021/0022/0024.
- **Reproduce against:** `packages/core/tests/spawn/patterns.test.ts` (RFC 0024 reasoning/data/nested), `packages/core/tests/spawn/{templates,discover,expand,status,ingest,strays}.test.ts` (per-module), `tests/test-seeding` (legacy spawner), `tests/test-queue-pattern` (legacy converger), `tests/test-financial-deep-research` (multi-level).
- **Watch:** `converge list`, `$CONVERGE_SPAWN_DIR/STATUS.md` (RFC 0024), `$CONVERGE_TASK_DIR/spawn.plan.{jsonl,result.jsonl}` (legacy), `journal/<playbook>/runstate.json`, `inventory/<playbook>/tasks.jsonl`, and `$CONVERGE_TASK_DIR/mode-violation.json` on contract violations.

### Test infrastructure
- **Source:** `tests/*.test.ts` (vitest, root-level integration tests), `tests/test-*/` (fixture directories), `packages/*/tests/` (per-package unit tests)
Expand Down
10 changes: 8 additions & 2 deletions .claude/skills/converge-development/reference/observability.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,14 @@ All paths are relative to the example directory (e.g. `/Users/minh/Documents/con
│ ├── summary.md human-readable status
│ ├── plan.md plan output (for containers)
│ ├── exec/ $CONVERGE_TASK_DIR — persists across attempts (RFC 0021)
│ │ ├── spawn.plan.jsonl child manifest emitted by mode: spawner / converger body (one JSON row per child)
│ │ ├── spawn.plan.result.jsonl per-row apply outcome — `{"ok":true}` or `{"ok":false,"errorCode":...}`
│ │ ├── spawn/ $CONVERGE_SPAWN_DIR — RFC 0024 invocations (the AI's authoring surface)
│ │ │ ├── STATUS.md single AI-facing transparency surface (one [x]/[ ] row per child + fix: blocks)
│ │ │ └── <childId>/
│ │ │ ├── spawn.yml body-authored invocation (template + depends_on + params)
│ │ │ ├── EXPANDED.md framework-rendered template TASK.md with {{...}} substituted
│ │ │ └── EVIDENCE.json per-child failure detail (machine-readable)
│ │ ├── spawn.plan.jsonl (legacy) child manifest emitted by mode: spawner / converger body (one JSON row per child)
│ │ ├── spawn.plan.result.jsonl (legacy) per-row apply outcome — `{"ok":true}` or `{"ok":false,"errorCode":...}`
│ │ ├── wave.counter current wave number for mode: converger (persists across re-leases)
│ │ ├── halt.marker body's explicit "I'm done" signal for a converger — highest-priority halt
│ │ └── mode-violation.json RFC 0022 contract violation evidence (errorCode + declaredMode + fixHint)
Expand Down
Loading
Loading