Repository navigation
Releases: phased-execution-public/phase-console
Release list
v9.0.0
The session made cheaper, measured. 9.0 is the work of plan measured-console: the skill is a
core and three mode files, a supervised session is told what it starts from at its start, the gate
runs every suite once and a phase verifies what it changed, the plugin is the skill alone, and a
phase session boots on 55K tokens where 8.0's booted on 111K. The free tree stays MIT. PE_API is
3: nothing an 8.x plan, session or script says changes meaning, except where Migration says so.
Migration
Every entry names the file that owns what it changes. Nothing an 8.x plan says changes meaning: each new
plan line below is optional, read by both engines, and silent where a plan says nothing.
- The skill is a core and three mode files (
PE_API3,PE_API_MIN2).SKILL.mdis the console
contract, the modes as pointers and the guardrails (under 16 KB); Mode 1–3 are
references/mode-plan.md,references/mode-phase-start.mdandreferences/mode-phase-finish.md,
each read whole in its mode; the helper catalogue isreferences/scripts.md, and the engine prints
its own (phase-graph.sh --help,--explain,--section); the maintainers' scripts are
docs/maintainers.md. A console atPE_API3 still runs a skill at 2, and a console at 2 runs this
skill, so the 8.0 plugin and an updated console work together either way round. A flag added to a
session-facing script is now a stamp bump, held byviewer/test/skill-api-ratchet.test.ts. Owner:
scripts/skill-api.env. - The plugin is a materialized tree, and it carries its own hooks. The skill installs as a
skill-only tree of about 2.0 MB, andassets/console.gifis gone (the READMEs link the v8.0.0 tag's
copy).hooks/hooks.jsoncarries the four presence events, soinstall-hookswrites nothing over a
config dir whose installed plugin already has them;phase-console install-hooks --splicekeeps
splicing them intosettings.json, and while a spliced entry exists the plugin's copy stays silent
(PE_PLUGIN_HOOKS=1overrides).phase-console update-plugininstalls the tree (--tree).
Owners:scripts/build-plugin-tree.mjs,hooks/hooks.json,viewer/server/hooks-install.ts. autopilotLoadsdefaults toconsole-set: an unattended session loads the console's own skill
tree and the plugins inautopilotPlugins(default none,name@marketplaceonly), under
--setting-sources project,local— the operator's user-level plugins, hooks and settings no longer
load; their deny rules still do. SetautopilotLoads: operator-configto keep 8.0's behaviour.
Owner:viewer/shared/prefs-model.js(resolved byviewer/server/runner/loads.ts).- The run's hooks block on failure (
onFailure: "block"on every hook the console registers): a
hook that errors refuses the call instead of letting it through. The Stop hook's refusal now takes
effect, so a session ending on red fast lines is held once;- **Fast gate:** offon a phase turns
that off. Owner:viewer/server/runner/approvals.ts(HOOK_ON_FAILURE). - The gate's marker is a record in the common gitdir. It was a bare sha at
git rev-parse --git-path phase-console-gates-ok; it is nowphase-console-gates-okand
phase-console-gates-ok.jsonundergit rev-parse --git-common-dir, so a linked worktree counts for
the repository. Only a green full-matrixgates.shrun on an unmoved, clean HEAD writes it (a dirty
tree exits 2; a phase-tier run writes nothing), and the pre-push hook and the release's
--gates-passedcheck refuse a bare sha or a record whose stages do not covergates.sh --list. A
.build-revthat is not HEAD fails both tarball gates. Owner:scripts/gates-marker.sh(written by
scripts/gates.sh). - New optional plan lines:
**Subagent model:**,- **Fast gate:**and- **May delete tests:**
(measured-console phase 6),- **Skills:**(11),**Verification tier:**(15),**Evaluator:**
(22),**Canary:**(24),**Plugins:**/- **Plugins:**(27),- **Out of scope:**,## Not yet specifiedand- **Amendment:**(30) — each with a fixture, and each printed by its
phase-graph.shmode. Owner:references/plan-format.md(read byscripts/phase-graph.shand
viewer/server/parse/plan.ts). - The session model is re-fit: 55K + 183K + 1.91 × weight, where it was 121K + 198K + 2.17
(reports/p33-after.json), so--session-planbatches more weight into a session than it did under
8.0. Owner:scripts/sizing.env. humanStepModedefaults todeferon a CLI at 2.1.306 or later (andparkbelow it): a run
that says nothing now pauses a person's step at its call instead of ending the turn. Set
humanStepMode: parkon a run to keep 8.0's behaviour, which is byte-identical in the journal.
Owner:viewer/shared/run-settings.js.compactiondefaults toconsole: nothing changes until a run choosesnativeoralternate.
Owner:viewer/shared/run-settings.js.- The presence hook's SessionStart entry waits 15 seconds (the brief). An install from before
readsbrief: falseinhooks-status— presence still works and the boot prompt stays whole — until
phase-console install-hooksrefreshes the entry in place. Owner:viewer/server/hooks-install.ts. - A run's journal is a chain at its cap (measured-console phase 17): at 32 MB
run-<id>.jsonlis
renamedrun-<id>.jsonl.<n>(n from 1) and indexed inrun-<id>.jsonl.index—{n, file, firstSeq, lastSeq, bytes, offset}per segment — and the fresh file opens withjournal.rotated.seqruns on
unbroken and every reader follows the chain; a tool that parsedrun-<id>.jsonlalone should read
the index's segments first.journal.fullapplies only past eight segments. Owner:
viewer/server/runner/journal.ts. - A run's record persists incrementally (
persistModel2):run-<id>.jsonis the base, and a save
appends the records it changed torun-<id>.json.delta— one fsync — rewriting the whole file at
most every minute, on a status change, or past a 4 MB delta. The first 9.0 boot rewrites every stored
run once into the new shape and journalsrun.persist-v2on it. A tool that parsedrun-<id>.json
by hand sees every status change at once and other fields within a minute; fold the delta's lines
past the base'spersistGenfor the rest. Owner:viewer/server/runner/state.ts. - Runs that never finished age out: a halted, parked, paused or interrupted run untouched for
runRetainDays(30) is pruned with its files, as a finished one always was. Owner:
viewer/server/retention.ts. - Webhooks are signed and queued: every POST carries
X-Phase-Console-Signature: sha256=<hmac>
over its body with the destination's secret (shown once at add; older rows get one at first boot),
and a destination in backoff holds every announcement and delivers them in order when it is back.
Owner:viewer/server/webhooks.ts. validate.sh's size warning is about the handoff's body now, not its start section: a long
generated section over a short body says nothing. Owner:scripts/validate.sh.- Words settled where they live: nineteen decision keys everywhere (
issuesincluded); an
unsupervised declaration lands asoutcomes/phase-NN-<stamp>.json; a lock's branch AND tree carve
two intersecting scopes apart;**Wait budget:**is the total one phase may spend parked; the
default target model isclaude-opus-5-5; Haiku is for subagents (a 200K window cannot hold a phase
session's floor);BUDGET_BIG's note says it is a preset, not a measurement; a session commits after
each green step and leaves pushed history alone; the plugin listing's description is a paragraph,
not a changelog. Owners:references/plan-format.md(the keys and the plan lines),
scripts/models.env(the model ids) andscripts/sizing.env(BUDGET_BIG).
Added
- A person's step pauses the session at its call and resumes it in place. Under the run setting
humanStepMode: deferthe hook defers thephase-outcome.sh … needs-humancall itself, the console
raises the item from the call, and when the person's check passes the SAME session carries on from
that call: the script prints the answer (--resumed-with) and declares nothing.parkkeeps 8.0's
park-and-resume.phase.deferred,phase.deferred-resumed. - A session's outcome arrives typed. Every session that can declare runs under
--json-schema
(viewer/shared/outcome-schema.js, which owns the file's shape too); itsstructured_outputis read
before the outcome file, and a file naming a different word journalsphase.outcome-disagreed. The
script keeps writing the file, so a hand session declares exactly as before. - Native compaction is an arm.
compaction: console | native | alternateandcompactionWindow
(350,000): undernativethe session getsCLAUDE_CODE_AUTO_COMPACT_WINDOW, the console's
PreCompact/PostCompacthooks journalphase.compaction(before, after, the re-cache), the 0.8×
checkpoint is off and the 0.6× line is a notice;alternategives odd phasesnative. A resumed
session's ownSessionStartcost is kept (phase.resume-cost) and quoted by the next resume brief. - A wrap-up pushes what is green. The 0.6× notice says commit AND push where the plan's
permission.destructiverow allowsgit push origin <branch>;phase-outcome.sh partialwarns of
every scope tree ahead oforigin/<branch>; a checkpointed session's fast-forward is pushed by the
console (to the origin it had before the session, under the hardened git); and the phase shows its
unpushed commits — the inspector's Unpushed and a heads-up on Your turn — until a push lands (#397). - A supervised session is told what it starts from at its start, not in its prompt. The presence
hook prints the phase's brief at SessionStart —phase-graph.sh <slug> --brief N: the mode f...
v8.0.0
Every open issue, fixed. 8.0 fixes every issue that was open on 2026-10-17: Your turn becomes a
page you work from and the eighth destination, the phase graphs become instruments, any MCP server
signs in on the page, the runner's mirrors, lanes, landing, watches and gates stop losing work, the
load guard reads the machine's real CPU, and the console's own git is hardened. The free tree stays
MIT. PE_API stays 2: nothing a 7.1 plan, session or script says changes meaning, except where
Migration says so.
Migration
- The accounts registry is v2 in both editions. A v1
accounts.jsonis upgraded in place on its
first read and copied once toaccounts.json.v1.bak. - A push is read by its clause. A bare
git pushis nowdeny. A trunk (main,master,
trunk, the run's base) is pushed only where apermission.destructiverow names it with its
phases (`main` by phase 34); a negated phase list (never,not,no,except,!)
excludes those phases, and a clause with only negated phases grants nothing. A forced or deleting
push,--no-verifyor another remote asks a person even under a wholeBash(git push:*)rule. - The console's own git ignores the system config (
GIT_CONFIG_NOSYSTEM=1) and runs no hooks
(core.hooksPath=/dev/null). A credential helper set only in the system config (macOS
osxkeychain) is no longer seen: setcredential.helperin~/.gitconfig. - A plan's
**Branch:**line names a branch only in backticks (`pe/<slug>`); bare prose
names none. A repeated### Phase Nsection fails the lint (F21phase-section-repeated), and the
JS parser joins it under the first. decisions.shrefuses a--valueover 500 characters, an--evidenceover 200 and a--by
over 64 (exit 2) instead of cutting them.- The landing ledger moves forward only:
phase-landing.shkeeps a row that is further along and
says so on stderr, unless--allow-backward. close-plan.sh --status completerefuses while the plan's work is unlanded, open or unfiled,
naming each item;--forcecloses it anyway and records how many were open.- A press from a page of an older release is refused with a 409 and a sentence asking for a
reload (x-build-rev). The CLI, the scripts and the hooks send no such header and are never
refused. - The load guard's
loadGuardFactorkeeps its name and changes its meaning: it now scales a CPU
reading (85 % busy over 30 s at factor 1.5), and the 5-minute load average is only a ceiling at
factor × cores × 2. - Your turn is the eighth destination. The phone's tab bar reads Runs · Your turn · Plans ·
Sessions · More; 7.1's?view=compacton Your turn reads as the list. phase-outcome.sh verifiedwritesrepoandrepo_tree, so a proof is keyed to its own
repository.- New state:
mcp/registry.jsonunder the instance's state directory (the MCP registry's
24-hour cache).
Added
- Your turn is a page you work from (#377, #366, #365, #334): the eighth destination and every
plan's fourth view (#/turn,#/plan/<slug>/turn), with tabs (Open · Done · Handled by the AI ·
All), four views (list, board, cards, timeline), five groupings and three sorts in the address; every
status word explains itself on hover, focus and tap; the bell's drawer is a five-row preview with
Put back (POST /api/notifications/unread). - The phase graphs are instruments (#376, #364): the map's filter chips, trace, colour, layers,
layouts, hover card and SVG or PNG export, each in its address; two new readings, Schedule and
Burn-up; the timeline's zoom and quick ranges; and a lock map on Sessions ▸ Locks. - Tables and cards that say more (#361, #360, #369, #362, #305): an outlined status badge, a plan
track drawn to proportion with its legend, Sessions rows with what each runs on, its account, model,
spend and context, a summary strip, the 6.x palette back as Settings ▸ Appearance ▸ Palette
(quietordepartures), and the halt card on the Runs home. - Any MCP server, signed in on the page (#363, #372): the open catalog searched here and cached,
Add by hand for any server, Fill it in for a${VAR}, and Sign in told step by step;
phase-console mcp list | add --manual | fill. - Waive this line (#267): an operator waives one red verification line for one phase
(POST /api/run/<slug>/verify-waive), listed under Waived by the operator. - Verification lines read Timed out and Skipped as well as green and red (#331).
- Two proof lints, both advisories: F45
proof-unparsable(aHuman step:proof the check
cannot read) and F46proof-script-run-branch-only(acmd:proof whose script is not on its
repository's trunk). - Two halts that name the tree:
run-tree-config(a session changed its tree's git
configuration) andrun-tree-locked(a git lock file nothing is writing blocks the tree). - The doctor says more (#343, #279): a deliberately disabled launchd unit is told from a broken one
with the line that enables it, and a check it cannot see is said, not passed.
Changed
- The console's own git is hardened (#272), and a push is read by its clause (#276, #275) — see
Migration. - The load guard reads the machine's real CPU (#243, #223): busy over 30 seconds, every console's
work and in-turn bursts counted. A heavy verification line waits for a seat in one machine-wide
budget, journalledphase.verify-queued(#327), and a session's orphaned test runners are reaped at
its end (#222). close-plan.shclosescompleteonly once the work has landed (#320).- A handoff's ▶ Start next phase(s) section is a pointer to
next-phase-prompt.sh, composed when
read (#338);new-handoff.sh --forcerewrites the INDEX row it supersedes (#265); a note keeps its
indented sub-bullets (#378); and every watch scheme is documented (#233). - Switching a run's account is a run act, under
--allow-runalone (#323). - Every CLI verb reaches the CLI (#359), and a release packs the client it names (#358).
- The boot prompt's wait procedure names the
pgrep -ftrap (#260): a wait on text in its own
command never ends; wait on the job's pid or a file it writes.
Fixed
- Your turn's presses and proofs (#368, #367, #371, #351, #374, #345, #350): Send my decision
sends once and then checks the proof; an item bound to a gate closes when the gate opens; a reminder
keeps a returned item on Redo; a proof whose script is missing reads Cannot run instead of
failing you; and one reader reads everycmd:proof. - Your turn's inputs (#346, #349, #393): a
Human step:inside an HTML comment is ignored,
--needs issuesis classified as itself, and the page's touch targets are full size. - The graphs draw no text over text (#375, #299, #342, #306): below 9 px only numbers show, marks
that share a spot read✎×4or●+3, the track sits behind its labels, a Runs row keeps its count
on one line, and the run clock counts frozen time once. - Small things on screen (#339, #335, #315): a chart's heading is named, short code tokens do not
break, and a dropdown fits the screen. - An update never paints the old release (#373), this browser is found by its push endpoint
(#325), a placeholder delivery counts as no device and no failure (#314, #324), and a repaired push
clears its health row (#280). - Mirrors and verification (#356, #357, #311, #301, #328, #333, #262, #283): a run's mirror grows
when its plan adds a repository, a docs-only line runs where the docs are,Verify in: .means the
plan's tree, a proof is keyed to its repository, an orphanedindex.lockis named, a resolved run's
trees are swept, the artefacts register credits only a phase's own trees, and an unexpanded$VAR
is never taken for a path. - Lanes and landing (#354, #295, #370, #336, #249, #240): a phase keeps its lane while it holds
work in the shared tree (run.lane-held), the landing proof fetches and recognises a squash or
pull-request merge, and the landing ledger never moves backward. - The lint and the decisions (#225, #228, #226, #235, #322, #253): a sibling's half-written
handoff no longer fails your lint, a repeated phase section does, a person's resume re-reads the
decisions, the Branch line is read right and a shared launch asks about it, and an over-long
decision is refused rather than cut. - Watches and gates (#337, #303, #245, #264, #239, #288): a watch beside a long
cmd:row is
asked, Resume re-asks a settled wait, Recheck works on a waiting ref, a live run re-reads its held
gates, and a phase whose dependency is not done is held, never boarded (phase.dependency-held). - Detectors and in-turn waits (#219, #220, #259, #260, #292, #274): a phase serial behind a live
lane is not undriven, the watchdog holds while a tool card stands, a turn ends without a ten-minute
idle wait, a wait loop whosepgrep -fmatches itself is refused, a wait on a detached job's file
arms its owncmd:ref, and a git ref after "held by" is not read as a lock. - The inbox answers without lag (#237).
- Accounts (#238, #287, #281): a run's start goes to the first account with room, the usage brake
keys to the account a session really spends, and a pinned model is checked on every frame, so a
fallback mid-session parks the phase. - The tests are hermetic and say why they fail (#353, #352, #344, #294, #291, #297, #329, #326,
#316, #394, #289, #302, #313, #241, #318, #246, #256, #230):tests/run-tests.shscrubs every
PE_*andDOCS_ROOTbefore bats, a test process's usage poller reads no meters unless given one,
and a test file that stops mid-run fails the gate.
v7.1.0
Your turn, told step by step. 7.1 makes every item on Your turn a card a person can act on without
reading anything else: one of four types — an Approval, a Task, a Decision or a Heads-up — with
numbered steps, NOW or NOT YET and when, a timing grid, where a press goes and who checks it, and one
act form for its type. Every word on the card comes from one composer, so the bell, the Runs home, the
lock screen and the page say the same thing. What only the run can do, what went stale and what the
AI could do itself no longer reaches a person as a chore. The free tree stays MIT. Nothing a 7.0
plan, session or script says changes, and PE_API stays 2: an item a 7.0 console raised keeps its id
and state and gains the new words when it is read.
Migration
- The page's groups are v2 (
TURN_GROUPS): Approve · Do now · Decide · Being checked ·
Coming up · Done, placed by the item's type and readiness, with Handled by the AI, Heads-up and
Where the plans stand as sections. 7.0's Needs one detail from you is gone: such an item is a Task
on Redo in Do now. GET /api/turngains the card contract on every item —type,personStatus,readiness,
route,checkedBy,timing,steps,verdictandverdicts, andoptionsorapprovalwhere they
apply — plusstripandheadsUpon the answer. Every field 7.0 sent is still there.- A plan's
Human step:bullet takes three more keys,options:,recommended:anddeadline:,
validated by both parsers and printed by neither, sophase-graph.sh --human-stepsis byte-identical;
itsguide:key is now read as a file under the docs root (F42guide-file-missinggates). - New routes:
POST /api/human-steps/:id/undoandPOST /api/run/:slug/run-request(an authority
route). A run verb pressed from a card carriesexpectRunIdand answers 409 with a sentence when the
plan's run is another one. - New state files under the instance's state directory:
turn-first-seen.json(when a projected
item was first seen) andturn-settled.json(how a pressed projected item ended).
Added
- The card (
client/src/cards/turn/): NOW or NOT YET with its when, the type's mark, the
person's status (To do, Checking, Passed, Redo, Not doing, Closed, Waiting for the
owner), the title that names the act and its meta, a timing grid on one page clock (Assigned, Do
it, Deadline, Open until, You sent it, Checked), why only you, once, the verdict before the
steps, From the AI, the numbered steps, How it's checked, Goes to and Checked by, and one act
form per type, its primary chosen by one move table. Every command a person runs is one command
block, and a command with a<placeholder>is a template to fill in. - The page: a strip (to do, checking, passed today, the next deadline) under the headline; one
toolbar row whose Filter (Status, Time, Plan, Type, Goes to), Group, Sort and View live in the
address; Heads-up; and Where the plans stand, one line per plan.#/turn/<id>marks its card and
opens the panel on it where the panel stands beside the page. - One composer (
server/turn/compose.ts) writes every word of every item and of its inbox row
and push. Steps come from the authored guide, else the plan's errands row, else the situation's own
words, else a minimal guide from the link or command and the check said in words
(shared/proof-words.js); no proof ref, route or "phase 0" reaches a person. - Plan-born guides: a
Human step:bullet'sguide:file is read, and a plan's## Operator errandstable is read by its headers; a bullet ending in(E-N)takes that row's steps, and a row
no bullet names is a Heads-up. New advisories F41human-step-no-guideand F43
errand-row-unbound, and the gate F42guide-file-missing. - What each situation asks: every ask the console raises carries a title, steps, a check and
options bound to their verbs; a gate, a plan approval, a permission and an owner request carry an
approval block (what, who asked, if approved, if denied); an identity park offers exactly its two
choices. - Where a press goes: Undo: I'm not finished takes back a running check; a press that ends an
item waits a few seconds on the page with Undo; Hand it back gives an item back to its phase
(Give back to Phase N), asks its session a question, or says you cannot; a pressed approval card, gate,
plan approval or question leaves a record, so Done shows how it ended, by whom and when. - Run requests:
phase-outcome.sh … blocked --needs run --run-act <act>asks the run to relaunch
isolated, switch account, raise a phase's budget, stop or hold — one Approval per run and act, every
asking phase a waiter; Approve takes the act and resumes them, Deny tells them no. - Items that close themselves: a stopped run a later run of its plan supersedes is resolved by
itself (journalrun.superseded, undone byunresolve), and the round closes its run-specific items,
those of a phase the board reads done and those whose errand is gone — each with a row under
Handled by the AI — while a sign-in, a secret or an errand at the machine moves to the new run. - Every surface in the card's words: the Runs home's and the bell's rows lead with NOW or NOT
YET and the type, and a push's lock screen is chosen by type — a Task Open and I did it, a run
request Approve and Deny on a console with--allow-run. - The help guide's Your turn, in English and Persian, explains the four types, the statuses,
NOW and NOT YET, the timing grid, Goes to and Checked by, Undo, Hand it back and run
requests; the README, USAGE,viewer/README.mdandreferences/turn.mdsay the same. - Tests: every card the console can raise is walked in both editions and held whole
(viewer/test/turn-cards-coverage.test.ts), and the tower rehearsal drives a card's round trips on
real consoles.
Changed
- A permission is approved at a scope — Approve, this call to Approve, always — and sits in
Approve, where Grant every low-risk ask now stands. - A decision's button is Send my decision, and nothing is chosen for you.
- The identity park skips a superseded run and never overwrites an operator's stop.
- An operator's act is an act of its phase: two phases asking for one command are two items,
each on its own clock (#261, #257).
Fixed
human-step-model.test.tsexpects the births there are (#271).- An inbox row no longer reads a human step's window as a question's countdown (#341).
- An act due at a date ahead reads Coming up, not notified at once (#255).
- An answer pressed while its run is still parking reaches the run (#273), and an in-process
resume through an automatic door no longer takes the person's slot (#268). - Your turn's panel says a person's reason once (#332).
- A section heading's count keeps its gap right to left (#296).
- A run request a live run declares raises its item before the run reaches disk, an answer that
arrives first raises the item and answers it, a hold released on a parked run resumes the phase that
waited, and a Done run request keeps its approval block.
v7.0.0
Quiet. 7.0 rebuilds the console's web client from zero around one question — does anything need
me? — answered in two seconds and fixed in one move, with everything else one expansion away. Seven
places (Runs · Plans · Sessions · Repo · Insights · Logs · Settings) under one shell; one line per
thing, a status drawn as a dot and a word, and three doors to depth on every page — expand in
place, inspect in the panel, and Everything, a per-page switch that shows every column, fact
and section. Every option the server has now has a door a person can reach: a registry names each
route, event, setting and operator verb with the page that offers it, and a test holds it to the
server both ways. The whole client works on a phone and a tablet, mirrored right to left, and stilled
for anyone who asks for less motion. The free tree stays MIT. The server, the runner,
the scripts and the skill are the same as 6.2's: nothing a 6.2 plan, session or script says changes,
and PE_API stays 2.
Migration
- Every 6.2 address still lands, in one hop, with its query (
client/src/shell/redirects.ts):
#/now…and#/readyon the Runs home,#/queueon its Queued group,#/approveon Your turn,
#/debug[/<section>]on Logs, a 6.2 Runs bay (?bay=) on its group (?group=). A bookmark, a push
and a link a session wrote keep working. - Debug is Logs. The place is
#/logs/<section>— Logs, Journal, Delivery, Health, Access — and
its chunk islogs-*. - The run page folds into its plan. A run is a plan's Run view,
#/plan/<slug>/run, with its
earlier runs at?view=history; the Tower's six bays are the Runs home's groups. - New read routes, no flag and no journal line:
GET /api/prefs/schema(every preference with its
type, default, section, description and where its value came from),GET /api/ceiling,
GET /api/credentialsandPOST /api/credentials/probe,GET /api/doctor/strays,
GET /api/sizing,GET /api/report,GET /api/run/:slug/proofsand
GET /api/run/:slug/verifications. - A preference can be reset. Settings ▸ Everything resets a key to its default, and a write keeps
only what the file held, what the write set and what differs from the default — so a reset leaves
the file rather than pinning the default in it. - First paint is held at 170 KB served (it was 190), in this build and the free one: the free
tree's gate now runscheck:diston its own client build. - The 6.x client is gone.
viewer/client/src/holdsui/,cards/,shell/,screens/and
data/besidelib/,styles/andcontent/; the 6.x app, features, components and lib/api
directories and lib/queries.ts no longer exist. A patch or a fork against a 6.x client file has
nothing to apply to.
Added
- The map: seven places in three bands (
DESTINATIONSinshared/route-meta.js), the same in
both editions. The rail from 900 px carries the instance switcher, the seven places, Help and the
account meter; the header the page title, the situation words, search (⌘K) and the bell; below
900 px a tab bar — Runs · Plans · Sessions · Insights · More. - Three doors on every page. Expand opens a row where it stands and remembers it. Inspect puts a
record in the panel — a column at 1200 px and up that stays open across pages, a sheet from 900 to
1200, a bottom sheet on a phone — on four tabs, Overview · Actions · History · Raw. Everything is a
per-page switch at the end of the toolbar. Every list's toolbar is one row in one order: search ·
Filter · Group · Sort · View · ⋯ · Everything, and one View sheet on a phone. - The Runs home (
#/runs): the situation sentence as its heading, then Your turn, Running,
Waiting, Queued, Ready to start and Settled today, one line per run with exactly one action — a
halted run's comes from its halt first (#278) — and All runs as a table view. - Your turn (
#/turn) on the 7.0 cards: the headline and the round, six sections, every item's
explanation in the panel (GET /api/turn/:id) and Withdraw among its moves. - Plans and a plan. The library is one line per plan — mark, name, line, sessions left, moved —
as a list or cards, with closed plans and documents folded. A plan opens on its glance over three
views: Phases (one table with the spine in its identity cell, and the route map), Run (the run's
folds: sessions and their consoles, where the time went, why it started and what it cost, what the
ladder tried, ways forward) and Source. - One launch form with a quick view: the departure line and its route, the findings in order, the
turns it will ask for, presets, and nine rows that open in place at every width. - Sessions (
#/sessions): every lane, terminal and Claude session on one grid, a session full
height, and the locks with their holders, narrowed by the server. - Repo as six addressed sections — History, Branches, Working trees, Changes, Settles and the
Issues desk — each record in the panel. - Insights: how long is left, what it costs, what a session may spend, this week's measurements,
velocity and approvals, each question answered by the server's own arithmetic. - Logs: the explorer and its live tail, the journal on a Gantt, delivery, health — the doctor, the
start ceiling and stray consoles — and access, plus the redacted bundle. - Settings as one place: an index with what each section holds, a section per address, search that
answers sections and keys with their controls, and Everything — every key the server describes,
with its value, default, origin, reset and, for a key only the CLI or the machine sets, the line
that sets it. - The palette's do: every operator verb, each saying when a capability flag disables it; and the
panel's Actions tab per record kind. - The surface registry (
client/src/data/surface.ts): every route, server event, settings key
and operator verb, each with its door or an exemption naming its caller, held both ways by
viewer/test/surface-parity.test.tswith nothing left unassigned. - The help guides describe 7.0, in English and Persian: Runs home, the launch's one screen, and
Getting around's card per place and its three doors.
Changed
- Status is a dot and a word (
client/src/ui/status/): the eight hues at one weight paint a 6 px
dot, a track segment or a row's edge, never a chip behind text; settled things wear a hollow dot. - One type face at four sizes, one cool neutral room in two lights, rows separated by space, a card
only around a thing acted on as a unit, and one shadow, for overlays. The design law is
viewer/docs/design.md; every rule in it names its guard. - One stream per browser. A second tab no longer breaks the first (#236): one tab holds the
console's event stream and shares it with the others, and a session console asks for its run's
stream only while it is on screen. - Lists read projections, the slim run list for the home and the header, the full record only for
the open run (#234's client half). - A long token breaks only where it must. A path, a slug, a label or a command in a cell or a
sentence wraps at its own spaces first and breaks inside a word only when the word alone is wider
than its box (wrap-anywhere, where 6.x broke every word at the line's end). - Phone, tablet, right to left and reduced motion are proven in a real browser on every
destination: the three doors by thumb at 360 and 768 px, every stop mirrored underdir="rtl",
text finding its own direction, and motion stilled when the system asks.
Removed
- The 6.x client — its app, features, components and lib/api directories, lib/api.ts,
lib/queries.ts and the two helpers only they used — every shim, every lint exemption and every
frozen allowance that kept them, and seven dependencies only they used (react-hook-form,
@hookform/resolversand five Radix primitives).
Fixed
- The phone's situation line has its inline padding (#284).
- The owner's confirm prompt shows the request it confirms — the body it replays, never a
self-chosen label (#277). - An item leaves Your turn only when the server says so, so a grant's Allow no longer dismisses an
item before the grant is known to have applied (#270). - A decision step's options are its primary moves, and a proof re-run reports its exit (#266).
- Retention's settings save (#317).
- Settings ▸ Essentials ▸ Keyboard lists the keys the help sheet does: ⌘K,
/, ⌘I, Esc and the
panel edge's arrows (part of #334). - Tests red only under the full gate's load — the write menu, the figures, the help sheet, the
rehearsal and the notifications specs — were rewritten with the screens they drive (#247, #227, #269). - Two node suites red since the rebuild began — a stray status reader and a free package file list
missing a shared module (#293).
v6.2.1
The public tree names no paid package. A patch to 6.2.0, with nothing to migrate: two strings in
an end-to-end fixture named the paid package, and 6.2.1 gives the fixture a neutral package name. The
free tree stays MIT. Nothing a 6.2 plan, session or console does changes.
Fixed
- The tests name no paid package and no paid route (#282). An end-to-end fixture
(viewer/e2e/fixture/seed.ts) named the paid package in an item's proof and in a permission's
command; both now name a neutral one,acme-widget. Two server suites,inbox.test.tsand
console-forge.test.ts, spelled routes only the paid tree serves; the free tree's copies assert each
action's verb and method instead.
v6.2.0
Your turn. 6.2 gives every act only a person can do one page, one shape and one road back to the
work that waits on it. A sign-in, a code, a secret, a payment, a permission the AI lacks, a decision,
an errand at the machine: each is an item on Your turn (#/turn) that says why only a person fits it,
carries a guide to follow and a proof the console checks, so I've done this — check comes back
passed, or sent back with exactly what to redo, and no session can pass its own. A permission ask is
answered by a grant with a scope and an end — this call, this phase, this plan, this repository or
always — that the server applies, audits and can revoke, beside a never list no door lowers. An owner
key, a passkey, lets a console tell its owner's press from any other door's. The console keeps the
page current by itself, and the approval, gate, question and errand cards elsewhere draw the item and
link to it. The free tree stays MIT. Nothing a 6.1 plan says stops working — what moved under people's
feet is below.
Migration
- Update the console's scripts and the plugin together.
PE_APIis 2, and a run refuses to start
when the console's and the skill's stamps do not meet, naming the half to update. A 6.1
phase-outcome.shtakes none of a person's turn's new flags and never exits 4. - A person's turn carries a reason. A
needs-human --stepdeclaration says why only a person fits
it —--why, one of ten reasons, held to what its kind allows — and one that names none is given its
kind's default, marked inferred, so an older skill copy and--actkeep working. A plan's
- **Human step:**bullet gainswhy:,effort:,unblocks:andguide:; a bullet with nowhy:
draws the advisory F40, never a gate. - A step needs a proof. A step with no
--proof, no--proof-wordsand no answer for a result is
refused (exit 2); name--proof-type attestto take the person's word. - Exit 4 is the guard's.
phase-outcome.shexits 4 when a declaration asks a person for what the
AI could do itself — apermissionorreservedreason whose every guide command the run's own
policy allows, areachwith no--tried— or a permission block cites no wall the console
recorded (G5). Read 4 as "run it yourself", never as a malformed call;--triedis the answer when
running it failed. - "Done — continue" is I've done this — check. A person errand is an
operator-actitem, and its
row's action, like every check button, reads I've done this — check. A check now comes back with a
verdict — passed, rejected with exactly what to redo, or needs-info — rather than "landed or not",
and a probe that misses leaves the itemreturned, where it used to readnotified. - The
secret-entryfield is gone. The console never takes a secret:checkrefuses a body that
carries one, and the item names where the value goes instead — the keychain itemphase-console-<id>
on macOS, a 0600 file elsewhere, or the item's owncredential:place. #/approveredirects.#/approveand#/approve?step=<id>land on Your turn (#/turn,
#/turn/<id>) in one hop, so a bookmark keeps working; a push opens#/turn/<id>.- A grant has a scope and an end. The approval card's Allow with remember, the widen card's Allow
and a standing card's one-time allow are grants now —call(spent on use),phase(until it
settles, 24 hours at most),plan,repository(a new policy layer between the plan's file and the
machine's) oralways— each a row ingrants.ndjsonsaying exactly what it changed, and a revoke
undoes exactly that. The widen card no longer strikes a rule from the plan for good. A grant below
plan scope lowers the rule in that run's settings until it ends, and for that long the CLI's own list
does not hold the rule with the console dead. - What an owner key changes for a script that presses the console. Nothing until a key is enrolled:
a console with none behaves as before, and/api/state.ownerDoorreadsunenrolled. Once one is,
an authority press from a script, the CLI, a browser with no key, a phone beyond its low and medium
answers or a session's token is answered 202{requested: true}— recorded on its item as "asked by
— confirm?" and pressed only when the owner confirms — so a script reads 202 as waiting for
the owner, never as done. What the plan's manifest already allows still executes through any door.
byin a body is only a label: the door a request proves is recorded aspressDoor, and a request
carrying a session's token presses no authority route its plan'spermission.destructiverow does
not name (403,owner.door-refused). - A turn's push names its buttons by kind: Open and I did it for an act, Allow and Deny for
a low or medium permission at its narrowest scope, Open and Deny for a high or never one, Open
for a decision. Quiet hours are one setting, the device's: at the first boot a console-wide
reminderQuietwindow moves onto every subscribed device that has none of its own, and is dropped. - For readers of the vocabularies: a person's turn has an eighteenth kind,
permission, and eleven
states withreturnedanddeclined;viewer/shared/turn-model.js(twinscripts/turn.env),
viewer/shared/guide-grammar.jsandviewer/shared/door-model.jsare new owners; the push catalogue
has a twentieth category,granted. git send-packandgit http-pushare walled likegit push(control-tower phase 141): two
default deny rules, the same on every profile. A plan that publishes through either names the rule in
itspermission.destructiverow, as it namesgit push.- A guide line may not hold an invisible or direction-changing character (control-tower phase 141):
a control character, a zero-width space, a bidi embedding, override or isolate, a word joiner or a
byte-order mark refuses the guide at that line, and a link holding one is not openable. The joiners
and the direction marks (U+200C–U+200F) stay, so a Persian guide reads as before.
Added
- The docs say what 6.2 adds, in English and Persian (control-tower phase 140). The help sheet's
Your turn guide is rewritten around the page — its six sections, why only you, the guide, the check
and its verdicts, your other moves, the phone — and Permissions says what a grant is (its five
scopes, its risk tiers, its end, the ledger and the revoke), names the never list and the three
publishing asks auto-grant never answers (git push,gh pr create,gh pr merge; it said two),
and gives the owner key a section that ends with what the key is not: a process running as you that
deliberately rewrites the console's own files can forge anything below the owner key, and can replace
the key registry itself; the console walls the paths a session takes and makes every grant visible,
and it is not a boundary against your own account. The tower, mobile, notifications and reference
guides, README, USAGE and the console's README follow, each with its Persian twin, section for
section. For sessions,references/turn.mdis new — the reasons with an example each, the guide's
grammar and the plan's**Guide language:**line, a template per kind, the proof types, the guard's
seven rules and how to answer each, a permission block, the answer's road back,handledand the
doors — and SKILL.md's rule names the five things a session does: give the reason, write the guide
in the plan's language, state the proof in words, never ask for what it can do itself, and record
what it handled; a denied tool is declared, never routed around, and the console is never pressed.
skill-sync.test.tsandguide-coverage.test.tshold each against the code it describes. - A decision names its
## Decisionsrow (control-tower phase 140, the deferral phase 133 left).
phase-outcome.sh … needs-human --step decisiontakes--decision-key KEY: the person's answer is
written to the plan's decisions table throughdecisions.shbefore the session is resumed with it.
Any other kind, or a key outside the manifest, is refused (exit 2). - The old cards move in (control-tower phase 139, closes #216). Nothing draws a person's ask in a
shape of its own any more: the approval queue, its question card, the gate card and the errand card
each draw the ITEM — one row, the kind's mark, the title, ONE primary pressed exactly as the server
spelled it, and Open on Your turn (#/turn/<id>), where everything else is one press away. A
low or medium permission's row grants in one press; a high or never one, or a choice of options,
opens the page. A gate no run is asked about keeps the plan's own Approve (gate-status.md). The
Needs-you bay keeps runs: each strip's one action is its oldest item's primary, the loose rows and
Coming up moved to the page, and one line links there with its counts; Your turn (n) in the
situation line counts items (an errand and its step are one) and opens the page. The launch door lists the run's items — the
pre-cleared shown done, Do it now opening an item a launch before this one raised — and a plan
step markedauto-open: hostnow OPENS at launch, on the machine, behind--allow-terminalor
--allow-agent, anhttp/httpslink only, once the door has shown it whole (the launch sends
back exactly the links it showed); a session's step never does. Aphysicalitem with a link
shows it for the phone. Not doing this is a person's no; the dead Dismiss fetcher
is gone. A broker card's
Allow that a keyed console turns into an owner request is toasted "asked of the owner", never "done". - Grants and owner keys on the screen (control-tower phase 138, closes #215). A permission item on
Your turn is answered in one press on its own card: "Raised because the AI lacks permission to …",
the command, why the phase ne...
v6.1.0
The world outside a run. 6.1 lets an unattended run wait on what happens outside its own checkout,
and tells the truth about where its work stands. A wait can watch a job on another machine
(unit:<host>/<unit>), a date can stand beside a live ref as its backstop, a plan can raise its own
wait count, and the operator's own acts — a command to run, a click path to follow — form one queue
whose entries come due when their trigger lands. A run is settled only once its branch has landed on
the trunk, a manual gate is a person's alone, a session can no longer press its own console, and the
Issues desk reads a repository's whole issue list, each issue with its category, severity and plan
status. The free tree stays MIT. Nothing a 6.0 plan says stops working — what moved under people's
feet is below.
Migration
- A manual gate is a person's. A gate the plan marks
manualis never delegated to a session,
whatever thegatesrow says.gate-status.mdgains aDoorcolumn, and--gate-statushonours a
manual approval only when a person's door — the console's Gate card or a person's own terminal —
wrote it, so a row written before 6.1, which names no door, no longer clears its gate: approve it
again. - A run is settled only once it has landed. A run that owns a branch reads
finishedonly when
every repository'spe/<slug>, the root's included, is held byorigin/<trunk>; until then it parks
unlandedwith one merge errand, where 6.0 called it finished. Underlanding: holdnothing moves
the branch onto the trunk by itself. - A session cannot press its own console. A supervised session's HTTP client (
curl,wget,
http,xh) against a console's approval, policy, run-settings, gate or person's-step routes, and
its writes into the console's state or config directories, are denied before they run
(console-forge). A phase that must press one names that press's CLI form in the plan's
permission.destructiverow. - A §Verification line runs without
CI=1. The console no longer sets it, so a suite that changed
its behaviour onCIruns as it does in the session; non-interactivity comes fromNO_COLOR,
TERM=dumband a closed stdin, and aCIthe console itself was started with passes through. - A session's turn ends after its own agents. While a background subagent or monitor the session
launched still runs, the Stop hook refuses apartialorcompleteexit (twice at most) and names
the two ways out: wait for it in one bounded foreground call, or stop it and record what it was doing. - A new advisory lint, F39
setup-deps-missing, names a §Verification line that runs a package
script or a.venv/bin/*binary in a repository the phase's- **Setup:**installs nothing for.
Add the install to Setup; the lint never changes the exit code. - Update the console's scripts and the plugin together, as at 6.0: a 6.0.0
phase-outcome.shhas
no--actor--due-whenand knows nounit:ref, and a 6.0.0phase-graph.shhas no
--wait-countor--verify-inarm. - For readers of the vocabularies: a person's turn has a seventeenth kind,
operator-act, and a
ninth state,upcoming, first in the path; a run has a new halt kind,unlanded.
Added
- A wait on another machine's job (control-tower phase 121, #181). A new watch scheme,
unit:<host>/<unit>, asks a systemd unit on another machine whether it is still running — over ONE
shared ssh connection per host (ControlMaster, its socket under the console's state directory),
every 5 minutes — and lands the moment the unit leavesactivating/active, recording itsResult=
and exit time in the wait history. The host's address, user, key and port come fromhosts.<name>in
the machine profile~/.config/phase-console/fleet.json; a host not named there is refused, and none
of those details is written into a run record — ssh's own failure is told in fixed words of its class,
never its text. The master socket lives with the instance's state, or, where that path would overrun a
Unix socket's 104 bytes, in a private directory of the user's own under the system's temporary one.
phase-outcome.shrefuses a malformedunit:ref. - A date beside a live ref is a backstop (#181).
waiting-external --watch <ref> --watch date:<iso>
wakes the phase the moment the ref lands; the date only bounds the wait, and if it passes first the
phase is resumed told that the ref has NOT landed and what it last read (phase.waitingcarries
backstop). The script says which ref is the backstop when it records the declaration. - A plan raises its own wait count (#40).
**Wait count:** <n>in §Session budget, or
- **Wait count:** <n>on a phase (1–99), lifts the four declared waits a phase may spend;
phase-graph.sh --wait-count [N]reads it,wait-budget.sh [--phase N] --count <n>writes it. A park
on a spent count or a spent time budget whose watch ref still polls now reads as a WAIT on that ref —
the runwaitingon it, looked at again every six hours — never an errand to re-check it by hand. A
date:is a clock, not such a ref, and a ref the watch clock has refused for good no longer counts:
either way the park is a person's again, with itsbudgetserrand. The session contract states the
raised count and where it came from. - The operator's acts, as one queue (#182). A seventeenth human-step kind,
operator-act— an act
only the operator does, a command to run or a click path to follow — and a ninth state,upcoming,
first in the path: a step declared with a due-when ref is shown under Coming up, unannounced and
unreminded, its window not started, until that ref lands; then it is due with ONE notification,
NOW: <command>, and its proof landing clears it and resumes the phase that needed it. A session
declares one withphase-outcome.sh … needs-human --act --due-when <ref>; a plan with adue: <ref>
field on its- **Human step:**bullet, in a phase or under## Operator errands(the plan's own,
phase 0). Adue:that fits no scheme's shape fails the F37 lint by name. While an act is coming up
its park summons nobody — no needs-you push, no errand row — and a due-when ref the console refuses
makes it due at once, the refusal named; a frozen console asks no due-when ref at all. The approve page
and the Tower list Coming up after what is due, each row with its command to copy. New journal line
phase.human-step-due, loghuman-steps.due. - The Issues desk (control-tower phase 118). Repo ▸ Issues reads a repository's WHOLE issue list:
onegh issue list --state allwith a cap of 2,000 (ISSUE_LIST_CAP, asked one over sotruncatedis
measured, never assumed), withauthor,createdAtandclosedAtnow among the fields and still no
field that returns a body. Each issue carries what the desk reads off it, derived once in
viewer/shared/issues-model.js(categoryOf,severityOf,planStateOf,triageOf) and drawn
through the status family: its category (bug·enhancement·documentation·question·
other), its severity (aseverity:label, or none) and where it stands in a plan — needs a plan
(awaiting-plan), planned in<slug>phase N (plan:<slug>, with the phase a local plan'sFixes:
line names), deferred (plan:<slug>-deferred), fixed (closed, with a plan label). Every column sorts
and the sort is in the URL beside the new Category, Severity and Plan status filters. The repository
picker takes any GitHubowner/namean operator adds (prefs.issueRepos, at most 12), read and
refreshed like the estate and marked "outside this console". The palette's entry is "The Issues
desk", and the Plans page links "Plan from issues" to it. - The issue rubric's words (control-tower phase 114).
viewer/shared/issues-model.jsowns the
three issue types (bug·enhancement·documentation), the four severities worst-first with their
one-line meanings (critical — data loss, a security or permission bypass, an unapproved production
change, or every lane blocked; high — a run halts or parks, or reaches a wrong verdict, with no in-run
workaround; medium — time or money wasted, a workaround exists; low — cosmetic, wording, noise), the
lifecycle labelsawaiting-planandfrom-sessionwith theseverity:prefix, and the suggestion
words;scripts/issues.envis their bash twin, held word for word bygates-vocab.test.ts. - The docs say what 6.1 adds, in English and Persian (control-tower phase 122). The help sheet's
Halts section gains A job on another machine (theunit:watch and itshosts.<name>profile
entry) and its watch card the date backstop and the raised wait count; Your turn gains Acts that come
due later (operator-act, Coming up,due:,--act --due-when); Getting around's Repo card says
what the Issues desk reads off each issue; When stuck ▸ Parked explains a run that is done but not on
its trunk (unlanded). README and USAGE carry a 6.1 paragraph each and their Persian twins the same,
viewer/README.mdand its twin state the 6.1 wait rules, anddocs/loop.md's scheme table gains
unit:.
Fixed
- A temp-file save under
docs/no longer makes the console forget every plan (#244). An editor's or
an agent's atomic write reached the plan store as a change that named no plan, so the console forgot
every plan and read them all again, and hung while every board was derived anew. The watcher now drops
temp-file and atomic-save artifacts before they become a change, and a change forgets only the plans it
moved or named. - A plan card's repository tag keeps its width on a phone (control-tower phase 118, #203). At 360 px
a range estimate and the time filled the card's meta row, and the tag — the one part allowed to shrink —
gave way to zero pixels. The row now wraps the tim...
v6.0.0
Control Tower. 6.0 makes an unattended run something a person can read at a glance and trust to
say when it needs them. The console's home is the Tower (#/runs): what needs you, what is
moving, what waits and what is next, each strip one sentence and one action, and amber only when a
person must act — a status word alone never paints it. A run that stops itself says why in one card
and comes back with one press; a failure is a failed phase, a declared wait is a wait, and a resumed
phase keeps its seat, its settings and its session. A person's turn — a sign-in, a code, an approval
at the machine — is a typed step with its own card and its own proof. The plan page is one phase table
with four views and a route map that holds seventy stations; runs, sessions, the repository and the
numbers were rebuilt on one data grid, one status model and one design law. Quick start is one screen,
and the guide explains every page in English and Persian. The free tree stays MIT. It is a major
version because the home, the plan page's tabs and the colour law changed under people's feet — the
list is below.
Migration
- The home is the Tower. An empty or unknown address opens
#/runs, and#/nowand#/dashboard
redirect there, keeping whatever half of the address still means something. A bookmark, a handoff
link or a notification from 5.x still lands. - A plan page has three tabs — Phases, Run, Source. The four lists that were Route, Phases, QA
and Handoffs are the Phases tab's views (?view=table|map|qa|handoffs); the old tab addresses
redirect into them. - Amber is a summons, never a status. A board
stuckphase reads Stuck in the waiting paint
with an alert glyph;halted,parkedandgatedread as waits. Anything that asked you for
something still turns amber, through its inbox item. - A
- **Human step:**bullet in the 5.1.0<who, what, proof ref>spelling fails the lint (F37).
Write the six-field grammar (references/plan-format.md) or leave the label off. - The failure streak counts failed phases, not endings. The same phase failing twice is one
failure, and a refused credential or a lost network never counts. - The decision manifest has nineteen keys —
issuesandplan-approvaljoined the seventeen.
A plan that answers neither keeps the shipped defaults (offandhold). - Update the console's scripts and the plugin together, as at 5.1.0: a 5.1.0
phase-outcome.sh
rejectsneeds-human --step, and a 5.1.0phase-graph.shhas no--human-stepsarm. - The console's heap is a number it chooses.
viewer/runpasses--max-old-space-sizeas a node
argument — neverNODE_OPTIONS, which everyclaudechild would inherit — fromheapMbin
~/.config/phase-console/fleet.json(6144 by default), clamped to half of RAM. A console started
some other way keeps V8's default heap until it is started throughviewer/runagain. - For forks of the client: the 2.x
Chip, its tones and the--line-*/ 2.x colour aliases are
gone; badges are the typed status family undercomponents/ui/status/, and ESLint refuses a raw
StatusBadgeimport outside it.
Added
- The guide explains every page, in English and Persian (control-tower phase 32). The help sheet
gains four sections — Quick start, Getting around (a card for every page on the rail and every
overlay), Tower and Halts — and every section now has a Persian twin beside its English body
(viewer/client/src/content/guide/<id>.fa.md), held to the English section for section by
docs-parity.test.tsand required byguide-coverage.test.ts, which also fails a page or an overlay
that has no card. - A person's turn has a language (control-tower phase 41; operator decision 7). An act only a
person can do — a sign-in that opens a browser, a device code, a token to paste, a password at the
machine, an approval on somebody else's dashboard — is a HUMAN STEP: one of sixteen typed kinds
(viewer/shared/human-step-model.js, twinscripts/human-steps.env), eight states, andKIND_META
as the one place a kind's icon, label, defaultwhereand proof hint are read. A session declares
one withphase-outcome.sh … needs-human --step <kind> --title … [--open-url | --open-command] [--where] [--proof] [--step-line]… [--code] [--credential]; a value shaped like a token, a password,
a one-time code or a URL query secret is refused with nothing written. A plan declares one with
- **Human step:** <kind> · <what> · open: … · proof: … · where: host|any · window: … [· auto-open: host], read byphase-graph.sh --human-steps [N]and its JS twin; the 5.1.0<who, what, proof ref>spelling, which nothing ever parsed, is superseded and fails the lint by name (F37), and a step
with no proof is advised about (F38). The console writes each step to an append-only ledger
(human-steps.ndjson: last state wins, a torn last line dropped, a retention sink), raises ONE
human-stepinbox row and ONEneeds-youpush naming Open and I did it (and a device code),
and parks the phase on a PERSON — an unbudgeted wait, situationblocked-declared:human-acts, no
rung. No code, token, password or URL query secret reaches the ledger, a journal line, a push
payload, the log or a transcript; asecret-entrysecret goes to the credential registry (the
keychain, else a 0600 file) and is never read back. - A person's turn can be acted on (control-tower phase 43; operator decision 7). Six routes —
GET /api/human-steps[?open=1]andPOST /api/human-steps/:id/{open,check,snooze,cannot,dismiss},
each a move through the ledger's one door, attributed, journalled (phase.human-step-opened,
-checked,-proven,-snoozed,-cannot,-dismissed,-expired,-reminded). Open
answers the link for the caller's browser, or opens it on the machine behind--allow-terminal/
--allow-agentonly once the caller has sent back the full URL it was shown; a command step opens
the embedded terminal, which prints the command and runs it on the person's Enter, and nothing
typed there reaches the ticket, a journal or the ledger. Open again is unlimited short of a settled
step. A step's proof is a WATCH ref, polled off the declaration on thecmd:back-off and bounded
by the step's window (seven days when it names none), never by a wait budget; when it lands — or
I did it — check finds it true, a terminal's exit 0 proves a step with no proof ref, and a person's
word one with no proof at all — the step readsprovenand the SAME session is resumed told what
was proven. An unlanded check says what the proof read, in its own words. Reminders at +15 m, +1 h,
+6 h, then daily, deferred out of the newreminderQuiethours and past a snooze, replace the
step's notification; at the window's end the step expires into an errand, and I can't do this
becomes one at once carrying the reason — neither loops. I did it is a signed notification button
(checkinPUSH_ACTION_VERBS), and the step's inbox row offers check, snooze and cannot. - The console notices a person's turn (control-tower phase 44; operator decision 7, its third
birth channel). The sign-in guard: a supervised Bash call that starts an interactive sign-in
(SIGN_IN_SHAPES—gh auth login,az login,npx wrangler login, … through wrappers,
sudo, package runners, env prefixes, groups and chains; never a here-doc's data) is denied
before it runs on every profile,phase.tool-deniedrulesign-in, with the whole
needs-human --step …declaration to make instead (SIGN_IN_STEPS: the kind, and the tool's own
status verb as the proof only where the console's command judge runs that tool —gh,npm,
pnpm,yarn; a password given with-pnever reaches the journal); a status verb (gh auth status) and a sign-in with a non-interactive
flag (--with-token,--password-stdin,--identity, …) run, and a call no run token names is
untouched. The stall reading: asilentlane whose last output named an http(s) link beside
waiting words (SUSPECT_WAIT_WORDS; "open this link in your browser" counts only for a link that
is not the machine's loopback) carriessuspectedStepand journalsphase.human-step-suspected;
its inbox row is raised at once, offers Make it a person's turn
(POST /api/human-steps/suspected,phase.human-step-converted, a step bornconsole; a second
press answers the same step) and never converts or opens anything by itself; the reading is made
over a result's whole text, and neither its link nor its words ever carry a device code. The launch door: probe 9 lists the plan's
- **Human step:**bullets for the phases the run will drive, runs every proof at once through
the watch clock's own probe, pre-clears the proven and returns the rest with their open actions
(Prelude.humanSteps); a start records the owed ones in the ledger, bornplan, just before the
runner starts, and the reminder clock withdraws one once its phase's handoff reads complete. The folds:
every inbox row that asks a person for an act carries onehumanStepview (humanStepView,
HUMAN_STEP_FOLDS) — a sign-in asclaude-login, an MCP sign-in asmcp-login, the verification
card asperson-check, a plan to approve, a gate, a relayed question and a QA verdict as
decision, phase 39's protected path asprotected-pathwith the act and the path the session
named (Errand.step) — andos-permissionnames its settings pane,physicalcarries a QR for
its link. - Runs is the Tower (control-tower phase 20; the operator's "a full live management page for
runs", and #19's fence made visible).#/runsanswers "does anything need me?" in one look: every
run sits in exactly one bay, most urgent first — Needs you, Live, Waiting, Queued, Ready to start,
Settled — read off the status model, s...
v5.1.0
Many plans, one repository. Several plans can now work one repository at the same time without
treading on each other: each run in a checkout of its own, locks two processes cannot both win, one
trace id through everything a run does, and notes that reach a phase before it starts. It is a minor
version because it is additive — a 5.0.0 plan lints and runs unchanged, and every new door stays
closed until a plan directive or a capability flag opens it. docs/releasing.md, §Upgrading to 5.1.0,
is the list of what an older copy will notice.
Migration
- None. Five lint ids are new — F26
note-target-unknown, F27land-word-unknownand F29
landed-gate-unknown-phasefail a plan, F28land-needs-laneand F30note-target-doneadvise —
and each fires only on a directive 5.0.0 did not have. Update the console's scripts and the plugin
together, as at 5.0.0: a 5.0.0phase-graph.shhas no--notesarm and a 5.0.0phase-outcome.sh
rejects--for. - One tightening. Nine shared-
.gitverbs (git stash,config,worktree,submodule,
checkout,switch, andgh issue create/comment/close) now raise a permission card on every
profile: one of them in a shared checkout is another lane's problem.
Added
- A checkout of its own, per repository. A run's branch forks from the trunk at a pinned sha and
records who chose the base; the console's worktrees aregit worktree locked with a reason naming the
console, the plan, the phase and the time, and sweeps unlock only the console's own stale locks. A
retention word —keep-on-failure(the default) ·prune·keep·ttl:<h>— says what becomes of
a checkout when the run settles, and a dirty tree is never removed. A per-repository cap bounds how
many isolated runs stand beside each other,.worktreeincludecopies ignored-but-needed files into a
fresh worktree, and- **Isolation:** shared|worktreelets one phase carve itself out. - Locks two processes cannot both win. A claim is made with
ln, which the filesystem makes
atomic; a lock with noscope=line reads asalleverywhere; the runner claims provisionally at
grant and stops the lane on losing the lock rather than editing on; the unsupervised outcome inbox
stamps its filenames so a second declaration never destroys an unread first; run ids are twelve hex
characters. - Worktrees, lanes and settles that tell the truth. The run pruner reads the phase list from disk;
merges into one tree are serialised per directory; a vanished lane directory is rebuilt and a drifted
mirror mount is named and reattached; a settle that ends on a usage wall no longer claims the branch
was published. - One trace id, one seam under every child process. A run's trace id rides the HTTP request, the
drive, every phase attempt, the spawned session, the scripts, the presence hook and every git command;
one module is the seam under every child and logs itsargv,cwd,msandcode. The log
envelope is v2 with adebuglevel,PHASE_CONSOLE_DEBUG=<channel>admits one channel atdebug,
/api/debug/levelflips it on a deadline, and a journal at its cap writes an in-bandjournal.full
marker and keeps a reserve sorun.finishedis always reachable. - Nothing grows without a bound, and one run exports as one file. A retention planner sweeps
transcripts, task ledgers, outcomes, git traces and the supervisor's stdio by a table Settings ▸ This
instance ▸ Logs and retention can preview before it acts; rulings are never pruned. Each session's raw
hook payloads are kept beside its record, capped at 1 MB with one marker line.GET /api/debug/bundle?slug=&run=answers one run as a redacted tarball, andphase-console diagnostics --run <id>builds the same thing with no console up. --allow-publish, the eighth capability flag. Off by default like the other seven, it is what
lets the console push a finished phase'spe/*branch — never a trunk, never with force — and file
issues on the repository's behalf, where a plan'spermission.destructiverow allows the act.
phase-console doctorgains a non-blockingpublishrow.- Notes reach the phase that needs them. A handoff bullet addressed forward and a deferral ruling
(phase-outcome.sh … ruling --kind deferral --for <M|next|all>) are collected by
phase-graph.sh <slug> --notes Ninto that phase's boot prompt, shown on the plan page, and linted
(F30) when addressed to a phase that has already finished. - The contracts. Three new vocabularies with bash twins (landing, messages, issues); the plan
directives--land,--landing,--base-branch,--gitlink,--conflict-policy,--isolation,
--clash-zones,--issues,--messagingand--notes, each answeringword · phase|plan|default;
two self-evaluating gate kinds,landed Nandpr-merged N, that read a ledger and run nothing;
scripts/phase-landing.sh, the ledger's writer; and an eighteenth decision key,issues. - The verification launch door. Every §Verification command a run would stop on is answered
once, at the start door: the prelude's fifth probe,verification, lists each by exact text, phase
and reason, to approve (bound to a hash of its whole text) or waive for the run; one verification
review answers boarding, the start response, the plan page, plan health and the repair gate alike.
The launch form says what each phase will run as and where the plan and the run differ; context is
counted per call (412K ctx · peak 455K · 2 rebuilds · 17 polls) and a session is told to wrap up
at 0.6 × its window; live spend shows beside booked spend. doctorprobes git under the console's ownPATH, blocking — under launchd that path led to
Apple'sgitshim, which exits 69 until the Xcode licence is accepted.bootedAton/api/state— the process's identity, so the page that pressed Restart reloads
once a different one answers.- Smaller things. Base branch, "Runs beside it in the repository" and "When the run settles, its
checkouts" on the launch form and in Settings ▸ Automation; aLand:chip and a locked chip on the
run and Now pages; a per-action words box on inbox rows;refusalReasonsonGET /api/state;
"Serialise conflicted branches" and "Call it looping after" among the automation preferences.
Changed
- One wait procedure, in every place a session meets it, and a runaway run of status checks
(six inside two minutes with nothing else between) is refused at the hook with the procedure. - A session is resumed only while it is worth resuming — one that ended at ≥ 250k tokens and is
cold, that declaredpartial --reason budget|context, or that the console checkpointed is boarded
fresh with the resume brief. - A nearly spent account stops taking on lanes, and a usage wall that cannot clear soon is waited
out at the first burst rather than after twenty minutes of retries.
Fixed
- A fresh run carries the start door's
maxParallel— it reached a continued run and never a new one. batsis a command,git merge-base/merge-treeare reads,2>&1is not a separator, and
! grep …is judged as the command it negates; a$(…)inside double quotes is judged too, and a
backgroundedcmd &no longer reports green without waiting.- The self-heal no longer dead-ends or buys a session for an approval, and a
Person-check: halt
park names the refused command and the remedy that exists. - Every turn of a multi-turn session is booked; a phase this run worked is no longer called
"closed outside this run";phase-console stopreports a console stopped when its process has left;
a hung server test fails by name; a terminal left open no longer holds the autopilot. - The findings 5.0.0 left open, each ended in one state: a generated prompt names the docs root it
was read from; the recovery panel stops offering a resume the policy refuses; a wait-resume onto a
lost session boards with the resume brief; a pull request is no longer cancelled by a declined
resume; the local-job nudge waits out the window the procedure grants; paging a file is not a poll;
a closeout that could not resume says why in English;preFirstTurnasks what this session spent.
v5.0.0
Zero-touch: what a run would stop to ask is asked before it starts. This is a major version
because a plan, a session and a script that starts runs each meet something that now refuses.
docs/releasing.md, §Upgrading to 5.0.0, is the full list.
Migration
- Three lint ids now fail a plan. F14
verification-empty-open: give every open phase's
§Verification a runnable command. F24gate-directive-missing: put a- **Gate-check:**line under
every*(GATED)*heading. F25: everyoutstandingrow in## Decisionsnames an owner, a known key
and a known state. Lint every live plan with the 5.0.0 scripts before a console runs it. phase-outcome.sh … blocked|needs-humanrequires--needs <key|class>. A 4.1.0 copy of the
script rejects the flag, so update the console and the plugin together.- A run started over HTTP must carry
resumeOnRestart,relayandaccounts(400 without them).
It is refused with 409 while a blocking decision is still open, a waiver is unacknowledged or a
probe fails. - Defaults and names moved.
delegateHumanGatesnow ships on andqa.exhaustedanswerswaive.
phase.tool-auto-grantedis nowphase.approval-auto-granted, andphase.resume-at-bootis now
phase.resume-automatic. - The relay needs
claude2.1.268 or later. Sessions that are not relayed run with
--permission-prompts nonefrom 2.1.259.
Added
- The decision manifest.
## Decisionshas seventeen keys; read it with
phase-graph.sh --decisionsand answer it withscripts/decisions.sh. - The run-start prelude. Four probes run before anything spawns: accounts, MCP servers,
credentials and a delivery channel.phase-console doctorruns the same probes by hand. - The policy table and ruling memory. Eighteen kinds of interruption are answered by default and
journalled.--rememberpromotes a ruling into the next plan, and the table is edited under
Settings ▸ Automation ▸ Policy. - The relay. A question a session asks is held for sixty seconds, then answered by rule, else the
recommended option, else the first. It is never answered on a deny-list match. - Honest sessions. Every stop sends SIGINT first, so turns and cost are booked. Every session
carries a turn cap and a dollar cap, and every automatic start names its door and counts against a
ceiling. - Honest waits. A declared wait longer than the console's budget is refused with the arithmetic
instead of being cut. Every resume checks that the session is not live, and an overdue clock at boot
is ruled on rather than fired. - Accounts at the wall. A credential an organisation refuses is retired for every console on the
machine. The run climbs to the next account, or parks the phase with one errand. - An off switch that stays off. Shut down lists what is running first, and Stay off holds every
later boot until it is cleared.phase-console sessions ingestrecords session presence with no
console up. - Delivery. A console with no device to notify says so, and an unattended start with no channel
asks for an acknowledgement. - Why a run started and what it cost, on every run and session page, reconciled against the run's
spend.
Changed
- The documentation describes all of the above —
docs/, the references,SKILL.mdand the
in-app guide, in English and Persian.