Created
July 22, 2026 00:21
-
-
Save jakkaj/c623c7c37abefcbf3b54a0c77cc34f44 to your computer and use it in GitHub Desktop.
pij — the deterministic layer (concept tree field guide)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="utf-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |
| <title>pij — the deterministic layer</title> | |
| <style> | |
| :root{ | |
| --paper:#F3F6F4; | |
| --card:#FFFFFF; | |
| --ink:#1E2724; | |
| --muted:#5D6C66; | |
| --line:#D9E1DD; | |
| --chipbg:#EEF3F0; | |
| --tmux:#A9C25B; | |
| --tmux-ink:#1F2612; | |
| --c-nodes:#15806F; | |
| --c-struct:#3D5CAB; | |
| --c-state:#9A6D14; | |
| --c-platform:#4C7C33; | |
| --c-coord:#7C4BA0; | |
| --c-comms:#B05330; | |
| --c-obs:#45688C; | |
| --c-ops:#94454F; | |
| --mono:ui-monospace,"SF Mono",Menlo,Consolas,"Liberation Mono",monospace; | |
| --serif:Charter,"Iowan Old Style","Palatino Linotype",Georgia,serif; | |
| } | |
| *{box-sizing:border-box;margin:0;padding:0} | |
| html{scroll-behavior:smooth} | |
| @media (prefers-reduced-motion: reduce){ | |
| html{scroll-behavior:auto} | |
| *{transition:none !important} | |
| } | |
| body{ | |
| background:var(--paper); | |
| color:var(--ink); | |
| font-family:var(--serif); | |
| font-size:17px; | |
| line-height:1.6; | |
| } | |
| a{color:inherit} | |
| a:focus-visible, .tmuxbar a:focus-visible{ | |
| outline:2px solid var(--c-struct); | |
| outline-offset:2px; | |
| border-radius:2px; | |
| } | |
| /* ── tmux status bar as navigation ─────────────────────────── */ | |
| .tmuxbar{ | |
| position:sticky;top:0;z-index:10; | |
| background:var(--tmux);color:var(--tmux-ink); | |
| font-family:var(--mono);font-size:12.5px; | |
| display:flex;align-items:center;gap:0; | |
| padding:5px 14px; | |
| overflow-x:auto;white-space:nowrap; | |
| border-bottom:1px solid #8FA649; | |
| } | |
| .tmuxbar .sess{font-weight:700;margin-right:10px} | |
| .tmuxbar a{ | |
| text-decoration:none;padding:2px 7px;border-radius:3px; | |
| color:var(--tmux-ink); | |
| } | |
| .tmuxbar a:hover{background:rgba(0,0,0,.14)} | |
| .tmuxbar .right{margin-left:auto;opacity:.75;padding-left:16px} | |
| /* ── page frame ────────────────────────────────────────────── */ | |
| .wrap{max-width:1000px;margin:0 auto;padding:48px 24px 80px} | |
| header.masthead{margin-bottom:36px} | |
| .masthead h1{ | |
| font-family:var(--mono);font-size:clamp(26px,4.5vw,40px); | |
| font-weight:700;letter-spacing:-.02em;line-height:1.15; | |
| } | |
| .masthead h1 .dim{color:var(--muted);font-weight:400} | |
| .masthead .lede{ | |
| max-width:64ch;margin-top:14px;font-size:18px;color:var(--ink); | |
| } | |
| .masthead .lede em{font-style:italic} | |
| .principle{ | |
| margin-top:22px; | |
| font-family:var(--mono);font-size:13.5px;line-height:1.9; | |
| border:1px solid var(--line);border-left:3px solid var(--ink); | |
| background:var(--card);border-radius:6px; | |
| padding:14px 18px; | |
| overflow-x:auto; | |
| } | |
| .principle b{font-weight:700} | |
| .principle .k{display:inline-block;min-width:5.5ch;color:var(--muted)} | |
| /* ── the tree (hero) ───────────────────────────────────────── */ | |
| .treecard{ | |
| background:var(--card);border:1px solid var(--line);border-radius:8px; | |
| margin:34px 0 10px; | |
| overflow:hidden; | |
| } | |
| .treecard .cap{ | |
| font-family:var(--mono);font-size:12px;color:var(--muted); | |
| padding:9px 16px;border-bottom:1px solid var(--line); | |
| display:flex;gap:8px;align-items:center; | |
| } | |
| .treecard .cap::before{content:"$";color:var(--c-platform);font-weight:700} | |
| .treecard pre{ | |
| font-family:var(--mono);font-size:13px;line-height:1.75; | |
| padding:18px 20px 22px;overflow-x:auto; | |
| } | |
| pre .g{color:#9AA8A2} /* box-drawing glyphs */ | |
| pre .v{color:var(--muted)} /* verbs, dimmed */ | |
| pre a{font-weight:700;text-decoration:none} | |
| pre a:hover{text-decoration:underline} | |
| .fn{ } | |
| .fam-nodes {color:var(--c-nodes)} | |
| .fam-struct {color:var(--c-struct)} | |
| .fam-state {color:var(--c-state)} | |
| .fam-platform {color:var(--c-platform)} | |
| .fam-coord {color:var(--c-coord)} | |
| .fam-comms {color:var(--c-comms)} | |
| .fam-obs {color:var(--c-obs)} | |
| .fam-ops {color:var(--c-ops)} | |
| .treenote{font-size:14.5px;color:var(--muted);margin-bottom:8px;font-style:italic} | |
| /* ── sections ─────────────────────────────────────────────── */ | |
| section.family{margin-top:56px;scroll-margin-top:56px} | |
| .famhead{display:flex;align-items:baseline;gap:12px;flex-wrap:wrap;margin-bottom:6px} | |
| .famhead .swatch{ | |
| width:13px;height:13px;border-radius:3px;flex:none;align-self:center; | |
| } | |
| .famhead h2{ | |
| font-family:var(--mono);font-size:19px;font-weight:700; | |
| text-transform:lowercase;letter-spacing:.01em; | |
| } | |
| .famhead .gloss{font-family:var(--serif);font-style:italic;color:var(--muted);font-size:16px} | |
| .famintro{max-width:70ch;color:var(--ink);margin:6px 0 18px;font-size:16.5px} | |
| .card{ | |
| background:var(--card); | |
| border:1px solid var(--line); | |
| border-left-width:3px; | |
| border-radius:6px; | |
| padding:16px 20px 15px; | |
| margin-bottom:14px; | |
| } | |
| .card .row1{ | |
| display:flex;align-items:baseline;gap:12px;flex-wrap:wrap;margin-bottom:6px; | |
| } | |
| .card h3{font-family:var(--mono);font-size:15.5px;font-weight:700} | |
| .store{ | |
| font-family:var(--mono);font-size:11px;color:var(--muted); | |
| background:var(--chipbg);border:1px solid var(--line);border-radius:4px; | |
| padding:2px 7px;white-space:nowrap; | |
| } | |
| .verbs{display:flex;flex-wrap:wrap;gap:6px;margin:8px 0 10px} | |
| .verb{ | |
| font-family:var(--mono);font-size:11.5px; | |
| border:1px solid var(--line);border-radius:4px; | |
| padding:2px 7px;background:var(--paper); | |
| } | |
| .card p{font-size:15.5px;max-width:74ch} | |
| .card p + p{margin-top:8px} | |
| .invariant{ | |
| margin-top:10px;font-size:14px; | |
| display:flex;gap:8px;align-items:baseline;max-width:74ch; | |
| } | |
| .invariant .tag{ | |
| font-family:var(--mono);font-size:10.5px;font-weight:700; | |
| text-transform:uppercase;letter-spacing:.06em; | |
| flex:none;padding:1px 6px;border-radius:3px;color:#fff; | |
| } | |
| .invariant em{font-style:italic} | |
| /* per-family accents */ | |
| #nodes .card{border-left-color:var(--c-nodes)} #nodes .swatch{background:var(--c-nodes)} #nodes .tag{background:var(--c-nodes)} | |
| #structure .card{border-left-color:var(--c-struct)} #structure .swatch{background:var(--c-struct)} #structure .tag{background:var(--c-struct)} | |
| #state .card{border-left-color:var(--c-state)} #state .swatch{background:var(--c-state)} #state .tag{background:var(--c-state)} | |
| #platform .card{border-left-color:var(--c-platform)} #platform .swatch{background:var(--c-platform)} #platform .tag{background:var(--c-platform)} | |
| #coordination .card{border-left-color:var(--c-coord)} #coordination .swatch{background:var(--c-coord)} #coordination .tag{background:var(--c-coord)} | |
| #comms .card{border-left-color:var(--c-comms)} #comms .swatch{background:var(--c-comms)} #comms .tag{background:var(--c-comms)} | |
| #observation .card{border-left-color:var(--c-obs)} #observation .swatch{background:var(--c-obs)} #observation .tag{background:var(--c-obs)} | |
| #ops .card{border-left-color:var(--c-ops)} #ops .swatch{background:var(--c-ops)} #ops .tag{background:var(--c-ops)} | |
| /* state axes mini-table */ | |
| .axes{width:100%;border-collapse:collapse;margin:4px 0 10px;font-size:14px} | |
| .axes td,.axes th{ | |
| border:1px solid var(--line);padding:7px 12px;text-align:left;vertical-align:top; | |
| } | |
| .axes th{font-family:var(--mono);font-size:11.5px;background:var(--chipbg);font-weight:700;white-space:nowrap} | |
| .axes .vals{font-family:var(--mono);font-size:12px;white-space:nowrap} | |
| .axes-wrap{overflow-x:auto} | |
| /* ── closing panels ───────────────────────────────────────── */ | |
| .nouns{ | |
| margin-top:64px;padding-top:28px;border-top:1px solid var(--line); | |
| } | |
| .nouns h2{font-family:var(--mono);font-size:17px;margin-bottom:4px} | |
| .nouns .sub{color:var(--muted);font-size:15.5px;margin-bottom:14px;max-width:70ch} | |
| .nounrow{display:flex;flex-wrap:wrap;gap:8px} | |
| .noun{ | |
| font-family:var(--mono);font-size:13px;font-weight:700; | |
| background:var(--card);border:1px solid var(--line);border-radius:5px; | |
| padding:6px 12px; | |
| } | |
| .noun small{display:block;font-weight:400;font-size:10.5px;color:var(--muted);margin-top:1px} | |
| .split{ | |
| display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-top:26px; | |
| } | |
| @media (max-width:640px){.split{grid-template-columns:1fr}} | |
| .split .half{ | |
| background:var(--card);border:1px solid var(--line);border-radius:6px; | |
| padding:16px 18px; | |
| } | |
| .split h3{font-family:var(--mono);font-size:13px;text-transform:uppercase;letter-spacing:.05em;margin-bottom:6px} | |
| .split .advisory h3{color:var(--c-platform)} | |
| .split .strict h3{color:var(--c-comms)} | |
| .split p{font-size:14.5px} | |
| .split ul{margin:8px 0 0 18px;font-size:14.5px} | |
| .split li{margin-bottom:4px} | |
| .split code{font-family:var(--mono);font-size:12.5px} | |
| footer{ | |
| margin-top:60px;padding-top:18px;border-top:1px solid var(--line); | |
| font-family:var(--mono);font-size:11.5px;color:var(--muted); | |
| display:flex;justify-content:space-between;gap:12px;flex-wrap:wrap; | |
| } | |
| code{font-family:var(--mono);font-size:.88em;background:var(--chipbg);padding:1px 5px;border-radius:3px} | |
| </style> | |
| </head> | |
| <body> | |
| <nav class="tmuxbar" aria-label="Sections"> | |
| <span class="sess">[pij]</span> | |
| <a href="#nodes">0:nodes</a> | |
| <a href="#structure">1:structure</a> | |
| <a href="#state">2:state</a> | |
| <a href="#platform">3:platform</a> | |
| <a href="#coordination">4:coordination</a> | |
| <a href="#comms">5:comms</a> | |
| <a href="#observation">6:observation</a> | |
| <a href="#ops">7:ops</a> | |
| <span class="right">"the deterministic layer" 22-Jul-26</span> | |
| </nav> | |
| <div class="wrap"> | |
| <header class="masthead"> | |
| <h1>pij <span class="dim">—</span> the deterministic layer</h1> | |
| <p class="lede"> | |
| pij coordinates agent sessions — pi, claude, copilot, codex — living in tmux panes. | |
| Everything it knows is a plain file under <code>~/.pij</code>: no database, no sockets. | |
| This page maps what pij can do <em>deterministically</em> — the record-keeping the | |
| agents themselves never have to be trusted with. | |
| </p> | |
| <div class="principle"> | |
| <div><span class="k">noun</span> = a file store under <b>~/.pij</b></div> | |
| <div><span class="k">verb</span> = a deterministic transition on exactly one store</div> | |
| <div><span class="k">clock</span> = the daemon tick — and <b>reads never need the daemon</b></div> | |
| </div> | |
| </header> | |
| <p class="treenote">The whole map. Every colored name links to its card below.</p> | |
| <div class="treecard"> | |
| <div class="cap">pij tree --concepts</div> | |
| <pre> | |
| pij | |
| <span class="g">│</span> | |
| <span class="g">├──</span> <a class="fam-nodes" href="#nodes">NODES & IDENTITY</a> <span class="v">who exists</span> | |
| <span class="g">│ ├──</span> <span class="fam-nodes">session / node</span> <span class="v">spawn · adopt · close · list · node show</span> | |
| <span class="g">│ ├──</span> <span class="fam-nodes">identity ledger</span> <span class="v">phonehome · canary</span> | |
| <span class="g">│ └──</span> <span class="fam-nodes">spawn expectation</span> <span class="v">durable launch intent</span> | |
| <span class="g">│</span> | |
| <span class="g">├──</span> <a class="fam-struct" href="#structure">STRUCTURE</a> <span class="v">how they relate</span> | |
| <span class="g">│ ├──</span> <span class="fam-struct">forest</span> <span class="v">tree · link</span> | |
| <span class="g">│ └──</span> <span class="fam-struct">focus</span> <span class="v">focus save · launch</span> | |
| <span class="g">│</span> | |
| <span class="g">├──</span> <a class="fam-state" href="#state">STATE</a> <span class="v">what condition it's in</span> | |
| <span class="g">│ ├──</span> <span class="fam-state">three measured axes</span> <span class="v">activity · liveness · systemState</span> | |
| <span class="g">│ └──</span> <span class="fam-state">one declared axis</span> <span class="v">state set · clear · verify</span> | |
| <span class="g">│</span> | |
| <span class="g">├──</span> <a class="fam-platform" href="#platform">WORK PLATFORM</a> <span class="v">project-related</span> | |
| <span class="g">│ ├──</span> <span class="fam-platform">project</span> <span class="v">create · list · show · set</span> | |
| <span class="g">│ ├──</span> <span class="fam-platform">stream</span> <span class="v">create · close</span> | |
| <span class="g">│ ├──</span> <span class="fam-platform">fence</span> <span class="v">set · show — notify-only</span> | |
| <span class="g">│ ├──</span> <span class="fam-platform">assignment / task</span> <span class="v">task set</span> | |
| <span class="g">│ └──</span> <span class="fam-platform">anomalies</span> <span class="v">derived safety queries</span> | |
| <span class="g">│</span> | |
| <span class="g">├──</span> <a class="fam-coord" href="#coordination">COORDINATION</a> <span class="v">shared-resource governance</span> | |
| <span class="g">│ ├──</span> <span class="fam-coord">baton</span> <span class="v">define · request · grant · return · reclaim</span> | |
| <span class="g">│ └──</span> <span class="fam-coord">prime</span> <span class="v">set · retire · unset</span> | |
| <span class="g">│</span> | |
| <span class="g">├──</span> <a class="fam-comms" href="#comms">COMMUNICATION</a> <span class="v">messages with receipts</span> | |
| <span class="g">│ ├──</span> <span class="fam-comms">message + inbox</span> <span class="v">send · inbox · broadcast · --command</span> | |
| <span class="g">│ ├──</span> <span class="fam-comms">delivery ownership</span> <span class="v">push (pi) · push (daemon) · pull</span> | |
| <span class="g">│ ├──</span> <span class="fam-comms">dispatch / ack</span> <span class="v">byte-verified packet receipts</span> | |
| <span class="g">│ └──</span> <span class="fam-comms">file watch</span> <span class="v">watch · unwatch</span> | |
| <span class="g">│</span> | |
| <span class="g">├──</span> <a class="fam-obs" href="#observation">OBSERVATION</a> <span class="v">append-only truth</span> | |
| <span class="g">│ ├──</span> <span class="fam-obs">peer events</span> <span class="v">tail · state — per session</span> | |
| <span class="g">│ └──</span> <span class="fam-obs">spine</span> <span class="v">append · events · render — global</span> | |
| <span class="g">│</span> | |
| <span class="g">└──</span> <a class="fam-ops" href="#ops">SUPERVISION & OPS</a> <span class="v">keeping it honest</span> | |
| <span class="g">├──</span> <span class="fam-ops">daemon</span> <span class="v">the only clock</span> | |
| <span class="g">├──</span> <span class="fam-ops">watchdog</span> <span class="v">per-session supervision</span> | |
| <span class="g">├──</span> <span class="fam-ops">agent packs</span> <span class="v">declarative one-shot workers</span> | |
| <span class="g">└──</span> <span class="fam-ops">bridges</span> <span class="v">operator inbox · telegram</span> | |
| </pre> | |
| </div> | |
| <!-- ───────────────────────── 0 · nodes ───────────────────────── --> | |
| <section class="family" id="nodes"> | |
| <div class="famhead"><span class="swatch"></span><h2>nodes & identity</h2><span class="gloss">who exists</span></div> | |
| <p class="famintro"> | |
| Before anything can be coordinated, pij has to know what's out there. This family answers | |
| one question and never guesses: <em>which terminals hold which agent sessions, and can we | |
| prove it?</em> | |
| </p> | |
| <div class="card"> | |
| <div class="row1"><h3>session / node</h3><span class="store">~/.pij/<id>.json · ~/.pij/<id>/</span></div> | |
| <div class="verbs"><span class="verb">spawn</span><span class="verb">adopt</span><span class="verb">close</span><span class="verb">whoami</span><span class="verb">list</span><span class="verb">sessions</span><span class="verb">node show</span><span class="verb">path</span></div> | |
| <p> | |
| Every agent terminal pij knows about gets a descriptor file and a data directory. The | |
| descriptor carries the memorable id (<code>pij-reasonable-dove</code> — never renamed), | |
| the harness it runs on, its pid, and its tmux address (<code>paneId</code> + | |
| <code>windowId</code>). That address is what lets any UI jump straight from a node to | |
| its live terminal. | |
| </p> | |
| <div class="invariant"><span class="tag">invariant</span><span>A session can never <code>close</code> itself, and death is recorded with evidence (<em>pid-missing</em>, pane gone) — never assumed.</span></div> | |
| </div> | |
| <div class="card"> | |
| <div class="row1"><h3>identity ledger</h3><span class="store">~/.pij/identities/ · by-native · by-pij</span></div> | |
| <div class="verbs"><span class="verb">phonehome</span><span class="verb">canary</span></div> | |
| <p> | |
| Proof that a pane really is the session — and the model — it claims to be. | |
| <code>phonehome</code> confirms a pending binding from inside the session; | |
| <code>canary</code> dispatches a nonce packet and attaches pass evidence. The ledger is | |
| indexed both ways (native session id ↔ pij id), so "which claude session is this node?" | |
| is a file lookup, not a guess. | |
| </p> | |
| </div> | |
| <div class="card"> | |
| <div class="row1"><h3>spawn expectation</h3><span class="store">~/.pij/spawn-expectations/</span></div> | |
| <div class="verbs"><span class="verb">spawn (writes one)</span><span class="verb">daemon reconciles</span></div> | |
| <p> | |
| When you ask for a colleague, pij first writes down what it <em>expects</em> to exist. | |
| The daemon then reconciles that expectation into a bound node when the real session | |
| appears — or reports an honest failure if it never does. Launch intent survives crashes | |
| because it's on disk before anything is running. | |
| </p> | |
| </div> | |
| </section> | |
| <!-- ───────────────────────── 1 · structure ───────────────────────── --> | |
| <section class="family" id="structure"> | |
| <div class="famhead"><span class="swatch"></span><h2>structure</h2><span class="gloss">how they relate</span></div> | |
| <p class="famintro"> | |
| Sessions form trees: a prime spawns workers, workers spawn helpers. Structure is its own | |
| concept because <em>reporting lines</em> and <em>teardown rights</em> are deliberately not | |
| the same thing. | |
| </p> | |
| <div class="card"> | |
| <div class="row1"><h3>forest</h3><span class="store">parentId per descriptor</span></div> | |
| <div class="verbs"><span class="verb">tree</span><span class="verb">tree --global</span><span class="verb">link --parent</span><span class="verb">link --root</span></div> | |
| <p> | |
| <code>tree</code> renders the current repo's forest (or everything with | |
| <code>--global</code>), filterable by liveness, activity, and lifecycle. | |
| <code>link</code> reparents a session with cycle and self-link checks, and every relink | |
| is audited to the spine. | |
| </p> | |
| <div class="invariant"><span class="tag">invariant</span><span>Two distinct edges: <code>effectiveParentId</code> is structure (who reports to whom); <code>spawnedBy</code> is close authorization (who may tear the pane down). Linking never transfers close rights.</span></div> | |
| </div> | |
| <div class="card"> | |
| <div class="row1"><h3>focus</h3><span class="store">saved native-session snapshots</span></div> | |
| <div class="verbs"><span class="verb">focus save</span><span class="verb">focus list</span><span class="verb">focus launch</span></div> | |
| <p> | |
| An immutable snapshot of a known-good native session — save it once, fork it on demand. | |
| Instead of re-briefing a fresh agent every time, you launch a copy of one that already | |
| has the context loaded. | |
| </p> | |
| </div> | |
| </section> | |
| <!-- ───────────────────────── 2 · state ───────────────────────── --> | |
| <section class="family" id="state"> | |
| <div class="famhead"><span class="swatch"></span><h2>state</h2><span class="gloss">what condition it's in</span></div> | |
| <p class="famintro"> | |
| The most opinionated part of pij: three axes are <em>measured</em> and can't be lied | |
| about, and exactly one is <em>declared</em> by agents. Keeping them separate is what makes | |
| "is it actually working?" answerable. | |
| </p> | |
| <div class="card"> | |
| <div class="row1"><h3>three measured axes</h3><span class="store">derived from pid · events · daemon ticks</span></div> | |
| <div class="axes-wrap"><table class="axes"> | |
| <tr><th>activity</th><td class="vals">working · done</td><td>What the event stream says it's doing right now.</td></tr> | |
| <tr><th>liveness</th><td class="vals">active · stale · dead</td><td>Is the process there, and how old is its last event?</td></tr> | |
| <tr><th>systemState</th><td class="vals">idle · stalled · dead</td><td>The daemon's verdict; every transition is journaled to the spine.</td></tr> | |
| </table></div> | |
| <p> | |
| <code>node show</code> collapses all axes into one display <code>badge</code>, and adds a | |
| live context gauge — current tokens with provenance (<em>claude-transcript</em>), so you | |
| can see a peer nearing its context ceiling from outside. | |
| </p> | |
| </div> | |
| <div class="card"> | |
| <div class="row1"><h3>one declared axis — semantic state</h3><span class="store">per-assignment, closed vocabulary</span></div> | |
| <div class="verbs"><span class="verb">state set</span><span class="verb">state clear</span><span class="verb">state verify</span></div> | |
| <p> | |
| The only state an agent gets to <em>say</em> about itself, scoped to an assignment and | |
| drawn from a fixed vocabulary. Clearing a declaration keeps the assignment's history — | |
| nothing is silently forgotten. | |
| </p> | |
| <div class="invariant"><span class="tag">invariant</span><span><em>Done is a claim until verified.</em> <code>state verify</code> stamps <code>verifiedBy</code>; an unverified done shows up in <code>anomalies</code>.</span></div> | |
| </div> | |
| </section> | |
| <!-- ───────────────────────── 3 · platform ───────────────────────── --> | |
| <section class="family" id="platform"> | |
| <div class="famhead"><span class="swatch"></span><h2>work platform</h2><span class="gloss">project-related</span></div> | |
| <p class="famintro"> | |
| The durable record of <em>what's being worked on</em>: projects, their worktrees, who owns | |
| which paths, and which node holds which task. This is the layer a governance UI would live | |
| on. | |
| </p> | |
| <div class="card"> | |
| <div class="row1"><h3>project</h3><span class="store">~/.pij/projects/</span></div> | |
| <div class="verbs"><span class="verb">project create</span><span class="verb">project list</span><span class="verb">project show</span><span class="verb">project set</span></div> | |
| <p> | |
| A named, durable unit of work: kebab slug (collision-resolved), description, an optional | |
| plan path, and a <code>primeId</code> — which session governs it. Created once, updated | |
| by attributed actors. | |
| </p> | |
| </div> | |
| <div class="card"> | |
| <div class="row1"><h3>stream</h3><span class="store">worktree allocations under a project</span></div> | |
| <div class="verbs"><span class="verb">stream create</span><span class="verb">stream close</span></div> | |
| <p> | |
| One attributed git-worktree allocation: reserved ordinal, base ref, owner. Closing it | |
| preserves work-in-progress, removes the worktree safely, and leaves a tombstone so the | |
| ordinal's history survives. | |
| </p> | |
| </div> | |
| <div class="card"> | |
| <div class="row1"><h3>fence</h3><span class="store">descriptive path ownership</span></div> | |
| <div class="verbs"><span class="verb">fence set --paths</span><span class="verb">fence show --path</span></div> | |
| <p> | |
| Declares which stream owns which paths, with optional shared paths. Ask | |
| <code>fence show --path</code> "who owns this file?" and get a deterministic answer. | |
| </p> | |
| <div class="invariant"><span class="tag">invariant</span><span>Notify-only, by design: overlap is <em>reported, never blocked</em>. Fences inform agents; they don't handcuff them.</span></div> | |
| </div> | |
| <div class="card"> | |
| <div class="row1"><h3>assignment / task</h3><span class="store">~/.pij/assignments/asg-*.json</span></div> | |
| <div class="verbs"><span class="verb">task set <node> "…"</span></div> | |
| <p> | |
| <code>task set</code> opens an assignment record and points the node at it. Semantic | |
| state declarations hang off the assignment, not the session — so a node's history of | |
| tasks, each with its own state trail, stays intact across handovers. | |
| </p> | |
| </div> | |
| <div class="card"> | |
| <div class="row1"><h3>anomalies</h3><span class="store">derived — no store of its own</span></div> | |
| <div class="verbs"><span class="verb">anomalies</span><span class="verb">--here</span><span class="verb">--project</span></div> | |
| <p> | |
| Read-only safety queries computed from everything above: a node whose declared state | |
| disagrees with its measured axes, a done that was never verified, a hold cleared by | |
| someone who didn't own it. An empty result means the books balance. | |
| </p> | |
| </div> | |
| </section> | |
| <!-- ───────────────────────── 4 · coordination ───────────────────────── --> | |
| <section class="family" id="coordination"> | |
| <div class="famhead"><span class="swatch"></span><h2>coordination</h2><span class="gloss">shared-resource governance</span></div> | |
| <p class="famintro"> | |
| When many agents share one machine, some things only one of them may touch at a time — | |
| the git index, a deploy, a database file. This family makes that exclusivity a record | |
| instead of a hope. | |
| </p> | |
| <div class="card"> | |
| <div class="row1"><h3>baton</h3><span class="store">~/.pij/orchestration/batons/*.json · *.lease</span></div> | |
| <div class="verbs"><span class="verb">define</span><span class="verb">request</span><span class="verb">grant</span><span class="verb">return</span><span class="verb">reclaim</span><span class="verb">list</span><span class="verb">show</span></div> | |
| <p> | |
| An atomic single-holder lease on a named resource, with a purpose queue: requesters say | |
| <em>why</em> they need it, the holder (or an operator) grants discretionarily, and | |
| grants are pushed to the recipient. Blocked time is measured. | |
| </p> | |
| <div class="invariant"><span class="tag">invariant</span><span>A stale holder triggers an <em>alert, never an auto-reclaim</em> — the lease changes hands only by an explicit, attributed act.</span></div> | |
| </div> | |
| <div class="card"> | |
| <div class="row1"><h3>prime</h3><span class="store">registry designation on the descriptor</span></div> | |
| <div class="verbs"><span class="verb">prime set</span><span class="verb">prime retire</span><span class="verb">prime unset</span></div> | |
| <p> | |
| Marks which session is the governing seat. One current prime (<code>P</code>), a history | |
| of retired old-primes (<code>O</code>) that keep their record but lose their authority. | |
| Projects point at their prime via <code>primeId</code>. | |
| </p> | |
| </div> | |
| </section> | |
| <!-- ───────────────────────── 5 · comms ───────────────────────── --> | |
| <section class="family" id="comms"> | |
| <div class="famhead"><span class="swatch"></span><h2>communication</h2><span class="gloss">messages with receipts</span></div> | |
| <p class="famintro"> | |
| Agents talk through durable inboxes, not sockets. The interesting part isn't sending — | |
| it's that <em>delivery ownership is explicit</em> and receipts are part of the message, | |
| so "did they get it?" always has an answer. | |
| </p> | |
| <div class="card"> | |
| <div class="row1"><h3>message + inbox</h3><span class="store">~/.pij/<id>/inbox/</span></div> | |
| <div class="verbs"><span class="verb">send</span><span class="verb">send --to … --to …</span><span class="verb">send --command</span><span class="verb">send --wait</span><span class="verb">inbox</span></div> | |
| <p> | |
| One-to-one messages, fan-out broadcasts (one independent result per recipient), or an | |
| allow-listed control command on a peer (<code>compact</code> · <code>new</code> · | |
| <code>reload</code>). <code>--wait</code> blocks until every send has a terminal receipt. | |
| </p> | |
| </div> | |
| <div class="card"> | |
| <div class="row1"><h3>delivery ownership</h3><span class="store">explicit, per harness</span></div> | |
| <div class="verbs"><span class="verb">pi → in-process push</span><span class="verb">tmux claude/copilot/codex → daemon push</span><span class="verb">external → pull</span></div> | |
| <p> | |
| Every inbox has exactly one owner. Pi sessions consume their own inbox in-process | |
| (the extension). Tmux-bound peers get keystrokes pushed by the daemon. Sessions without | |
| tmux pull with <code>pij inbox --wait</code> — and the daemon then never touches that | |
| inbox. No double delivery, no ambiguity about whose job it was. | |
| </p> | |
| </div> | |
| <div class="card"> | |
| <div class="row1"><h3>dispatch / ack</h3><span class="store">~/.pij/dispatches/dispatch-*.json</span></div> | |
| <div class="verbs"><span class="verb">dispatch --packet</span><span class="verb">ack --packet-sha</span><span class="verb">canary</span></div> | |
| <p> | |
| For payloads that matter: a packet gets a durable three-state dispatch receipt, and the | |
| recipient acks with the packet's sha-256 — so the record proves the exact bytes arrived. | |
| <code>canary</code> composes this with the identity ledger to verify who and what is on | |
| the other end. | |
| </p> | |
| </div> | |
| <div class="card"> | |
| <div class="row1"><h3>file watch</h3><span class="store">per-peer subscriptions</span></div> | |
| <div class="verbs"><span class="verb">watch <glob></span><span class="verb">unwatch</span><span class="verb">--debounce</span></div> | |
| <p> | |
| A non-pi peer can subscribe to file globs; the daemon injects debounced, coalesced | |
| <code>[file-watch]</code> notices into its pane. Agents that can't watch files still get | |
| told when files change. | |
| </p> | |
| </div> | |
| </section> | |
| <!-- ───────────────────────── 6 · observation ───────────────────────── --> | |
| <section class="family" id="observation"> | |
| <div class="famhead"><span class="swatch"></span><h2>observation</h2><span class="gloss">append-only truth</span></div> | |
| <p class="famintro"> | |
| Two parallel event planes, both append-only and sequence-numbered — which means any | |
| reader can poll cheaply with <code>--since N</code> and never re-ingest history. | |
| </p> | |
| <div class="card"> | |
| <div class="row1"><h3>peer events</h3><span class="store">~/.pij/<id>/events.ndjson</span></div> | |
| <div class="verbs"><span class="verb">tail <id></span><span class="verb">--since N</span><span class="verb">--type</span><span class="verb">--follow</span><span class="verb">state <id></span></div> | |
| <p> | |
| What <em>one agent</em> did: its transcript-level activity, seq-numbered. This is how an | |
| expensive reviewer follows a cheap worker incrementally — paying only for new events. | |
| <code>state</code> answers working/idle + liveness without parsing the stream, and even | |
| reports its own freshness (<code>daemonTickStale</code>). | |
| </p> | |
| </div> | |
| <div class="card"> | |
| <div class="row1"><h3>spine</h3><span class="store">~/.pij/spine/</span></div> | |
| <div class="verbs"><span class="verb">spine append</span><span class="verb">spine events --since</span><span class="verb">spine render</span></div> | |
| <p> | |
| What <em>the system</em> did: one global governed log of state transitions, dispatches, | |
| links, grants, task openings — each event with seq, timestamp, attributed actor, and | |
| node refs. <code>spine render</code> regenerates a human-readable markdown view, | |
| per-project if asked. | |
| </p> | |
| <div class="invariant"><span class="tag">invariant</span><span>Peer streams say what an agent did; the spine says what the <em>system</em> did. An activity feed wants the spine; a drill-in wants the tail.</span></div> | |
| </div> | |
| </section> | |
| <!-- ───────────────────────── 7 · ops ───────────────────────── --> | |
| <section class="family" id="ops"> | |
| <div class="famhead"><span class="swatch"></span><h2>supervision & ops</h2><span class="gloss">keeping it honest</span></div> | |
| <p class="famintro"> | |
| The machinery that keeps every store above truthful — and the one process that's allowed | |
| to act on a timer. | |
| </p> | |
| <div class="card"> | |
| <div class="row1"><h3>daemon</h3><span class="store">~/.pij/daemon.lock</span></div> | |
| <div class="verbs"><span class="verb">daemon start</span><span class="verb">status</span><span class="verb">stop</span><span class="verb">kill</span></div> | |
| <p> | |
| The only clock in the system. Each tick reconciles: rebuild indexes, detect deaths, | |
| drive pending spawns, push daemon-owned inboxes into tmux panes, run watchdogs. | |
| Single-instance by atomic lock, with stale-lock reclaim. | |
| </p> | |
| <div class="invariant"><span class="tag">invariant</span><span>Reads never need it. If the daemon is down you can still list, tree, and tail — the data is just as fresh as the last tick, and says so.</span></div> | |
| </div> | |
| <div class="card"> | |
| <div class="row1"><h3>watchdog</h3><span class="store">~/.pij/<id>/watchdog.json</span></div> | |
| <div class="verbs"><span class="verb">status</span><span class="verb">pause</span><span class="verb">resume</span><span class="verb">exempt</span><span class="verb">interval</span><span class="verb">watch</span><span class="verb">disable-all</span></div> | |
| <p> | |
| Per-session supervision: fires on an interval when a peer looks stuck, with pauses and | |
| time-boxed exemptions recorded — including <em>who</em> paused it. Supervision itself | |
| leaves an audit trail. | |
| </p> | |
| </div> | |
| <div class="card"> | |
| <div class="row1"><h3>agent packs</h3><span class="store">project · user · built-in packs</span></div> | |
| <div class="verbs"><span class="verb">agent list</span><span class="verb">agent run</span><span class="verb">agent spawn</span><span class="verb">agent report</span><span class="verb">check</span><span class="verb">eject</span></div> | |
| <p> | |
| Declarative, validated worker definitions run one-shot (<code>run</code>) or as a | |
| resident daemon-bound peer (<code>spawn</code>). A spawned pack pushes its result back to | |
| the spawner with <code>agent report</code> — delegation with a return envelope. | |
| </p> | |
| </div> | |
| <div class="card"> | |
| <div class="row1"><h3>bridges</h3><span class="store">~/.pij/operator/inbox · pane-signals/ · telegram.env</span></div> | |
| <div class="verbs"><span class="verb">telegram init</span><span class="verb">start</span><span class="verb">stop</span></div> | |
| <p> | |
| Where the outside world plugs in: an operator inbox for the human seat, raw pane | |
| signals, and a Telegram bridge that runs in-process with the daemon but tears down | |
| independently — a bridge failure never takes the daemon with it. | |
| </p> | |
| </div> | |
| </section> | |
| <!-- ───────────────────────── closing ───────────────────────── --> | |
| <div class="nouns"> | |
| <h2>the nine nouns</h2> | |
| <p class="sub"> | |
| Strip away the verbs and pij keeps exactly nine kinds of record. Everything else on this | |
| page is either a transition on one of these or plumbing that keeps them truthful. | |
| </p> | |
| <div class="nounrow"> | |
| <span class="noun">Node<small>a session</small></span> | |
| <span class="noun">Edge<small>parent / spawnedBy</small></span> | |
| <span class="noun">State<small>3 measured + 1 declared</small></span> | |
| <span class="noun">Project<small>unit of work</small></span> | |
| <span class="noun">Stream<small>a worktree</small></span> | |
| <span class="noun">Fence<small>path ownership</small></span> | |
| <span class="noun">Assignment<small>a task held</small></span> | |
| <span class="noun">Baton<small>a lease</small></span> | |
| <span class="noun">Event<small>spine + peer</small></span> | |
| </div> | |
| <div class="split"> | |
| <div class="half advisory"> | |
| <h3>Advisory — where it touches agents' judgment</h3> | |
| <p>Reported, never enforced. The system informs; the agent decides.</p> | |
| <ul> | |
| <li><code>fence</code> — overlap is a notice, not a lock</li> | |
| <li><code>anomalies</code> — findings, not failures</li> | |
| <li><code>watchdog</code> — a nudge, pausable and exemptible</li> | |
| </ul> | |
| </div> | |
| <div class="half strict"> | |
| <h3>Strict — where machines coordinate</h3> | |
| <p>Atomic, attributed, and verifiable. No trust required.</p> | |
| <ul> | |
| <li><code>baton</code> — one holder, explicit handoff only</li> | |
| <li><code>dispatch/ack</code> — receipts prove the exact bytes</li> | |
| <li><code>state verify</code> — done is a claim until stamped</li> | |
| </ul> | |
| </div> | |
| </div> | |
| </div> | |
| <footer> | |
| <span>source: live ~/.pij registry + pij CLI survey</span> | |
| <span>pi-hacking/pij · 2026-07-22</span> | |
| </footer> | |
| </div> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment