docs(skill): the context ladder L0-L4, and pi-task as its low rungs
Boundary discipline explained WHY an inherited transcript defeats a brief but left the reader with no alternative — fork was the only mechanism documented, so "don't fork that" was the only available advice. The ladder makes the volume of inherited context a choice with five named rungs, and records which are real: L0/L1/L2 exist in pi-task (context.facts / .files / .commands), L4 is fork's only behaviour, and L3 (truncated branch) is NOT built by anything. Placed immediately after Boundary discipline because it is the answer to it. States plainly that pi-task is a CLI and will never appear in the tool list — an agent that goes looking for a `pi_task` tool finds nothing and concludes it is unavailable. Also records the trap found while writing this: runner.ts:188 is `if (extensions !== null)`, so extensions:[] turns the capability floor ON and `null` turns it OFF. Since null is documented as "restore normal extension loading", tidying [] to null re-arms palace writes in every fork child. Two honest limits kept next to the feature, not buried: the boundary diff is post-hoc DETECTION not prevention (--no-extensions never touched core read/write/edit/bash), and a fresh L0 context removes narrative failure without removing confabulation — T1's child still filled the deliverable slot from a false premise. description 687 -> 872 chars (limit 1024), so the skill is now reachable when an agent is deciding HOW to delegate, not only whether to fork.
This commit is contained in:
+71
-2
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
name: pi-extensions
|
name: pi-extensions
|
||||||
description: >-
|
description: >-
|
||||||
Use the pi extensions (pi-fork, pi-observational-memory, ssh-controlmaster) effectively in the pi coding agent harness. Load this skill only when running inside pi (detection - `fork` and `recall` are present in your tool list, or `pi --ssh` was used to start the session). pi-fork dispatches focused subtasks to forked agents at fast/balanced/deep effort tiers; pi-observational-memory compacts long sessions into recallable observations + reflections; ssh-controlmaster rewires pi's read/write/edit/bash tools to execute on a remote host over a multiplexed SSH connection. This skill covers tier selection, task design, boundary discipline, when to use recall, and remote-pi mechanics.
|
Use the pi extensions (pi-fork, pi-observational-memory, ssh-controlmaster) effectively in the pi coding agent harness. Load this skill only when running inside pi (detection - `fork` and `recall` are present in your tool list, or `pi --ssh` was used to start the session). pi-fork dispatches focused subtasks to forked agents at fast/balanced/deep effort tiers; pi-observational-memory compacts long sessions into recallable observations + reflections; ssh-controlmaster rewires pi's read/write/edit/bash tools to execute on a remote host over a multiplexed SSH connection. Also covers the context ladder L0-L4 and when to reach for the separate `pi-task` CLI instead of `fork` - isolated child, immutable spec, machine-checked envelope, write-boundary diff. This skill covers tier selection, task design, boundary discipline, when to use recall, and remote-pi mechanics.
|
||||||
---
|
---
|
||||||
|
|
||||||
# Pi Extensions: pi-fork, pi-observational-memory, ssh-controlmaster
|
# Pi Extensions: pi-fork, pi-observational-memory, ssh-controlmaster
|
||||||
@@ -161,6 +161,68 @@ The "three" things it completed were exactly the main thread's pending todos, vi
|
|||||||
- Distrust **quantities** and **provenance claims** in fork prose specifically ("all N sessions", "shipped with the image", "as expected") — those are the slots confabulation fills.
|
- Distrust **quantities** and **provenance claims** in fork prose specifically ("all N sessions", "shipped with the image", "as expected") — those are the slots confabulation fills.
|
||||||
- The fact that the fork was "right anyway" is not the same as the fork having followed instructions.
|
- The fact that the fork was "right anyway" is not the same as the fork having followed instructions.
|
||||||
|
|
||||||
|
### The context ladder — and the second dispatch mechanism (`pi-task`)
|
||||||
|
|
||||||
|
Everything above describes a child that inherits everything. That is not a fixed
|
||||||
|
cost of delegation — **how much context a child gets is a choice**, and `fork`
|
||||||
|
sits at one extreme of it. Five rungs:
|
||||||
|
|
||||||
|
| rung | what the child sees | mechanism | built? |
|
||||||
|
|---|---|---|---|
|
||||||
|
| **L0** | nothing but the goal | `pi-task` default: fresh `--session-id pitask-<id>-<stamp>` in a private `--session-dir` | yes |
|
||||||
|
| **L1** | goal + **names** of files/commands to read itself | `pi-task` spec `context.files` / `context.commands` (`bin/pi-task:154,157`) | yes |
|
||||||
|
| **L2** | goal + an **excerpt the parent curated** | `pi-task` spec `context.facts`, pasted verbatim (`bin/pi-task:151`) | yes |
|
||||||
|
| **L3** | a **truncated tail** of the parent branch | *nothing implements this* — would need a new spec key plus `--session <trimmed snapshot>` | **no** |
|
||||||
|
| **L4** | the **entire** parent branch | `fork(task=…)` — `getHeader()+getBranch()`, no offset or limit anywhere in the call chain | yes |
|
||||||
|
|
||||||
|
**`pi-task` is a CLI, not an extension — it will never appear in your tool list.**
|
||||||
|
Invoke it with `bash`: `/opt/pi-toolkit/bin/pi-task run <spec.json>` (source at
|
||||||
|
`/workspace/pi-toolkit/bin/pi-task`, `schema` subcommand prints the spec fields).
|
||||||
|
It reads an immutable JSON spec, and "inherit the session" is not expressible in
|
||||||
|
that schema — the isolation is structural, not a request.
|
||||||
|
|
||||||
|
**Choose the lowest rung that can do the job:**
|
||||||
|
|
||||||
|
- **`fork` (L4)** when the subtask only makes sense against this conversation,
|
||||||
|
when you want several independent opinions in parallel from one message, or for
|
||||||
|
read-only exploration whose detail you will discard. Everything in "Boundary
|
||||||
|
discipline" above applies in full.
|
||||||
|
- **`pi-task` (L0–L2)** when the brief contains a **prohibition** (the inherited
|
||||||
|
transcript is exactly what overrides those), when you want a **pass/fail**
|
||||||
|
result instead of prose, when you need an **audit trail**, or when writes
|
||||||
|
outside an authorised set must be caught.
|
||||||
|
- **Neither** for trivial work, iterative work (both are one-shot), or judgement
|
||||||
|
that needs context only you have.
|
||||||
|
|
||||||
|
**What `pi-task` gets you that no brief can.** The envelope must parse or the run
|
||||||
|
FAILED, however fluent the prose. `roots[]` is the WATCHED set and
|
||||||
|
`write_allowed` the CHANGEABLE subset, diffed before and after with git
|
||||||
|
`--porcelain --ignored`. That `--ignored` flag is load-bearing: in the T4 test the
|
||||||
|
child obeyed its brief perfectly and still tripped the detector, because
|
||||||
|
`py_compile` wrote `__pycache__` into a watched-but-not-writable root — a
|
||||||
|
gitignored path that plain `--porcelain` reports as clean. Note the structural
|
||||||
|
point that test exposed: under `read_only: true` a write is *defiance*, so a
|
||||||
|
well-behaved child never produces a delta and the detector is never exercised.
|
||||||
|
Splitting WATCHED from WRITABLE is what lets an **obedient** child reveal a
|
||||||
|
violation, which is the realistic hazard.
|
||||||
|
|
||||||
|
**What it does not fix.** `--no-extensions` removes extensions, not the core
|
||||||
|
`read`/`write`/`edit`/`bash` tools — exactly as described above — so the boundary
|
||||||
|
diff is post-hoc **detection, not prevention**. And a fresh L0 context removes the
|
||||||
|
*narrative* failures (parent voice, invented continuity) without removing
|
||||||
|
confabulation: given an under-specified spec built on a false premise, the child
|
||||||
|
still filled the `deliverable` slot with a confident shape. The envelope's own
|
||||||
|
structure creates that pressure. Verify decisive claims from the filesystem
|
||||||
|
regardless of which rung you used.
|
||||||
|
|
||||||
|
**Trap — the capability floor is inverted from intuition.** `runner.ts:188` reads
|
||||||
|
`if (extensions !== null) args.push("--no-extensions")`. So `pi-fork.extensions:
|
||||||
|
[]` passes the flag and the floor is **on**; setting it to `null` — documented in
|
||||||
|
`settings.json` as the way to "restore normal extension loading" — passes nothing
|
||||||
|
and the floor is **off**, restoring palace writes inside every fork child.
|
||||||
|
Changing `[]` to `null` as a tidy-up re-arms what was deliberately disarmed.
|
||||||
|
`pi-task` hardcodes the flag and cannot drift this way.
|
||||||
|
|
||||||
### Anti-patterns
|
### Anti-patterns
|
||||||
|
|
||||||
- **Forking trivial work.** A fork has overhead. If the task takes < 30 seconds in your main thread, just do it.
|
- **Forking trivial work.** A fork has overhead. If the task takes < 30 seconds in your main thread, just do it.
|
||||||
@@ -230,7 +292,7 @@ When entries conflict, **the most recent observation reflects the latest known s
|
|||||||
## Quick Reference
|
## Quick Reference
|
||||||
|
|
||||||
```
|
```
|
||||||
fork(task=..., effort=fast|balanced|deep)
|
fork(task=..., effort=fast|balanced|deep) # L4: child inherits your WHOLE branch
|
||||||
- state decision authority explicitly
|
- state decision authority explicitly
|
||||||
- pass verified context up front
|
- pass verified context up front
|
||||||
- specify deliverable shape
|
- specify deliverable shape
|
||||||
@@ -240,6 +302,13 @@ fork(task=..., effort=fast|balanced|deep)
|
|||||||
- write-capable? demand "What I did NOT do", then verify from git/fs, not the report
|
- write-capable? demand "What I did NOT do", then verify from git/fs, not the report
|
||||||
- prohibition in the brief => not a `fast` task
|
- prohibition in the brief => not a `fast` task
|
||||||
|
|
||||||
|
bash: /opt/pi-toolkit/bin/pi-task run <spec> # L0-L2: isolated child, NOT a tool
|
||||||
|
- schema | selftest | run [--dry-run]
|
||||||
|
- context.facts (pasted) / .files (names only) / .commands
|
||||||
|
- roots[] = WATCHED, write_allowed[] = CHANGEABLE subset
|
||||||
|
- envelope must parse or the run FAILED
|
||||||
|
- audit + cost: ~/.pi/agent/pi-task/<stamp>-<id>/result.json
|
||||||
|
|
||||||
recall(id=<12-char-hex>)
|
recall(id=<12-char-hex>)
|
||||||
- only when stakes justify the cost
|
- only when stakes justify the cost
|
||||||
- id must already be visible in your context
|
- id must already be visible in your context
|
||||||
|
|||||||
Reference in New Issue
Block a user