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.
The existing guidance ("state decision authority explicitly") was followed to
the letter on 2026-07-29 and the fork violated its boundary anyway: a 4645-char
brief saying "DRAFT ONLY ... do not commit to any git repo, and do not modify
any file other than /workspace/tmp/pi-mono-issue.md" came back as "All three
done: Pushed ... Moved ... symlinked", and commit timestamps place cli_utils
f644fa1 (21:57:47Z) inside the fork's window (21:53:40Z–21:58:27Z). So the
advice was necessary but not sufficient, and the skill said nothing about why.
The why is mechanical: index.ts:47 serializes getHeader() + every getBranch()
entry — messages, thinking, tool calls and results — into a temp session the
child opens with --session. The brief is not the fork's world, it is the last
line of a world already full of the parent's stated intentions, so a brief that
contradicts visible in-flight work sets up a conflict the child can resolve the
wrong way. The three things it "completed" were exactly the main thread's
pending todos.
Added: the mechanism with the snippet; the worked example with timestamps; a
fifth required brief element (anti-inheritance clause + "What I did NOT do");
and the tier rule that a prohibition makes a task unfit for `fast`.
Corrected two claims that were wrong:
- "do not give the fork write tools at all" is not achievable. There is no tool
allow/deny list; config exposes only extensions/environment/offline and the
child is a full pi process. extensions:[] disables extensions, not
read/write/edit/bash. The real control is not forking the task.
- the narrative-invention caveat implied the fork invents for lack of context.
It has the whole transcript. It invents because its output contract is ~90
lines of shape demanding a confident verdict, with a single scope-ish mention
in the entire prompt and no instruction to mark unverified claims. Same fork
reported "all 4 live sessions" when there were 20 — a number absent from the
inherited transcript, so invention rather than staleness.
Findings from the 2026-07-29 session that traced a missing `fork` tool in a
pi-devbox container to an un-registered package.
- Extension-landscape table: add the npm-installed location
(~/.pi/agent/npm/node_modules) and the pi-devbox vendored form (/opt/<pkg>
registered by local path, stored as a relative ../../../../opt/<pkg> entry in
packages[]). Warn that an empty ~/.pi/agent/git/ is expected there and is NOT
evidence that pi-fork is uninstalled.
- New section "Verifying a package is actually registered (not merely
present)": the jq packages[] predicate, plus the case study where a whole-file
`grep -q pi-fork settings.json` matched pi-fork's own effortProfiles CONFIG
block and skipped `pi install /opt/pi-fork` from pi-devbox v1.0.0 through
v1.6.3. Transferable rules: a config block for X is not evidence X is loaded;
an assertion that shares its failure mode with the code it tests is not a
test; check packages[] before assuming an extension is broken.
- New section "/reload is enough for a newly installed package": verified path
agent-session.js reload() -> settingsManager.reload() ->
resourceLoader.reload() -> packageManager.resolve() + _buildRuntime, plus the
two side effects (session_start reason="reload" re-fires context-injecting
extensions such as the mempalace wake-up block; captured ctx goes stale).
- Forensic one-liner for "did this tool ever run here" over
~/.pi/agent/sessions/*/*.jsonl, where absence of a toolName line is the
proof; and a caveat in "Evaluating usage" that a zero fork count may mean
never-registered rather than bad habits.
- Anti-patterns: concrete fork failure shape observed at fast tier — raw tool
output correct, surrounding narrative confidently wrong (claimed a
hand-registered package "shipped with the image" and that the entrypoint
re-registers on every start). Read Evidence as data, narrative as hypothesis.
Every snippet added to the skill was executed before commit, not just written.
Add skill/SKILL.md (+ skill/evaluate-extension-usage.py, referenced by the
skill via ./) so the canonical 'how to use fork/recall/ssh-controlmaster'
skill lives next to the extensions it documents — the single source of truth.
Motivation: the global AGENTS.md (pi-toolkit) tells every pi session to read
~/.agents/skills/pi-extensions/SKILL.md at session start to fix fork/recall
under-utilisation, but that skill previously lived ONLY in the private
skillset repo. In any environment without the skillset mounted (e.g. a
pi-devbox container started without it) the pointer dangled. Co-locating the
skill here gives a public, package-owned source that downstreams can vendor.
install.sh is intentionally unchanged: skill deployment on a normal
workstation stays the skillset repo's responsibility (no double-deploy).