518 lines
42 KiB
HTML
518 lines
42 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>Novox Playbooks</title>
|
|
<style>
|
|
:root{
|
|
--bg:#f7f6f2; --card:#ffffff; --ink:#1d1f23; --mute:#5d6470; --line:#d9d6cc; --soft:#efede6;
|
|
--idea:#2f5fa7; --sym:#b4462b; --dec:#7a4bb0; --build:#3c8a5a; --mesh:#0f6f7a; --op:#c2410c; --side:#8a6d1f;
|
|
--ok:#15803d; --okbg:#e6f4ea; --warn:#a16207; --warnbg:#fdf3d7; --bad:#b91c1c; --badbg:#fde8e8;
|
|
}
|
|
@media (prefers-color-scheme: dark){
|
|
:root:not([data-theme="light"]){
|
|
--bg:#15171b; --card:#1e2127; --ink:#e8e6e1; --mute:#9aa1ad; --line:#353a43; --soft:#262a31;
|
|
--idea:#7aa7ef; --sym:#f08a6c; --dec:#b993ef; --build:#6fcf97; --mesh:#4fc3cf; --op:#fb923c; --side:#d9b44a;
|
|
--ok:#4ade80; --okbg:#173222; --warn:#facc15; --warnbg:#3a3016; --bad:#f87171; --badbg:#3a1d1d;
|
|
}
|
|
}
|
|
:root[data-theme="dark"]{
|
|
--bg:#15171b; --card:#1e2127; --ink:#e8e6e1; --mute:#9aa1ad; --line:#353a43; --soft:#262a31;
|
|
--idea:#7aa7ef; --sym:#f08a6c; --dec:#b993ef; --build:#6fcf97; --mesh:#4fc3cf; --op:#fb923c; --side:#d9b44a;
|
|
--ok:#4ade80; --okbg:#173222; --warn:#facc15; --warnbg:#3a3016; --bad:#f87171; --badbg:#3a1d1d;
|
|
}
|
|
*{box-sizing:border-box}
|
|
body{margin:0;background:var(--bg);color:var(--ink);font:15px/1.55 system-ui,-apple-system,"Segoe UI",sans-serif}
|
|
main{max-width:1120px;margin:0 auto;padding:28px 16px 72px}
|
|
h1{font-size:28px;margin:0 0 4px}
|
|
h2{font-size:20px;margin:44px 0 6px}
|
|
h3{font-size:16px;margin:0 0 6px}
|
|
p{margin:6px 0 10px}
|
|
.sub{color:var(--mute);margin:0 0 18px}
|
|
.lead{max-width:760px}
|
|
nav{display:flex;flex-wrap:wrap;gap:8px;margin:14px 0 6px}
|
|
nav a,.chip{display:inline-block;padding:4px 11px;border:1px solid var(--line);border-radius:999px;color:var(--ink);text-decoration:none;font-size:13px;background:var(--card);cursor:pointer}
|
|
nav a:hover,.chip:hover{border-color:var(--mute)}
|
|
.chip.on{background:var(--ink);color:var(--bg);border-color:var(--ink)}
|
|
.box{background:var(--card);border:1px solid var(--line);border-radius:12px;padding:16px;margin:12px 0}
|
|
.scroll{overflow-x:auto}
|
|
svg{display:block;max-width:100%;height:auto;font-family:inherit}
|
|
svg text{fill:var(--ink);font-size:13px}
|
|
svg .mute{fill:var(--mute);font-size:11.5px}
|
|
svg .t{font-weight:600}
|
|
svg .edge{stroke:var(--mute);stroke-width:1.6;fill:none}
|
|
svg .dash{stroke-dasharray:5 4}
|
|
.legend{display:flex;flex-wrap:wrap;gap:14px;font-size:13px;color:var(--mute);margin-top:8px}
|
|
.legend i{display:inline-block;width:12px;height:12px;border-radius:3px;margin-right:6px;vertical-align:-1px}
|
|
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(320px,1fr));gap:14px}
|
|
.pb{background:var(--card);border:1px solid var(--line);border-radius:12px;padding:14px 16px;border-top:4px solid var(--c,var(--line))}
|
|
.pb .num{font:600 12px/1 ui-monospace,monospace;color:var(--c);letter-spacing:.04em}
|
|
.pb h3{margin:4px 0 4px;font-size:17px}
|
|
.pb .trig{color:var(--mute);font-size:13.5px;margin:0 0 8px}
|
|
.pb details{border-top:1px solid var(--line);padding-top:6px;margin-top:6px}
|
|
.pb summary{cursor:pointer;font-size:13.5px;font-weight:600;color:var(--ink);list-style:none}
|
|
.pb summary::before{content:"▸ ";color:var(--mute)}
|
|
.pb details[open] summary::before{content:"▾ "}
|
|
.pb ol,.pb ul{margin:6px 0 4px;padding-left:20px;font-size:13.5px}
|
|
.pb li{margin:2px 0}
|
|
.kv{display:grid;grid-template-columns:96px 1fr;gap:4px 10px;font-size:13px;margin-top:6px}
|
|
.kv b{color:var(--mute);font-weight:500}
|
|
.tag{display:inline-block;font-size:11.5px;padding:1px 7px;border-radius:999px;background:var(--soft);color:var(--mute);margin:0 4px 4px 0}
|
|
.new{background:var(--okbg);color:var(--ok);font-weight:600}
|
|
table{border-collapse:collapse;width:100%;font-size:14px}
|
|
th,td{text-align:left;padding:7px 9px;border-bottom:1px solid var(--line);vertical-align:top}
|
|
th{color:var(--mute);font-weight:600;font-size:12.5px;text-transform:uppercase;letter-spacing:.03em}
|
|
.note{border-left:3px solid var(--ok);background:var(--okbg);padding:9px 12px;border-radius:6px;font-size:14px}
|
|
.two{display:grid;grid-template-columns:1fr 1fr;gap:14px}
|
|
@media (max-width:760px){.two{grid-template-columns:1fr}}
|
|
footer{color:var(--mute);font-size:13px;margin-top:48px}
|
|
code{font:12.5px ui-monospace,monospace;background:var(--soft);padding:1px 5px;border-radius:4px}
|
|
.theme{float:right}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<main>
|
|
<button class="chip theme" id="theme" type="button">Theme</button>
|
|
<h1>Novox Playbooks</h1>
|
|
<p class="sub">How work is done on the mesh, from an idea or a symptom to a delivered change. Drawn from hq's <code>00-META/process/</code> at main <code>f269e83</code>, 2026-10-19, including playbook change hq #433 (try before the review).</p>
|
|
|
|
<nav>
|
|
<a href="#cycle">The cycle</a>
|
|
<a href="#feature">A feature, step by step</a>
|
|
<a href="#tests">When tests run</a>
|
|
<a href="#review">The review loop</a>
|
|
<a href="#issues">An issue's life</a>
|
|
<a href="#where">Where things live</a>
|
|
<a href="#all">All playbooks</a>
|
|
</nav>
|
|
|
|
<!-- ============ THE CYCLE ============ -->
|
|
<h2 id="cycle">The development cycle</h2>
|
|
<p class="lead">Work enters as an <b>idea</b> or a <b>symptom</b>. An idea becomes a decision, a design and then milestones; a symptom becomes an issue. Both end as code delivered to the machines, and the as-is design is updated. <b>No design without a decision; no development without a design or an issue that sends it.</b></p>
|
|
<div class="box scroll">
|
|
<svg viewBox="0 0 1080 470" role="img" aria-label="The development cycle across playbooks 01 to 09">
|
|
<defs>
|
|
<marker id="ar" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0L10,5L0,10z" fill="var(--mute)"/></marker>
|
|
</defs>
|
|
<!-- entry points -->
|
|
<g>
|
|
<rect x="16" y="40" width="120" height="50" rx="25" fill="none" stroke="var(--idea)" stroke-width="2"/>
|
|
<text x="76" y="70" text-anchor="middle" class="t">Idea</text>
|
|
<rect x="16" y="300" width="120" height="50" rx="25" fill="none" stroke="var(--sym)" stroke-width="2"/>
|
|
<text x="76" y="330" text-anchor="middle" class="t">Symptom</text>
|
|
</g>
|
|
<!-- idea path -->
|
|
<g>
|
|
<rect x="176" y="30" width="150" height="70" rx="10" fill="var(--card)" stroke="var(--idea)" stroke-width="2"/>
|
|
<text x="251" y="58" text-anchor="middle" class="t">01 Research</text>
|
|
<text x="251" y="78" text-anchor="middle" class="mute">questions, findings,</text>
|
|
<text x="251" y="92" text-anchor="middle" class="mute">direction confirmed</text>
|
|
|
|
<rect x="366" y="30" width="160" height="70" rx="10" fill="var(--card)" stroke="var(--dec)" stroke-width="2"/>
|
|
<text x="446" y="58" text-anchor="middle" class="t">02 Graduation</text>
|
|
<text x="446" y="78" text-anchor="middle" class="mute">decisions in the</text>
|
|
<text x="446" y="92" text-anchor="middle" class="mute">knowledge base · to-be</text>
|
|
|
|
<rect x="566" y="30" width="160" height="70" rx="10" fill="var(--card)" stroke="var(--dec)" stroke-width="2"/>
|
|
<text x="646" y="58" text-anchor="middle" class="t">04 Breakdown</text>
|
|
<text x="646" y="78" text-anchor="middle" class="mute">milestones by release,</text>
|
|
<text x="646" y="92" text-anchor="middle" class="mute">issues per repository</text>
|
|
|
|
<rect x="766" y="30" width="150" height="70" rx="10" fill="var(--card)" stroke="var(--build)" stroke-width="2.5"/>
|
|
<text x="841" y="58" text-anchor="middle" class="t">07 Feature</text>
|
|
<text x="841" y="78" text-anchor="middle" class="mute">one milestone =</text>
|
|
<text x="841" y="92" text-anchor="middle" class="mute">one release</text>
|
|
</g>
|
|
<!-- symptom path -->
|
|
<g>
|
|
<rect x="176" y="290" width="150" height="70" rx="10" fill="var(--card)" stroke="var(--sym)" stroke-width="2"/>
|
|
<text x="251" y="318" text-anchor="middle" class="t">03 Issues</text>
|
|
<text x="251" y="338" text-anchor="middle" class="mute">opened in the tracker,</text>
|
|
<text x="251" y="352" text-anchor="middle" class="mute">diagnosed, located</text>
|
|
|
|
<rect x="566" y="290" width="160" height="70" rx="10" fill="var(--card)" stroke="var(--build)" stroke-width="2.5"/>
|
|
<text x="646" y="318" text-anchor="middle" class="t">08 Small fix</text>
|
|
<text x="646" y="338" text-anchor="middle" class="mute">one issue, one repository,</text>
|
|
<text x="646" y="352" text-anchor="middle" class="mute">about an hour</text>
|
|
</g>
|
|
<!-- delivered + as-is -->
|
|
<g>
|
|
<rect x="950" y="160" width="116" height="74" rx="10" fill="var(--card)" stroke="var(--mesh)" stroke-width="2.5"/>
|
|
<text x="1008" y="188" text-anchor="middle" class="t">Delivered</text>
|
|
<text x="1008" y="206" text-anchor="middle" class="mute">Approve, merge,</text>
|
|
<text x="1008" y="220" text-anchor="middle" class="mute">delivery plan</text>
|
|
|
|
<rect x="766" y="160" width="150" height="74" rx="10" fill="var(--card)" stroke="var(--dec)" stroke-width="2" stroke-dasharray="5 4"/>
|
|
<text x="841" y="188" text-anchor="middle" class="t">As-is updated</text>
|
|
<text x="841" y="206" text-anchor="middle" class="mute">02 "when it ships":</text>
|
|
<text x="841" y="220" text-anchor="middle" class="mute">to-be → implemented</text>
|
|
</g>
|
|
<!-- side playbooks -->
|
|
<g>
|
|
<rect x="176" y="400" width="190" height="56" rx="10" fill="var(--soft)" stroke="var(--side)"/>
|
|
<text x="271" y="424" text-anchor="middle" class="t">05 Constitution sync</text>
|
|
<text x="271" y="442" text-anchor="middle" class="mute">how-we-build → derived page</text>
|
|
<rect x="396" y="400" width="190" height="56" rx="10" fill="var(--soft)" stroke="var(--side)"/>
|
|
<text x="491" y="424" text-anchor="middle" class="t">06 Writing a module</text>
|
|
<text x="491" y="442" text-anchor="middle" class="mute">what runs today → a module</text>
|
|
<rect x="876" y="400" width="190" height="56" rx="10" fill="var(--soft)" stroke="var(--side)"/>
|
|
<text x="971" y="424" text-anchor="middle" class="t">09 Cleanup</text>
|
|
<text x="971" y="442" text-anchor="middle" class="mute">branches, worktrees, on the word</text>
|
|
</g>
|
|
<!-- edges -->
|
|
<path class="edge" d="M136,65 H174" marker-end="url(#ar)"/>
|
|
<path class="edge" d="M326,65 H364" marker-end="url(#ar)"/>
|
|
<path class="edge" d="M526,65 H564" marker-end="url(#ar)"/>
|
|
<path class="edge" d="M726,65 H764" marker-end="url(#ar)"/>
|
|
<path class="edge" d="M916,65 C990,65 1008,100 1008,158" marker-end="url(#ar)"/>
|
|
<path class="edge" d="M136,325 H174" marker-end="url(#ar)"/>
|
|
<path class="edge" d="M326,325 H564" marker-end="url(#ar)"/>
|
|
<text x="445" y="317" text-anchor="middle" class="mute">a fix of about an hour</text>
|
|
<path class="edge" d="M726,325 C990,325 1008,290 1008,236" marker-end="url(#ar)"/>
|
|
<path class="edge" d="M948,197 H918" marker-end="url(#ar)"/>
|
|
<path class="edge dash" d="M251,290 C251,200 420,170 440,102" marker-end="url(#ar)"/>
|
|
<text x="300" y="196" class="mute">a design gap: decision first</text>
|
|
<path class="edge dash" d="M326,300 C560,250 720,170 800,102" marker-end="url(#ar)"/>
|
|
<text x="612" y="236" class="mute">larger than an hour: a feature</text>
|
|
<path class="edge dash" d="M1008,236 C1008,330 990,360 971,398" marker-end="url(#ar)"/>
|
|
</svg>
|
|
<div class="legend">
|
|
<span><i style="background:var(--idea)"></i>from an idea</span>
|
|
<span><i style="background:var(--sym)"></i>from a symptom</span>
|
|
<span><i style="background:var(--dec)"></i>decisions and designs</span>
|
|
<span><i style="background:var(--build)"></i>code changes</span>
|
|
<span><i style="background:var(--mesh)"></i>the mesh delivers</span>
|
|
<span><i style="background:var(--side)"></i>alongside</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ============ FEATURE SWIMLANE ============ -->
|
|
<h2 id="feature">A feature, step by step (playbook 07)</h2>
|
|
<p class="lead">One milestone is one release: one branch name in every repository it touches, one worktree per repository, one fixing agent per repository kept for the whole milestone, one reviewer, and one pull request per repository, opened once. Since hq #433 the branches are <b>tried before the review</b>, so the reviewer reads real integration results.</p>
|
|
<div class="box scroll">
|
|
<svg viewBox="0 0 1080 430" role="img" aria-label="Playbook 07 in lanes: lead, fixing agents, reviewer, the mesh, the operator">
|
|
<defs>
|
|
<marker id="ar2" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0L10,5L0,10z" fill="var(--mute)"/></marker>
|
|
</defs>
|
|
<!-- step columns -->
|
|
<g class="mute">
|
|
<text x="190" y="20" text-anchor="middle" class="t" style="fill:var(--mute)">0 · Prepare</text>
|
|
<text x="190" y="34" text-anchor="middle" class="mute">15 min</text>
|
|
<text x="410" y="20" text-anchor="middle" class="t" style="fill:var(--mute)">1 · Develop + try</text>
|
|
<text x="410" y="34" text-anchor="middle" class="mute">30 min per issue · one try</text>
|
|
<text x="630" y="20" text-anchor="middle" class="t" style="fill:var(--mute)">2 · Review</text>
|
|
<text x="630" y="34" text-anchor="middle" class="mute">≤ 3 rounds · 15 min</text>
|
|
<text x="830" y="20" text-anchor="middle" class="t" style="fill:var(--mute)">3 · Check</text>
|
|
<text x="830" y="34" text-anchor="middle" class="mute">≤ 1 more try · 1 check</text>
|
|
<text x="1000" y="20" text-anchor="middle" class="t" style="fill:var(--mute)">4 · Deliver</text>
|
|
<text x="1000" y="34" text-anchor="middle" class="mute">its delivery plan</text>
|
|
</g>
|
|
<!-- lanes -->
|
|
<g>
|
|
<rect x="0" y="46" width="1080" height="72" fill="var(--soft)" opacity=".55"/>
|
|
<rect x="0" y="190" width="1080" height="72" fill="var(--soft)" opacity=".55"/>
|
|
<rect x="0" y="334" width="1080" height="72" fill="var(--soft)" opacity=".55"/>
|
|
<text x="10" y="86" class="t">Lead</text><text x="10" y="102" class="mute">larger model</text>
|
|
<text x="10" y="158" class="t">Fixing agents</text><text x="10" y="174" class="mute">smaller model</text>
|
|
<text x="10" y="230" class="t">Reviewer</text><text x="10" y="246" class="mute">larger model</text>
|
|
<text x="10" y="302" class="t">The mesh</text><text x="10" y="318" class="mute">try, checks, plan</text>
|
|
<text x="10" y="374" class="t">Operator</text><text x="10" y="390" class="mute">Telegram</text>
|
|
</g>
|
|
<!-- lead -->
|
|
<rect x="110" y="56" width="160" height="52" rx="8" fill="var(--card)" stroke="var(--dec)" stroke-width="2"/>
|
|
<text x="190" y="78" text-anchor="middle" class="t">Survey · criteria</text>
|
|
<text x="190" y="96" text-anchor="middle" class="mute">context file per repo</text>
|
|
<rect x="340" y="56" width="140" height="52" rx="8" fill="var(--card)" stroke="var(--dec)" stroke-width="2"/>
|
|
<text x="410" y="78" text-anchor="middle" class="t">Up to trunk · push</text>
|
|
<text x="410" y="96" text-anchor="middle" class="mute">each branch</text>
|
|
<rect x="760" y="56" width="140" height="52" rx="8" fill="var(--card)" stroke="var(--dec)" stroke-width="2"/>
|
|
<text x="830" y="78" text-anchor="middle" class="t">Push reviewed heads</text>
|
|
<text x="830" y="96" text-anchor="middle" class="mute">open the PRs together</text>
|
|
<rect x="930" y="56" width="140" height="52" rx="8" fill="var(--card)" stroke="var(--dec)" stroke-width="2"/>
|
|
<text x="1000" y="78" text-anchor="middle" class="t">Follow to done</text>
|
|
<text x="1000" y="96" text-anchor="middle" class="mute">resolve the issues</text>
|
|
<!-- fixing -->
|
|
<rect x="300" y="128" width="220" height="52" rx="8" fill="var(--card)" stroke="var(--build)" stroke-width="2"/>
|
|
<text x="410" y="150" text-anchor="middle" class="t">Build each issue, commit</text>
|
|
<text x="410" y="168" text-anchor="middle" class="mute">unit suite before every commit</text>
|
|
<rect x="560" y="128" width="140" height="52" rx="8" fill="var(--card)" stroke="var(--build)" stroke-width="2"/>
|
|
<text x="630" y="150" text-anchor="middle" class="t">Fix findings</text>
|
|
<text x="630" y="168" text-anchor="middle" class="mute">the same agents</text>
|
|
<!-- reviewer -->
|
|
<rect x="560" y="200" width="140" height="52" rx="8" fill="var(--card)" stroke="var(--idea)" stroke-width="2"/>
|
|
<text x="630" y="222" text-anchor="middle" class="t">Read all repos</text>
|
|
<text x="630" y="240" text-anchor="middle" class="mute">+ tries · every criterion</text>
|
|
<!-- mesh -->
|
|
<rect x="340" y="272" width="140" height="52" rx="8" fill="var(--card)" stroke="var(--mesh)" stroke-width="2"/>
|
|
<text x="410" y="294" text-anchor="middle" class="t">try</text>
|
|
<text x="410" y="312" text-anchor="middle" class="mute">integration tests run</text>
|
|
<rect x="760" y="272" width="140" height="52" rx="8" fill="var(--card)" stroke="var(--mesh)" stroke-width="2"/>
|
|
<text x="830" y="294" text-anchor="middle" class="t">Forge checks</text>
|
|
<text x="830" y="312" text-anchor="middle" class="mute">gate · repo · group</text>
|
|
<rect x="930" y="272" width="140" height="52" rx="8" fill="var(--card)" stroke="var(--mesh)" stroke-width="2"/>
|
|
<text x="1000" y="294" text-anchor="middle" class="t">Merge · delivery plan</text>
|
|
<text x="1000" y="312" text-anchor="middle" class="mute">backups first, tier by tier</text>
|
|
<!-- operator -->
|
|
<rect x="860" y="344" width="140" height="52" rx="8" fill="var(--card)" stroke="var(--op)" stroke-width="2.5"/>
|
|
<text x="930" y="366" text-anchor="middle" class="t">Approve</text>
|
|
<text x="930" y="384" text-anchor="middle" class="mute">once, for the group</text>
|
|
<!-- edges -->
|
|
<path class="edge" d="M190,108 C190,140 260,154 298,154" marker-end="url(#ar2)"/>
|
|
<path class="edge" d="M410,128 V110" marker-end="url(#ar2)"/>
|
|
<path class="edge" d="M480,90 C545,110 545,250 462,270" marker-end="url(#ar2)"/>
|
|
<path class="edge" d="M480,298 C530,298 540,226 558,226" marker-end="url(#ar2)"/>
|
|
<text x="490" y="262" class="mute">results</text>
|
|
<path class="edge" d="M630,200 V182" marker-end="url(#ar2)"/>
|
|
<path class="edge dash" d="M610,182 V198" marker-end="url(#ar2)"/>
|
|
<text x="514" y="196" class="mute">round n+1</text>
|
|
<path class="edge" d="M700,226 C740,226 740,82 758,82" marker-end="url(#ar2)"/>
|
|
<text x="712" y="140" class="mute">approved</text>
|
|
<path class="edge" d="M830,108 V270" marker-end="url(#ar2)"/>
|
|
<path class="edge" d="M900,298 C915,298 920,330 925,342" marker-end="url(#ar2)"/>
|
|
<path class="edge" d="M1000,344 V326" marker-end="url(#ar2)"/>
|
|
<path class="edge" d="M1000,272 V110" marker-end="url(#ar2)"/>
|
|
</svg>
|
|
</div>
|
|
<div class="two">
|
|
<div class="box">
|
|
<h3>Step 0 · what the lead writes before any agent starts</h3>
|
|
<ul style="margin:6px 0;padding-left:20px;font-size:14px">
|
|
<li>Every open question settled: on the issue, in the knowledge base, or by the operator.</li>
|
|
<li>Into each issue: every file and test it touches (file:line), how it is checked and delivered, a group's merge order.</li>
|
|
<li><span class="tag new">new</span>Every other reader and writer of each value the change touches, each with a criterion.</li>
|
|
<li>Acceptance criteria: given / when / then, each naming its test level.</li>
|
|
<li>Release orders hidden in the milestone, said to the operator, never built around.</li>
|
|
<li>Fixtures copied from what the other side really writes, never from memory.</li>
|
|
<li>An issue sized over an hour is split before work starts.</li>
|
|
</ul>
|
|
</div>
|
|
<div class="box">
|
|
<h3>Step 1 · the fixing agent's brief</h3>
|
|
<ul style="margin:6px 0;padding-left:20px;font-size:14px">
|
|
<li>One commit per issue, naming it; tests cite their criterion (<code>// 1217/AC3</code>).</li>
|
|
<li><span class="tag new">new</span>A test fails first on what the code does. A compile error is no failure: take out the change's effect and see an assertion fail.</li>
|
|
<li>The whole unit suite before every commit; each test well under a second.</li>
|
|
<li>Integration tests are compiled only: never run on the machine.</li>
|
|
<li>Never push, open or merge; never move an issue; never ssh.</li>
|
|
<li>Code that differs from the context file: stop and report, file:line.</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ============ TESTS ============ -->
|
|
<h2 id="tests">When tests run</h2>
|
|
<p class="lead">Two levels. Unit tests run all the time on the machine; integration tests (a bus, a store or a process started) never run there: <code>mesh-controller.try</code> runs them on the pushed branch, and the forge's merge check runs them again as the gate.</p>
|
|
<div class="box scroll">
|
|
<svg viewBox="0 0 1080 170" role="img" aria-label="Test levels over a feature's timeline">
|
|
<defs><marker id="ar3" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto"><path d="M0,0L10,5L0,10z" fill="var(--mute)"/></marker></defs>
|
|
<path class="edge" d="M120,140 H1060" marker-end="url(#ar3)"/>
|
|
<text x="120" y="162" class="mute">develop</text><text x="470" y="162" class="mute">end of step 1</text><text x="640" y="162" class="mute">review</text><text x="800" y="162" class="mute">step 3</text><text x="960" y="162" class="mute">merge</text>
|
|
<text x="10" y="44" class="t">Unit</text><text x="10" y="60" class="mute">on the machine</text>
|
|
<g fill="var(--build)">
|
|
<rect x="120" y="36" width="700" height="16" rx="8" opacity=".25"/>
|
|
<circle cx="160" cy="44" r="6"/><circle cx="220" cy="44" r="6"/><circle cx="290" cy="44" r="6"/><circle cx="350" cy="44" r="6"/><circle cx="420" cy="44" r="6"/><circle cx="600" cy="44" r="6"/><circle cx="680" cy="44" r="6"/>
|
|
</g>
|
|
<text x="830" y="42" class="mute">whole suite before every commit</text><text x="830" y="56" class="mute">each test under a second</text>
|
|
<text x="10" y="104" class="t">Integration</text><text x="10" y="120" class="mute">never on the machine</text>
|
|
<g>
|
|
<rect x="120" y="96" width="340" height="16" rx="8" fill="var(--mute)" opacity=".18"/>
|
|
<text x="290" y="108" text-anchor="middle" class="mute">compiled only: go vet -tags integration</text>
|
|
<rect x="470" y="90" width="110" height="28" rx="6" fill="var(--mesh)"/><text x="525" y="109" text-anchor="middle" style="fill:var(--bg)" class="t">try</text>
|
|
<rect x="770" y="90" width="120" height="28" rx="6" fill="none" stroke="var(--mesh)" stroke-width="2" stroke-dasharray="5 4"/><text x="830" y="109" text-anchor="middle" class="mute">try again if changed</text>
|
|
<rect x="920" y="90" width="120" height="28" rx="6" fill="var(--mesh)"/><text x="980" y="109" text-anchor="middle" style="fill:var(--bg)" class="t">merge check</text>
|
|
</g>
|
|
</svg>
|
|
</div>
|
|
|
|
<!-- ============ REVIEW LOOP ============ -->
|
|
<h2 id="review">The review loop</h2>
|
|
<div class="two">
|
|
<div class="box scroll">
|
|
<svg viewBox="0 0 520 300" role="img" aria-label="The review loop: at most three rounds">
|
|
<defs><marker id="ar4" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto"><path d="M0,0L10,5L0,10z" fill="var(--mute)"/></marker></defs>
|
|
<rect x="20" y="20" width="150" height="54" rx="8" fill="var(--card)" stroke="var(--idea)" stroke-width="2"/>
|
|
<text x="95" y="44" text-anchor="middle" class="t">Round n of ≤ 3</text><text x="95" y="62" text-anchor="middle" class="mute">code + tries + criteria</text>
|
|
<polygon points="300,20 380,47 300,74 220,47" fill="var(--card)" stroke="var(--mute)"/>
|
|
<text x="300" y="51" text-anchor="middle" class="t">Wrong?</text>
|
|
<rect x="400" y="20" width="110" height="54" rx="8" fill="var(--okbg)" stroke="var(--ok)" stroke-width="2"/>
|
|
<text x="455" y="44" text-anchor="middle" class="t">Approve</text><text x="455" y="62" text-anchor="middle" class="mute">Reviewed-by line</text>
|
|
<rect x="210" y="130" width="180" height="54" rx="8" fill="var(--card)" stroke="var(--build)" stroke-width="2"/>
|
|
<text x="300" y="154" text-anchor="middle" class="t">Same fixing agents fix</text><text x="300" y="172" text-anchor="middle" class="mute">answer per finding</text>
|
|
<rect x="20" y="130" width="160" height="54" rx="8" fill="var(--soft)" stroke="var(--side)"/>
|
|
<text x="100" y="154" text-anchor="middle" class="t">issue-tracker.open</text><text x="100" y="172" text-anchor="middle" class="mute">anything beyond the issue</text>
|
|
<rect x="300" y="236" width="210" height="54" rx="8" fill="var(--warnbg)" stroke="var(--warn)" stroke-width="2"/>
|
|
<text x="405" y="260" text-anchor="middle" class="t">After round 3: the operator</text><text x="405" y="278" text-anchor="middle" class="mute">what is left, with a recommendation</text>
|
|
<path class="edge" d="M170,47 H218" marker-end="url(#ar4)"/>
|
|
<path class="edge" d="M380,47 H398" marker-end="url(#ar4)"/><text x="384" y="38" class="mute">no</text>
|
|
<path class="edge" d="M300,74 V128" marker-end="url(#ar4)"/><text x="306" y="104" class="mute">yes</text>
|
|
<path class="edge" d="M210,157 C140,157 95,120 95,76" marker-end="url(#ar4)"/><text x="120" y="112" class="mute">n+1</text>
|
|
<path class="edge dash" d="M250,66 C200,100 170,120 150,128" marker-end="url(#ar4)"/><text x="178" y="96" class="mute">hardening,</text><text x="178" y="108" class="mute">a nicer form</text>
|
|
<path class="edge dash" d="M360,184 C380,210 400,220 405,234" marker-end="url(#ar4)"/>
|
|
</svg>
|
|
</div>
|
|
<div class="box">
|
|
<h3>What a round may ask</h3>
|
|
<p style="font-size:14px">Only what makes the change <b>wrong</b> against its issues, their acceptance criteria and its decisions. Anything more (hardening, a further case, a nicer form) becomes a new issue, and the round does not wait for it.</p>
|
|
<h3 style="margin-top:12px">What every round reads</h3>
|
|
<ul style="margin:6px 0;padding-left:20px;font-size:14px">
|
|
<li>Every criterion against its test: a test at its level that would fail without the change.</li>
|
|
<li>Every new integration test traced through the code, and its try's result.</li>
|
|
<li>That each integration test enters by the path the live trigger takes.</li>
|
|
</ul>
|
|
<div class="note" style="margin-top:10px">Why reviews took more than one round (R3b, 2026-10-19): a test that "failed" only by not compiling, a reader of the changed record the survey missed, and bus tests judged by reasoning before they ran. hq #433 changes all three.</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ============ ISSUE LIFE ============ -->
|
|
<h2 id="issues">An issue's life (playbook 03)</h2>
|
|
<p class="lead">The issue tracker (seat <code>issue-tracker</code>) holds every issue's number, status, owner, fix and links. Its <code>move</code> refuses a wrong move at the write.</p>
|
|
<div class="box scroll">
|
|
<svg viewBox="0 0 1080 230" role="img" aria-label="Issue statuses and the moves between them">
|
|
<defs><marker id="ar5" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto"><path d="M0,0L10,5L0,10z" fill="var(--mute)"/></marker></defs>
|
|
<g>
|
|
<rect x="20" y="70" width="120" height="50" rx="25" fill="var(--card)" stroke="var(--sym)" stroke-width="2"/><text x="80" y="100" text-anchor="middle" class="t">open</text>
|
|
<rect x="220" y="70" width="140" height="50" rx="25" fill="var(--card)" stroke="var(--warn)" stroke-width="2"/><text x="290" y="100" text-anchor="middle" class="t">diagnosing</text>
|
|
<rect x="440" y="70" width="120" height="50" rx="25" fill="var(--card)" stroke="var(--dec)" stroke-width="2"/><text x="500" y="100" text-anchor="middle" class="t">located</text>
|
|
<rect x="640" y="70" width="120" height="50" rx="25" fill="var(--card)" stroke="var(--build)" stroke-width="2"/><text x="700" y="100" text-anchor="middle" class="t">fixing</text>
|
|
<rect x="860" y="70" width="140" height="50" rx="25" fill="var(--okbg)" stroke="var(--ok)" stroke-width="2.5"/><text x="930" y="100" text-anchor="middle" class="t">resolved</text>
|
|
<rect x="440" y="170" width="120" height="44" rx="22" fill="var(--soft)" stroke="var(--mute)"/><text x="500" y="197" text-anchor="middle" class="t">wontfix</text>
|
|
</g>
|
|
<path class="edge" d="M140,95 H218" marker-end="url(#ar5)"/><text x="146" y="86" class="mute">by</text>
|
|
<path class="edge" d="M360,95 H438" marker-end="url(#ar5)"/><text x="366" y="86" class="mute">located-in</text>
|
|
<path class="edge" d="M560,95 H638" marker-end="url(#ar5)"/><text x="590" y="86" class="mute">by</text>
|
|
<path class="edge" d="M760,95 H858" marker-end="url(#ar5)"/><text x="770" y="138" class="mute">fixed-by, delivered,</text><text x="770" y="152" class="mute">proven · core: replay</text>
|
|
<path class="edge dash" d="M140,105 C300,150 520,150 640,108" marker-end="url(#ar5)"/><text x="300" y="150" class="mute">straight to fixing, with by</text>
|
|
<path class="edge dash" d="M500,120 V168" marker-end="url(#ar5)"/><text x="508" y="150" class="mute">reason</text>
|
|
<path class="edge dash" d="M700,70 C700,30 600,30 590,30 C400,30 120,30 90,68" marker-end="url(#ar5)"/><text x="320" y="24" class="mute">stopping: back to open, with a note of branch, last commit and what is left</text>
|
|
</svg>
|
|
<div class="legend"><span>A worker idle for a day may be taken over without a reason; otherwise <code>take-over</code> with a reason. An issue about a condition is linked to it, so the alert names the issue.</span></div>
|
|
</div>
|
|
|
|
<!-- ============ WHERE THINGS LIVE ============ -->
|
|
<h2 id="where">Where things live</h2>
|
|
<div class="box scroll">
|
|
<table>
|
|
<tr><th>What</th><th>Lives in</th><th>Written with</th><th>Never</th></tr>
|
|
<tr><td>Decisions (ADRs) and research efforts</td><td>the knowledge base (seat <code>knowledge-base</code>)</td><td><code>reserve</code>, then <code>propose</code> the record whole</td><td>a file in hq</td></tr>
|
|
<tr><td>Issues: status, owner, fix, links</td><td>the issue tracker</td><td><code>open</code>, <code>move</code>, <code>note</code>, <code>link</code></td><td>a status key in a report</td></tr>
|
|
<tr><td>An issue's narrative (design or governance only)</td><td>hq <code>04-ISSUES/NNN-…/</code></td><td>a pull request</td><td>its status</td></tr>
|
|
<tr><td>Designs, as-is and to-be</td><td>hq <code>03-DESIGN/</code>, status in frontmatter</td><td>a pull request</td><td>the two layers mixed</td></tr>
|
|
<tr><td>Playbooks, glossary, how-we-build</td><td>hq <code>00-META/</code></td><td>a pull request</td><td>a copy in a session's memory</td></tr>
|
|
<tr><td>The rules every session keeps</td><td>instruction sections (module <code>claude-code</code>)</td><td><code>claude_code_instructions_propose</code>, the operator's Approve</td><td>a hand-edited file</td></tr>
|
|
</table>
|
|
</div>
|
|
|
|
<!-- ============ ALL PLAYBOOKS ============ -->
|
|
<h2 id="all">All playbooks</h2>
|
|
<p class="sub">Open a card for its steps, what it produces and how it is checked.</p>
|
|
<div id="filters" style="display:flex;flex-wrap:wrap;gap:8px;margin-bottom:12px"></div>
|
|
<div class="grid" id="cards"></div>
|
|
|
|
<footer>Drawn from hq main f269e83 on 2026-10-19 and kept in hq beside the playbooks. The playbooks are the authority; this page is a picture of them, and a change to a playbook's steps updates it in the same pull request (not yet checked: issue 1222).</footer>
|
|
</main>
|
|
|
|
<script>
|
|
const P = [
|
|
{n:"01",name:"Research",kind:"idea",c:"--idea",
|
|
trig:"An idea worth investigating before it is committed to design. A symptom is not research.",
|
|
who:"The session that heard the idea leads.",
|
|
steps:["Read what is known: ask, get, the tracker's list.","Ask 3 to 5 setup questions, one at a time, each with a recommendation.","Check every option against the mission and how-we-build first.","Reserve a number (kind research), never pick one by hand.","Propose the effort: overview, questions, method, with links.","Research; findings in 04-findings, one version per settled set.","Try at the cheapest level that answers the question.","Write 03-domains: modules, seats, glossary words added or retired.","Write 05-direction: decisions, rejected options, acceptance tests.","Ask the operator to confirm the direction, one point at a time.","If the review asks changes: propose again, at most three times."],
|
|
prod:"A research effort in the knowledge base, status active, every version kept. Nothing in hq.",
|
|
next:"02",bounds:"Setup 30 min · proposing 15 min · each experiment 30 min · direction 30 min · report every 10 min",
|
|
prev:["Research that measured nothing","A recommendation the operator must argue back","Efforts copied into hq"],
|
|
check:"propose refuses rule breaks; the knowledge base reviews; records.py on every PR."},
|
|
{n:"02",name:"Graduation & design change",kind:"idea",c:"--dec",
|
|
trig:"A research effort concludes, or an existing design must change.",
|
|
who:"The lead, on the larger model; the decision is recorded before the design moves.",
|
|
steps:["Check the conclusion against mission, context and effect.","Reserve a number per decision.","Propose one record per choice that can be reversed on its own.","Pre-check, then propose siblings together.","On changes: revise whole; after three rounds fix findings yourself.","A finding that would change what was decided: ask the operator.","needs-operator: wait for the operator's Approve.","Amend the design, or create a to-be design with status designed.","Add the decisions, updated:, and the effort's glossary words.","Hand the design to the breakdown (04).","Close the effort: graduated with became links, or abandoned.","When it ships: write the as-is, set the to-be implemented."],
|
|
prod:"Decision records; a to-be design with frontmatter; glossary entries; later the as-is.",
|
|
next:"04",bounds:"One sitting of about an hour",
|
|
prev:["A decision rewritten silently","A deferral nothing tracks","A design amended without a decision"],
|
|
check:"propose refuses; records.py and index.py; set-wide rules raise conditions."},
|
|
{n:"03",name:"Issues",kind:"symptom",c:"--sym",
|
|
trig:"Something is wrong: an unenforced rule, a stated behaviour that does not happen, a silent failure.",
|
|
who:"Anyone opens; a worker claims it by moving it.",
|
|
steps:["Open it in the tracker: title, report; take the number it gives.","Tracker unreachable: take no number, say so.","A design or governance issue: a folder in hq with the report.","About a condition: link the condition's key.","Move to diagnosing with by, before investigating.","Note the trail; a folder issue also gets a dated diagnosis.","Move to located with located-in, a registered owner.","Move to fixing with by, before fixing.","Name fixed-by at the merge; stay fixing until proven live.","Move to resolved; a core issue gives replay or replay-none.","A design gap: run 02 and give amended-design.","Not going to be fixed: wontfix with a reason."],
|
|
prod:"A tracker record with notes and links; for a design issue, its narrative folder in hq.",
|
|
next:"02 · 07 · 08",bounds:"A worker idle for a day is stale",
|
|
prev:["Numbers that collide","Two agents starting the same work","Status held in two places"],
|
|
check:"The tracker's move refuses at the write; cycle.py checks the folders."},
|
|
{n:"04",name:"Work breakdown & handoff",kind:"idea",c:"--dec",
|
|
trig:"A to-be design is settled and its building is about to start.",
|
|
who:"The lead who will run the feature, on the larger model.",
|
|
steps:["Confirm status designed; each decision traces to a record.","An open question remains: back to research.","Set code: to the owning repositories.","Read the as-is; write it first if it does not say what is replaced.","Write the Milestones: outcome, acceptance tests, repositories.","Cut milestones by release order; fold any not usable alone.","Ask the operator to confirm the milestones.","Open one issue per repository part, linked depends-on.","Write the issue numbers into the design; flip to in-progress.","Hand each milestone to 07, one at a time, one design at a time."],
|
|
prod:"Milestones in the design; issues per repository part; the design in-progress.",
|
|
next:"07",bounds:"One sitting · one design built at a time · a milestone is one release",
|
|
prev:["Building without a breakdown","Building against an undocumented as-is","Deferrals with no owning issue"],
|
|
check:"Read by the lead and the reviewer; a check is tracked by issue 845."},
|
|
{n:"05",name:"Constitution sync",kind:"side",c:"--side",
|
|
trig:"how-we-build.md changed a rule the mesh enforces at runtime.",
|
|
who:"Whoever made the change.",
|
|
steps:["Edit how-we-build; the reasoning stays there.","Record the change as a decision (02).","Publish the derived page under its slug.","Keep the section numbering stable.","Read the page back; verify the change is there.","Note the sync in the record's consequences."],
|
|
prod:"A decision record; the derived constitution page injected into design sessions.",
|
|
next:"02",bounds:"no time bound",
|
|
prev:["Drift between the reasoned and the enforced copy","A publish that said success and did nothing","Edits to the derived page that vanish"],
|
|
check:"The read-back in step 5."},
|
|
{n:"06",name:"Writing a module",kind:"side",c:"--side",
|
|
trig:"Something that runs today must run on the mesh.",
|
|
who:"Whoever ports or writes it.",
|
|
steps:["Read what runs: compose file, environment, where data sits.","Name what it provides and requires.","Declare capabilities for machine facts.","Write resources in the order they must happen.","Every secret in a file, never in env.","Pin every image by digest.","New: generate the credential; adopted: accept it.","Prove it against the real software: auth, refusal, restart."],
|
|
prod:"A module declaration, optionally a provisioner, and its proof.",
|
|
next:"07",bounds:"no time bound",
|
|
prev:["A service running with no password","A new password locking out adopted data","A test that cannot fail"],
|
|
check:"Example tests refuse the env-file mistake; review."},
|
|
{n:"07",name:"Features",kind:"build",c:"--build",
|
|
trig:"Work that changes code across issues or repositories, too large for a small fix.",
|
|
who:"A lead (larger model), one fixing agent per repository (smaller), one reviewer (larger).",
|
|
steps:["Prepare: name the branch, make the worktrees, settle every question.","Write one context file per repository.","Write the findings and acceptance criteria into each issue.","Survey every other reader and writer of each changed value. (new)","Find release orders; copy fixtures from the real writer.","Develop: one agent per repository, one commit per issue.","A test fails first on behaviour, not on compiling. (new)","Bring each branch up to the trunk, push, and try it. (new)","Review all of it with the tries' results; at most 3 rounds.","Push the reviewed heads; try again only what changed.","Open one pull request per repository, together, with Reviewed-by.","The operator's Approve on Telegram; the group merges in one pass.","Follow the delivery plan to done; resolve the issues; clean up."],
|
|
prod:"One pull request per repository, one delivery plan, resolved issues, no branch or worktree left.",
|
|
next:"02 (when it ships)",bounds:"Prepare 15 min · 30 min per issue · review 15 min, ≤ 3 rounds · one try and one check",
|
|
prev:["Pull requests as scratchpads","Context bought again and again","Review rounds spent on what a run would show"],
|
|
check:"The forge's merge-gate and repo-check; the review record; the tracker's move."},
|
|
{n:"08",name:"Small fixes",kind:"build",c:"--build",
|
|
trig:"One issue in one repository, about an hour of work.",
|
|
who:"The session, with one reviewer on the larger model.",
|
|
steps:["Read the issue and the code; settle any open question.","Write the acceptance criteria.","Move the issue to fixing.","Build on fix/<issue>-<slug> in its own worktree.","A test that fails today first, on behaviour. (new)","Push and try before the review. (new)","One review of the commit and its try.","Push the reviewed head; open the pull request.","The operator's Approve; the merge; the delivery plan.","Prove the fix live and resolve the same hour."],
|
|
prod:"One pull request, one delivery, a resolved issue.",
|
|
next:"—",bounds:"About an hour · one try before the review, one review, one check",
|
|
prev:["A fix reviewed after its pull request opened","A long part run twice","A fix waiting on someone else's delivery"],
|
|
check:"The forge's checks; the review record; the tracker's move."},
|
|
{n:"09",name:"Cleanup",kind:"side",c:"--side",
|
|
trig:"The operator asks, or merged branches, stale pull requests and old worktrees pile up.",
|
|
who:"A session on the operator's word; the operator decides every deletion.",
|
|
steps:["Take stock, read-only.","Write one report: item, class, evidence.","Classify: safe, likely safe, kept, unknown.","Report stale pull requests; never close them.","Ask: delete the safe items as listed?","Delete only the approved list; never force.","Report what was cleaned, kept and unknown."],
|
|
prod:"A stock report, the approved deletions, issues for missing tools.",
|
|
next:"03",bounds:"A worktree is safe only if untouched for 2 h · a PR is stale after 3 days",
|
|
prev:["Deleting unfinished work","Deletion by name or age alone","Missing tools left as session notes"],
|
|
check:"The report names the evidence for each deletion."}
|
|
];
|
|
const kinds={all:"All",idea:"From an idea",symptom:"From a symptom",build:"Code changes",side:"Alongside"};
|
|
let current="all";
|
|
function esc(s){return s.replace(/&/g,"&").replace(/</g,"<")}
|
|
function render(){
|
|
const g=document.getElementById("cards"); g.innerHTML="";
|
|
P.filter(p=>current==="all"||p.kind===current).forEach(p=>{
|
|
const d=document.createElement("article"); d.className="pb"; d.style.setProperty("--c",`var(${p.c})`);
|
|
d.innerHTML=`<div class="num">PLAYBOOK ${p.n}</div><h3>${esc(p.name)}</h3><p class="trig">${esc(p.trig)}</p>
|
|
<div>${p.bounds.split(" · ").map(b=>`<span class="tag">${esc(b)}</span>`).join("")}</div>
|
|
<details><summary>Steps (${p.steps.length})</summary><ol>${p.steps.map(s=>`<li>${esc(s).replace("(new)",'<span class="tag new">new</span>')}</li>`).join("")}</ol></details>
|
|
<details><summary>Produces, prevents, checked by</summary><div class="kv">
|
|
<b>Who</b><span>${esc(p.who)}</span><b>Produces</b><span>${esc(p.prod)}</span>
|
|
<b>Then</b><span>${esc(p.next)}</span><b>Prevents</b><span>${p.prev.map(esc).join("; ")}</span>
|
|
<b>Checked by</b><span>${esc(p.check)}</span></div></details>`;
|
|
g.appendChild(d);
|
|
});
|
|
}
|
|
const f=document.getElementById("filters");
|
|
Object.entries(kinds).forEach(([k,v])=>{const b=document.createElement("button");b.type="button";b.className="chip"+(k===current?" on":"");b.textContent=v;
|
|
b.onclick=()=>{current=k;[...f.children].forEach(x=>x.classList.remove("on"));b.classList.add("on");render()};f.appendChild(b)});
|
|
render();
|
|
const root=document.documentElement;
|
|
try{const t=localStorage.getItem("pb-theme");if(t)root.dataset.theme=t}catch(e){}
|
|
document.getElementById("theme").onclick=()=>{const dark=root.dataset.theme?root.dataset.theme==="dark":matchMedia("(prefers-color-scheme: dark)").matches;
|
|
root.dataset.theme=dark?"light":"dark";try{localStorage.setItem("pb-theme",root.dataset.theme)}catch(e){}};
|
|
</script>
|
|
</body>
|
|
</html>
|