skill floor: refresh vendored pi-extensions skill to pi-extensions@25c1265 (task tool + fork-gate)
check-skill-floor.sh: OK, tree_sha256 9b85a633… matches the package at main (25c1265). Forces one base rebuild (base_tag hashes rootfs/), which is also what bakes extensions/task.ts and fork-gate.ts into /opt/pi-extensions via the floating PI_EXTENSIONS_REF=main; install.sh symlinks every extensions/*.ts on start.
This commit is contained in:
@@ -1,17 +1,17 @@
|
|||||||
---
|
---
|
||||||
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. 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.
|
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 the `task` tool (pi-task: isolated child, immutable spec, machine-checked envelope, write-boundary diff) that is the DEFAULT for delegated work, with `fork` reserved for read-only exploration and parallel opinions, plus the `fork-gate` hook that enforces the split. This skill covers rung and 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 + task/fork-gate, pi-observational-memory, ssh-controlmaster
|
||||||
|
|
||||||
## When to Load This Skill
|
## When to Load This Skill
|
||||||
|
|
||||||
Load only when **both** of these are true:
|
Load only when **both** of these are true:
|
||||||
|
|
||||||
1. You are running inside the **pi coding agent harness** (not Claude Code, not opencode, not any other harness).
|
1. You are running inside the **pi coding agent harness** (not Claude Code, not opencode, not any other harness).
|
||||||
2. The `fork` and/or `recall` tools appear in your available tool list, **or** the session was started with `pi --ssh ...`.
|
2. The `fork`, `task` and/or `recall` tools appear in your available tool list, **or** the session was started with `pi --ssh ...`.
|
||||||
|
|
||||||
If you do not see those tools, this skill does not apply — skip it. Other harnesses do not have these extensions and the patterns below will not work there.
|
If you do not see those tools, this skill does not apply — skip it. Other harnesses do not have these extensions and the patterns below will not work there.
|
||||||
|
|
||||||
@@ -71,7 +71,78 @@ ssh-controlmaster is orthogonal but composes cleanly: when pi is operating remot
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Part 1: pi-fork
|
## Part 1: delegating work — `task` and `fork`
|
||||||
|
|
||||||
|
### Decide the rung BEFORE the brief (read this first)
|
||||||
|
|
||||||
|
Two tools run a child agent. They differ in one thing, and it decides the
|
||||||
|
quality of what comes back: **what the child sees.**
|
||||||
|
|
||||||
|
| tool | child sees | rung | gives you | use for |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| **`task`** (pi-extensions `task.ts`, wraps `pi-task`) | **only your spec** — goal, named files, curated facts | L0–L2 | immutable spec, PASS/FAIL envelope, per-root boundary diff, audit dir, budgets | **any delegated work that changes files or must obey a rule** — the default |
|
||||||
|
| **`fork`** (pi-fork) | **your entire branch**, brief appended last | L4 | prose report, effort tiers, parallel dispatch from one message | read-only exploration that needs this conversation; N independent opinions |
|
||||||
|
|
||||||
|
**Pre-flight before any `fork(...)` — one *yes* makes it a `task`:**
|
||||||
|
1. Does the brief say *do not / only / never / must not*?
|
||||||
|
2. Will the child write, edit, commit or push anything?
|
||||||
|
3. Do I want a PASS/FAIL I can check, rather than prose?
|
||||||
|
|
||||||
|
Why the text alone did not work (and why this is now enforced): the rule above
|
||||||
|
lived in this skill and in the global AGENTS.md for months and was still
|
||||||
|
violated by agents that had just read it — five fork briefs in one session on
|
||||||
|
2026-09-17, all carrying "do not", one of which returned confident verbatim
|
||||||
|
quotes that did not exist. `fork` is a *tool*: its self-recommending
|
||||||
|
description ("implementation, testing, review…") is in the tool list every turn
|
||||||
|
and survives compaction; this skill is gone after the first compaction, and
|
||||||
|
`pi-task` was a CLI to be remembered. Two structural fixes shipped 2026-09-19:
|
||||||
|
|
||||||
|
- **`task` is a tool** (`extensions/task.ts`), so both rungs sit in the tool
|
||||||
|
list with the decision rule in their descriptions, and `promptGuidelines`
|
||||||
|
puts the rule in the system prompt where compaction cannot remove it. It
|
||||||
|
rejects, before spending a model run, the two spec errors that make a
|
||||||
|
violation certain (see "roots" below) and serialises overlapping tasks.
|
||||||
|
- **`fork-gate`** (`extensions/fork-gate.ts`) is a `tool_call` hook that BLOCKS
|
||||||
|
a fork whose brief contains a prohibition, a write boundary or a clause-initial
|
||||||
|
file-changing imperative, and returns the `task(...)` call to make instead.
|
||||||
|
It matches wording, not intent: a genuinely read-only exploration brief that
|
||||||
|
trips it is rephrased, and one that cannot be rephrased without its
|
||||||
|
prohibition needed `task` all along. `PI_FORK_GATE=off` logs instead of
|
||||||
|
blocking; `/ext` disables it entirely.
|
||||||
|
|
||||||
|
Minimal call:
|
||||||
|
|
||||||
|
```
|
||||||
|
task(id="slug", goal="…verbatim; the child has NO other context…",
|
||||||
|
deliverable="…exact shape wanted…", effort="fast|balanced|deep",
|
||||||
|
read_only=false,
|
||||||
|
roots=["/abs/repo/docs", "/abs/repo/src"], # WATCHED, each diffed alone
|
||||||
|
write_allowed=["/abs/repo/docs"], # exact subset of roots
|
||||||
|
facts=["verified fact"], files=["/abs/path/to/read"])
|
||||||
|
```
|
||||||
|
|
||||||
|
**Roots — the two errors the tool refuses up front.** Every root is diffed on
|
||||||
|
its own and a delta is allowed only if that *exact root string* is in
|
||||||
|
`write_allowed`. So (a) `write_allowed` must be a subset of `roots`, not a
|
||||||
|
subdirectory of one, and (b) a writable root must not lie inside a watched-only
|
||||||
|
root — the parent's porcelain would change and register a violation every time.
|
||||||
|
List the writable part as its own root and leave the enclosing repo out. This is
|
||||||
|
the shape the 2026-09-17 migration tasks used (sibling roots, `write_allowed`
|
||||||
|
naming four of them) and it passed cleanly.
|
||||||
|
|
||||||
|
**Overlap.** Sibling `task` calls whose roots overlap would see each other's
|
||||||
|
writes as violations; the tool runs them one after another automatically. Do
|
||||||
|
not rely on that for ordering *semantics* — if B needs A's output, call B after
|
||||||
|
A returns.
|
||||||
|
|
||||||
|
**What isolation does not fix.** L0 removes the *narrative* failures (parent
|
||||||
|
voice, invented continuity, ignored prohibitions). It does not remove
|
||||||
|
confabulation: an under-specified spec still gets a confident deliverable. The
|
||||||
|
report prints the evidence pointers under a "SPOT-CHECK THESE" heading for a
|
||||||
|
reason.
|
||||||
|
|
||||||
|
Everything below about tiers, brief design and boundary discipline applies to
|
||||||
|
**both** tools — a `task` spec is a brief too.
|
||||||
|
|
||||||
### Effort tier mapping
|
### Effort tier mapping
|
||||||
|
|
||||||
@@ -85,15 +156,15 @@ Configured in `~/.pi/agent/settings.json` under `pi-fork.effortProfiles`. The co
|
|||||||
|
|
||||||
**Rule of thumb:** start at `balanced` unless you have a specific reason to go up or down. Going too cheap on a deep task wastes a fork; going too expensive on a mechanical task is just slow.
|
**Rule of thumb:** start at `balanced` unless you have a specific reason to go up or down. Going too cheap on a deep task wastes a fork; going too expensive on a mechanical task is just slow.
|
||||||
|
|
||||||
### When to fork vs. do it yourself
|
### When to delegate vs. do it yourself
|
||||||
|
|
||||||
Fork when **any** of:
|
Having chosen the rung above, delegate (either tool) when **any** of:
|
||||||
- The task requires reading many files whose contents you don't need to keep in your main context afterwards (the fork returns a dense summary; raw file contents stay in the fork's context and are discarded).
|
- The task requires reading many files whose contents you don't need to keep in your main context afterwards (the fork returns a dense summary; raw file contents stay in the fork's context and are discarded).
|
||||||
- You want to run multiple analyses in **parallel** (especially: comparing N options, where independent reasoning is itself a signal — see "parallel forks" below).
|
- You want to run multiple analyses in **parallel** (especially: comparing N options, where independent reasoning is itself a signal — see "parallel forks" below).
|
||||||
- The task is well-scoped enough to specify completely up front and well-bounded enough that returning a dense report is more useful than continuing the dialogue.
|
- The task is well-scoped enough to specify completely up front and well-bounded enough that returning a dense report is more useful than continuing the dialogue.
|
||||||
- You are about to do something that would burn a lot of tokens on tool calls (long file reads, many bash invocations) whose output you will mostly discard.
|
- You are about to do something that would burn a lot of tokens on tool calls (long file reads, many bash invocations) whose output you will mostly discard.
|
||||||
|
|
||||||
Don't fork when:
|
Don't delegate when:
|
||||||
- The work fits in your current context budget without crowding out what comes next.
|
- The work fits in your current context budget without crowding out what comes next.
|
||||||
- The task is exploratory and you'll need to iterate based on what you find (forking turns iteration into round-trips with full task-spec rewrites).
|
- The task is exploratory and you'll need to iterate based on what you find (forking turns iteration into round-trips with full task-spec rewrites).
|
||||||
- You need to make decisions during the work that depend on context only the main thread has.
|
- You need to make decisions during the work that depend on context only the main thread has.
|
||||||
@@ -175,11 +246,12 @@ sits at one extreme of it. Five rungs:
|
|||||||
| **L3** | a **truncated tail** of the parent branch | *nothing implements this* — would need a new spec key plus `--session <trimmed snapshot>` | **no** |
|
| **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 |
|
| **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.**
|
**`pi-task` is a CLI (`/opt/pi-toolkit/bin/pi-task`, `schema` prints the spec
|
||||||
Invoke it with `bash`: `/opt/pi-toolkit/bin/pi-task run <spec.json>` (source at
|
fields); the `task` tool from pi-extensions wraps it** so it appears in your tool
|
||||||
`/workspace/pi-toolkit/bin/pi-task`, `schema` subcommand prints the spec fields).
|
list next to `fork`. If the tool is absent, invoke the CLI with `bash`:
|
||||||
It reads an immutable JSON spec, and "inherit the session" is not expressible in
|
`/opt/pi-toolkit/bin/pi-task run <spec.json>`. Either way it reads an immutable
|
||||||
that schema — the isolation is structural, not a request.
|
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:**
|
**Choose the lowest rung that can do the job:**
|
||||||
|
|
||||||
@@ -292,7 +364,17 @@ When entries conflict, **the most recent observation reflects the latest known s
|
|||||||
## Quick Reference
|
## Quick Reference
|
||||||
|
|
||||||
```
|
```
|
||||||
|
task(id, goal, deliverable, effort, read_only, roots, write_allowed, facts, files, commands, wall_s, usd)
|
||||||
|
- L0-L2: isolated child sees ONLY the spec — DEFAULT for work that writes or has rules
|
||||||
|
- roots[] = WATCHED (each diffed alone); write_allowed[] = exact subset of roots,
|
||||||
|
never nested inside a watched-only root (the tool rejects both errors up front)
|
||||||
|
- envelope must parse or the run FAILED; spot-check evidence pointers
|
||||||
|
- overlapping-root tasks are serialised; audit: ~/.pi/agent/pi-task/<stamp>-<id>/result.json
|
||||||
|
- CLI fallback: bash /opt/pi-toolkit/bin/pi-task run <spec.json> (schema | selftest | run --dry-run)
|
||||||
|
|
||||||
fork(task=..., effort=fast|balanced|deep) # L4: child inherits your WHOLE branch
|
fork(task=..., effort=fast|balanced|deep) # L4: child inherits your WHOLE branch
|
||||||
|
- ONLY for read-only exploration needing this conversation, or N parallel opinions
|
||||||
|
- fork-gate BLOCKS briefs with do-not/only/never, write boundaries, or "edit/commit/fix …"
|
||||||
- state decision authority explicitly
|
- state decision authority explicitly
|
||||||
- pass verified context up front
|
- pass verified context up front
|
||||||
- specify deliverable shape
|
- specify deliverable shape
|
||||||
@@ -302,13 +384,6 @@ fork(task=..., effort=fast|balanced|deep) # L4: child inherits your WHOLE b
|
|||||||
- 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
|
||||||
@@ -317,8 +392,9 @@ recall(id=<12-char-hex>)
|
|||||||
|
|
||||||
```
|
```
|
||||||
~/.pi/agent/settings.json
|
~/.pi/agent/settings.json
|
||||||
pi-fork.effortProfiles — model + thinking-depth per tier
|
pi-fork.effortProfiles — model + thinking-depth per tier (used by BOTH fork and task)
|
||||||
pi-fork.defaultEffort — usually "balanced"
|
pi-fork.defaultEffort — usually "balanced"
|
||||||
|
env PI_FORK_GATE=off — fork-gate logs instead of blocking (default: block)
|
||||||
observational-memory.* — token thresholds, model, agentMaxTurns
|
observational-memory.* — token thresholds, model, agentMaxTurns
|
||||||
observational-memory.debugLog: true — opt-in NDJSON telemetry at
|
observational-memory.debugLog: true — opt-in NDJSON telemetry at
|
||||||
~/.pi/agent/observational-memory/debug/<session>.ndjson (off by default)
|
~/.pi/agent/observational-memory/debug/<session>.ndjson (off by default)
|
||||||
|
|||||||
Reference in New Issue
Block a user