Session → Collaboration Guideline
Produces a Markdown document that reads like a playbook for collaborating with the AI
on a task — not a raw transcript. It separates the goal from the steering, surfaces
the skills/memories created and why they work, and ends with a reproduce-it checklist.
Two layers:
- Deterministic extract (
scripts/extract_session.ts) — parses the session JSONL on
the active branch and emits a structured facts sheet (prompts in order, tool usage,
files written/edited, searches, skills/memories created, failed commands, cost). This is
raw material, not the deliverable. TypeScript, run with npx tsx (repo convention).
- Synthesis — read the facts sheet and write the guideline using
references/guideline-template.md. The why it's effective and what to steer parts
require judgment. Run it inline for a single session, or delegate to the
SessionGuideline subagent for batch / past-session application (see below) — the
synthesis is self-contained (facts sheet in, one guideline out), so it isolates cleanly.
Where sessions live
~/.pi/agent/sessions/--<cwd-with-slashes-as-dashes>--/<timestamp>_<uuid>.jsonl
(JSONL tree; see the pi session-format docs). The scripts locate files for you.
Worktrees are included by default. A project's OpenSpec work runs in .worktrees/<name>
sub-checkouts, which get their own encoded session dir (--<project>-.worktrees-<name>--).
Both scripts resolve a --cwd to the project root + every .worktrees/* worktree, so
project-scoped listing/latest covers worktree sessions too (rows tagged [wt:<name>]).
Pass --no-worktrees for the old root-only behavior. Running from inside a worktree still
lists the whole project (the root is recovered by stripping /.worktrees/<name>).
Procedure
Pick the session. If the user didn't name one, list candidates:
npx tsx scripts/list_sessions.ts --cwd "$(pwd)" --limit 20 # this project + its worktrees
npx tsx scripts/list_sessions.ts --cwd "$(pwd)" --no-worktrees # project root only
npx tsx scripts/list_sessions.ts --all --limit 30 # every project
Worktree rows are tagged [wt:<name>] so you can tell root work from worktree work.
(tsx runs the .ts directly, no build step.)
Show the table and confirm which one (by 8-char id or # index). The current live
session is usually #0/latest; documenting a finished prior session gives a complete
picture (the live one won't include the not-yet-written tail).
Extract the facts sheet (cheap, deterministic). Use a UNIQUE output path per run —
the fixed /tmp/session_facts.md is NOT parallel-safe: concurrent runs (e.g. a batch
of SessionGuideline spawns) clobber the same file and every reader gets the last
writer's sheet. Always mktemp:
FACTS=$(mktemp /tmp/session_facts.XXXXXX.md)
npx tsx scripts/extract_session.ts <selector> --cwd "$(pwd)" --out-md "$FACTS"
<selector> may be an 8-char id, a full path, or latest (use --index N for the
Nth most recent). In BATCH runs prefer the explicit JSONL path — the extract's
parent-chain walk can drift to a parent file on forked sessions.
- Use
--max-text / --max-cmd to widen truncation if you need more prompt/command text.
Read the facts sheet ($FACTS). Pay attention to:
- Prompt 1 = the goal; prompts 2..N = steering (corrections, scope additions,
quality bars, yes/all-three style unlocks).
- Skills created / Memories saved — these are the reusable assets; explain why.
- Tool errors / failed commands — these become the Pitfalls section.
- Artifacts — the files the operator ends up with.
Synthesize the guideline following references/guideline-template.md. Fill every
section. Rules:
- Write for a future operator with the same goal — instructive, not a log.
- Turn each steering turn into a guardrail ("the AI tended to X → state Y up front").
- For each skill/memory created, state the reusable problem it solves and when to invoke it.
- Rewrite weak prompts into the stronger version the reader should use.
- Quote sparingly; summarize tool activity into phases.
Write the deliverable into the weekly folder. The bucket is the
ISO week bucket line from the facts sheet Metadata (YYYY/Www, ISO-8601 week of the
session start). Default location, unless the user says otherwise:
<cwd>/Prompt stories/<YYYY>/W<WW>/<Topic>.md # e.g. Prompt stories/2026/W30/Hermes memory pressure.md
mkdir -p the week folder first. (Do NOT write it inside a skill folder.) Name the file
after the session name/topic. Begin the file with the YAML frontmatter block (see
references/guideline-template.md), filled from the facts sheet:
---
session: <8-char id>
week: <YYYY/Www>
type: <development|planning|research|documentation|other> # copy "Session type" verbatim
model: "@fast" # ALWAYS quote — an @-prefixed role is INVALID YAML unquoted
premium: <true|false> # copy the "Premium candidate" flag verbatim
premium_reason: "<reasons from the flag, or empty>"
upgrade_status: <pending|done|n/a>
# --- the next two ONLY when the facts sheet has an "OpenSpec changes" line ---
openspec_changes: [<change-name>, ...]
proposal_excerpt: "<the facts sheet 'Proposal excerpt' line, or omit if none>"
---
model MUST be quoted ("@fast", "@research"): a YAML plain scalar cannot start
with @ (reserved indicator) — unquoted model: @fast makes the whole frontmatter
invalid. It is the model that generated THIS story. A subagent cannot observe its own
runtime model, so when spawning SessionGuideline the parent MUST state it in the
prompt (e.g. generated-by: @fast) and the subagent writes that verbatim. Getting this
wrong mis-routes the upgrade queue (a budget story stamped @research never gets
re-run). Inline (non-subagent) runs: use the model you are actually running as.
type is classified deterministically by the extractor (Session type line:
code files → development, proposal/design/spec files → planning, research docs / many
searches + no code → research, docs → documentation, else other). Copy it; only override
if the narrative clearly contradicts the signal.
openspec_changes / proposal_excerpt appear only when a proposal is attached
to the session (the extractor found openspec/changes/<name>/ in the session's
files/commands and prints an OpenSpec changes line). Omit both fields entirely when
that line is absent — do not invent a proposal link.
When the write-up references images (storyboards, screenshots), link them relative to the
story file — from a week folder that is ../../Projektek/<Project>/.../shot_01.png — and
verify each resolves. Tell the user the path.
Mark premium stories for later Opus upgrade. Premium is decided deterministically
by the extractor — the facts sheet's Premium candidate flag is yes when the session
created a skill/memory, OR had ≥5 user prompts, OR produced a facts sheet ≥ ~10K tokens.
You do NOT judge it; you transcribe it. Set upgrade_status:
pending — premium: true AND a budget model wrote this story (@fast/@compact);
it is a candidate for an Opus re-run.
done — @research/Opus wrote it (already premium quality).
n/a — premium: false.
When upgrade_status: pending, append one row to the queue index
<cwd>/Prompt stories/_premium-queue.md (create with the header if missing):
| week | story | model | reason | status |
|------|-------|-------|--------|--------|
| 2026/W30 | 2026/W30/<Topic>.md | @fast | heavy steering (7 prompts) | pending |
A later upgrade pass re-runs each pending story on @research/Opus, overwrites the
file, and flips both its upgrade_status and the queue row to done.
Batch / past-session application (via the SessionGuideline subagent)
The synthesis is self-contained — facts sheet in, one guideline out, no coherence with any
ongoing work — so it is a clean subagent job. For a SINGLE interactive session, running it
inline (above) is fine. For applying to MANY past sessions, delegate each to the
SessionGuideline subagent so the facts sheet and the reasoning stay out of the main
context and sessions don't accumulate there:
- List the target sessions once:
npx tsx scripts/list_sessions.ts --cwd "$(pwd)" --limit 50 # or --all
- For each session, spawn
SessionGuideline (explicit Agent call), passing the
explicit JSONL path (not a partial id — the extract's parent-chain walk can drift to
a parent file on forked sessions) + an explicit output path. Each spawn runs BOTH layers
in isolation (extract → synthesise) and returns only the written path + a short abstract:Agent(subagent_type="SessionGuideline", model="@fast",
prompt="session JSONL <abs-path>; cwd <dir>; generated-by: @fast; write to the weekly
folder Prompt stories/<YYYY>/W<WW>/<Topic>.md (bucket from the facts sheet's
ISO week line); add frontmatter; if premium+budget-model, queue it")
Pass the model twice: the Agent(model=…) param sets the runtime model, and
generated-by: <same model> in the prompt tells the subagent what to write into
model: (it cannot introspect its own model). Keep them identical.
For bulk backfill on @fast, each spawn writes into its week folder and self-marks
premium candidates (upgrade_status: pending) into _premium-queue.md — a later Opus
pass drains that queue. See steps 5–6.
- Collect the returned paths. Parallel batches are safe ONLY because step 2 uses a
mktemp facts sheet per run — the old fixed /tmp/session_facts.md raced (concurrent
spawns overwrote it, so every playbook got the same sheet). Verify no two outputs share
an H1 title before trusting a batch.
Model role. The synthesis is judgment-heavy WRITING on a SMALL, pre-condensed input
(the extract script shrinks the JSONL first — it is NOT a long-context job). Quality lives
in the insight sections (goal-vs-steering, steering→guardrails, why-skills-effective),
where a weak model produces generic slop. Use @research (the subagent's default) for
quality. For bulk backfill where cost dominates, @compact is the budget fallback
(mechanical sections stay fine; insight degrades) — pass model on the Agent call to
override per run.
Selector cheatsheet
| Goal |
Command |
| Latest session in this project (+ worktrees) |
npx tsx scripts/extract_session.ts latest --cwd "$(pwd)" |
| Latest, project root only (no worktrees) |
npx tsx scripts/extract_session.ts latest --cwd "$(pwd)" --no-worktrees |
| 2nd-most-recent |
npx tsx scripts/extract_session.ts latest --cwd "$(pwd)" --index 1 |
| A specific session by id |
npx tsx scripts/extract_session.ts 019ea8a9 |
| A session in another project |
npx tsx scripts/extract_session.ts latest --cwd /path/to/other |
| An explicit file |
npx tsx scripts/extract_session.ts /abs/path/to/session.jsonl |
Notes & pitfalls
- The extractor walks the active branch only (leaf → root via
parentId), so abandoned
/tree branches are excluded — you document what actually happened.
- Tool names are normalized (
mcp__pi__web_search → web_search); skill and memory
calls are captured with their action/scope/target so "skills created & why effective" is
easy to write.
- The
Tokens total includes cache reads, so it can dwarf the in/out numbers — report cost,
not raw total, if it looks confusing.
- No third-party deps; TypeScript on Node built-ins (
fs/path/os). Run with npx tsx
— no compile/build step. Scripts never write to the session store.
- If a session is huge, raise
--max-cmds only when you actually need more commands; the
default keeps the facts sheet token-cheap.
1---2name: session-to-guideline3description: Turn a pi session into a Markdown "how-we-did-it" collaboration guideline: reads the session's JSONL transcript and synthesizes a reusable playbook of which prompts worked, what had to be steered, and how to reproduce the result faster. Use when: "document this session", "write up how we did X with the AI", "make a guideline from this session", "turn this session into a playbook/tutorial".4---56# Session → Collaboration Guideline78Produces a Markdown document that reads like a **playbook for collaborating with the AI**9on a task — not a raw transcript. It separates the *goal* from the *steering*, surfaces10the skills/memories created and why they work, and ends with a reproduce-it checklist.1112Two layers:13141. **Deterministic extract** (`scripts/extract_session.ts`) — parses the session JSONL on15 the active branch and emits a structured **facts sheet** (prompts in order, tool usage,16 files written/edited, searches, skills/memories created, failed commands, cost). This is17 raw material, not the deliverable. TypeScript, run with `npx tsx` (repo convention).182. **Synthesis** — read the facts sheet and write the guideline using19 `references/guideline-template.md`. The *why it's effective* and *what to steer* parts20 require judgment. Run it inline for a single session, or delegate to the21 **`SessionGuideline` subagent** for batch / past-session application (see below) — the22 synthesis is self-contained (facts sheet in, one guideline out), so it isolates cleanly.2324## Where sessions live2526`~/.pi/agent/sessions/--<cwd-with-slashes-as-dashes>--/<timestamp>_<uuid>.jsonl`27(JSONL tree; see the pi `session-format` docs). The scripts locate files for you.2829**Worktrees are included by default.** A project's OpenSpec work runs in `.worktrees/<name>`30sub-checkouts, which get their own encoded session dir (`--<project>-.worktrees-<name>--`).31Both scripts resolve a `--cwd` to the project root + every `.worktrees/*` worktree, so32project-scoped listing/`latest` covers worktree sessions too (rows tagged `[wt:<name>]`).33Pass `--no-worktrees` for the old root-only behavior. Running from *inside* a worktree still34lists the whole project (the root is recovered by stripping `/.worktrees/<name>`).3536## Procedure37381. **Pick the session.** If the user didn't name one, list candidates:39 ```bash40 npx tsx scripts/list_sessions.ts --cwd "$(pwd)" --limit 20 # this project + its worktrees41 npx tsx scripts/list_sessions.ts --cwd "$(pwd)" --no-worktrees # project root only42 npx tsx scripts/list_sessions.ts --all --limit 30 # every project43 ```44 Worktree rows are tagged `[wt:<name>]` so you can tell root work from worktree work.45 (`tsx` runs the `.ts` directly, no build step.)46 Show the table and confirm which one (by 8-char id or # index). The *current* live47 session is usually #0/`latest`; documenting a finished prior session gives a complete48 picture (the live one won't include the not-yet-written tail).49502. **Extract the facts sheet** (cheap, deterministic). Use a UNIQUE output path per run —51 the fixed `/tmp/session_facts.md` is **NOT parallel-safe**: concurrent runs (e.g. a batch52 of `SessionGuideline` spawns) clobber the same file and every reader gets the last53 writer's sheet. Always `mktemp`:54 ```bash55 FACTS=$(mktemp /tmp/session_facts.XXXXXX.md)56 npx tsx scripts/extract_session.ts <selector> --cwd "$(pwd)" --out-md "$FACTS"57 ```58 - `<selector>` may be an 8-char id, a full path, or `latest` (use `--index N` for the59 Nth most recent). In BATCH runs prefer the **explicit JSONL path** — the extract's60 parent-chain walk can drift to a parent file on forked sessions.61 - Use `--max-text` / `--max-cmd` to widen truncation if you need more prompt/command text.62633. **Read the facts sheet** (`$FACTS`). Pay attention to:64 - **Prompt 1 = the goal**; **prompts 2..N = steering** (corrections, scope additions,65 quality bars, yes/all-three style unlocks).66 - **Skills created / Memories saved** — these are the reusable assets; explain *why*.67 - **Tool errors / failed commands** — these become the *Pitfalls* section.68 - **Artifacts** — the files the operator ends up with.69704. **Synthesize the guideline** following `references/guideline-template.md`. Fill every71 section. Rules:72 - Write for a *future operator with the same goal* — instructive, not a log.73 - Turn each steering turn into a **guardrail** ("the AI tended to X → state Y up front").74 - For each skill/memory created, state the reusable problem it solves and when to invoke it.75 - Rewrite weak prompts into the stronger version the reader should use.76 - Quote sparingly; summarize tool activity into phases.77785. **Write the deliverable** into the **weekly** folder. The bucket is the79 `ISO week bucket` line from the facts sheet Metadata (`YYYY/Www`, ISO-8601 week of the80 session start). Default location, unless the user says otherwise:81 ```82 <cwd>/Prompt stories/<YYYY>/W<WW>/<Topic>.md # e.g. Prompt stories/2026/W30/Hermes memory pressure.md83 ```84 `mkdir -p` the week folder first. (Do NOT write it inside a skill folder.) Name the file85 after the session name/topic. **Begin the file with the YAML frontmatter block** (see86 `references/guideline-template.md`), filled from the facts sheet:87 ```yaml88 ---89 session: <8-char id>90 week: <YYYY/Www>91 type: <development|planning|research|documentation|other> # copy "Session type" verbatim92 model: "@fast" # ALWAYS quote — an @-prefixed role is INVALID YAML unquoted93 premium: <true|false> # copy the "Premium candidate" flag verbatim94 premium_reason: "<reasons from the flag, or empty>"95 upgrade_status: <pending|done|n/a>96 # --- the next two ONLY when the facts sheet has an "OpenSpec changes" line ---97 openspec_changes: [<change-name>, ...]98 proposal_excerpt: "<the facts sheet 'Proposal excerpt' line, or omit if none>"99 ---100 ```101 - **`model`** MUST be quoted (`"@fast"`, `"@research"`): a YAML plain scalar cannot start102 with `@` (reserved indicator) — unquoted `model: @fast` makes the whole frontmatter103 invalid. It is the model that generated THIS story. A subagent **cannot observe its own104 runtime model**, so when spawning `SessionGuideline` the parent MUST state it in the105 prompt (e.g. `generated-by: @fast`) and the subagent writes that verbatim. Getting this106 wrong mis-routes the upgrade queue (a budget story stamped `@research` never gets107 re-run). Inline (non-subagent) runs: use the model you are actually running as.108 - **`type`** is classified deterministically by the extractor (`Session type` line:109 code files → development, proposal/design/spec files → planning, research docs / many110 searches + no code → research, docs → documentation, else other). Copy it; only override111 if the narrative *clearly* contradicts the signal.112 - **`openspec_changes` / `proposal_excerpt`** appear **only when a proposal is attached**113 to the session (the extractor found `openspec/changes/<name>/` in the session's114 files/commands and prints an `OpenSpec changes` line). Omit both fields entirely when115 that line is absent — do not invent a proposal link.116 When the write-up references images (storyboards, screenshots), link them relative to the117 story file — from a week folder that is `../../Projektek/<Project>/.../shot_01.png` — and118 verify each resolves. Tell the user the path.1191206. **Mark premium stories for later Opus upgrade.** Premium is decided **deterministically**121 by the extractor — the facts sheet's `Premium candidate` flag is `yes` when the session122 created a skill/memory, OR had ≥5 user prompts, OR produced a facts sheet ≥ ~10K tokens.123 You do NOT judge it; you transcribe it. Set `upgrade_status`:124 - `pending` — `premium: true` AND a **budget** model wrote this story (`@fast`/`@compact`);125 it is a candidate for an Opus re-run.126 - `done` — `@research`/Opus wrote it (already premium quality).127 - `n/a` — `premium: false`.128129 When `upgrade_status: pending`, append one row to the queue index130 `<cwd>/Prompt stories/_premium-queue.md` (create with the header if missing):131 ```132 | week | story | model | reason | status |133 |------|-------|-------|--------|--------|134 | 2026/W30 | 2026/W30/<Topic>.md | @fast | heavy steering (7 prompts) | pending |135 ```136 A later upgrade pass re-runs each `pending` story on `@research`/Opus, overwrites the137 file, and flips both its `upgrade_status` and the queue row to `done`.138139## Batch / past-session application (via the `SessionGuideline` subagent)140141The synthesis is self-contained — facts sheet in, one guideline out, no coherence with any142ongoing work — so it is a clean subagent job. For a SINGLE interactive session, running it143inline (above) is fine. For applying to MANY past sessions, delegate each to the144**`SessionGuideline`** subagent so the facts sheet and the reasoning stay out of the main145context and sessions don't accumulate there:1461471. List the target sessions once:148 ```bash149 npx tsx scripts/list_sessions.ts --cwd "$(pwd)" --limit 50 # or --all150 ```1512. For each session, spawn `SessionGuideline` (explicit `Agent` call), passing the152 **explicit JSONL path** (not a partial id — the extract's parent-chain walk can drift to153 a parent file on forked sessions) + an explicit output path. Each spawn runs BOTH layers154 in isolation (extract → synthesise) and returns only the written path + a short abstract:155 ```156 Agent(subagent_type="SessionGuideline", model="@fast",157 prompt="session JSONL <abs-path>; cwd <dir>; generated-by: @fast; write to the weekly158 folder Prompt stories/<YYYY>/W<WW>/<Topic>.md (bucket from the facts sheet's159 ISO week line); add frontmatter; if premium+budget-model, queue it")160 ```161 **Pass the model twice:** the `Agent(model=…)` param sets the runtime model, and162 `generated-by: <same model>` in the prompt tells the subagent what to write into163 `model:` (it cannot introspect its own model). Keep them identical.164 ```165 ```166 For **bulk backfill on `@fast`**, each spawn writes into its week folder and self-marks167 premium candidates (`upgrade_status: pending`) into `_premium-queue.md` — a later Opus168 pass drains that queue. See steps 5–6.1693. Collect the returned paths. Parallel batches are safe ONLY because step 2 uses a170 `mktemp` facts sheet per run — the old fixed `/tmp/session_facts.md` raced (concurrent171 spawns overwrote it, so every playbook got the same sheet). Verify no two outputs share172 an H1 title before trusting a batch.173174**Model role.** The synthesis is judgment-heavy WRITING on a SMALL, pre-condensed input175(the extract script shrinks the JSONL first — it is NOT a long-context job). Quality lives176in the insight sections (goal-vs-steering, steering→guardrails, why-skills-effective),177where a weak model produces generic slop. Use **`@research`** (the subagent's default) for178quality. For bulk backfill where cost dominates, **`@compact`** is the budget fallback179(mechanical sections stay fine; insight degrades) — pass `model` on the `Agent` call to180override per run.181182## Selector cheatsheet183184| Goal | Command |185|------|---------|186| Latest session in this project (+ worktrees) | `npx tsx scripts/extract_session.ts latest --cwd "$(pwd)"` |187| Latest, project root only (no worktrees) | `npx tsx scripts/extract_session.ts latest --cwd "$(pwd)" --no-worktrees` |188| 2nd-most-recent | `npx tsx scripts/extract_session.ts latest --cwd "$(pwd)" --index 1` |189| A specific session by id | `npx tsx scripts/extract_session.ts 019ea8a9` |190| A session in another project | `npx tsx scripts/extract_session.ts latest --cwd /path/to/other` |191| An explicit file | `npx tsx scripts/extract_session.ts /abs/path/to/session.jsonl` |192193## Notes & pitfalls194195- The extractor walks the **active branch only** (leaf → root via `parentId`), so abandoned196 `/tree` branches are excluded — you document what actually happened.197- Tool names are normalized (`mcp__pi__web_search` → `web_search`); `skill` and `memory`198 calls are captured with their action/scope/target so "skills created & why effective" is199 easy to write.200- The `Tokens total` includes cache reads, so it can dwarf the in/out numbers — report cost,201 not raw total, if it looks confusing.202- No third-party deps; TypeScript on Node built-ins (`fs`/`path`/`os`). Run with `npx tsx`203 — no compile/build step. Scripts never write to the session store.204- If a session is huge, raise `--max-cmds` only when you actually need more commands; the205 default keeps the facts sheet token-cheap.