Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
fb397d5
Keep the chief's whole program record in program.json and render it o…
sreejithraman Nov 9, 2026
a1d4ff6
Prefer Playwright's Chromium in command center tests to stop leaking …
sreejithraman Nov 9, 2026
dd3ee4a
Keep the chief's served page and record writes consistent under failure
sreejithraman Nov 10, 2026
e57019a
Guard the chief's record history and check the hosted page on resume
sreejithraman Nov 10, 2026
94cba2f
Keep the chief's record history in git and re-host on every resume
sreejithraman Nov 10, 2026
30167f3
Read the record diff before the move goes live
sreejithraman Nov 10, 2026
2984bb5
Check the program file against HEAD and re-host before taking the pag…
sreejithraman Nov 10, 2026
9fc0a11
WIP: make HEAD the chief's record (partial; hosting to move to artifa…
sreejithraman Nov 10, 2026
91f3aee
Publish the chief's command center as a Claude artifact
sreejithraman Nov 10, 2026
aca8fae
WIP: partial pass-5 renderer repairs
sreejithraman Nov 10, 2026
13c083e
Organize the chief's program record by topic
sreejithraman Nov 10, 2026
73b2223
Tighten the chief's program schema and fix the reload test flake
sreejithraman Nov 10, 2026
3f9417e
Hold work on a dead base with an open Need or blocked
sreejithraman Nov 10, 2026
7acb424
Hold dead-base work only with an open scrap-or-rebase Need
sreejithraman Nov 10, 2026
508079c
Tie a rebase answer's base to its stack edge
sreejithraman Nov 10, 2026
c811af7
Say a rebase onto another outcome targets its unmerged layer
sreejithraman Nov 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 11 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,9 @@ deleting `reference-transaction`.

Read each skill before use. Delegation and review target T3 Code's orchestration
MCP tools and its configured provider runtimes. Other workflows may need goal
state tools, GitHub CLI, optional Tailscale sharing, or Apple development tools. Linking skills
does not install provider executables, authenticate accounts, or enable models.
state tools, GitHub CLI, Claude artifact tools, optional Tailscale sharing, or
Apple development tools. Linking skills does not install provider executables,
authenticate accounts, or enable models.

### Claude Code

Expand Down Expand Up @@ -272,13 +273,15 @@ to you, with judgment calls ahead of quick picks. Each outcome has its own
**Land**, **Try**, or PR finish line: an owner lands its accepted outcome once
the chief releases it, the chief lands only sets that span owners, and each
**Try** waits for your merge, changes, or scrap choice. Its command center shows
Needs you first and Work second, rendered from a checked snapshot of the program
record: each outcome with its finish line, results to open, layers, provisional
Needs you first and Work second, rendered from the program record's last
commit: each outcome with its finish line, results to open, layers, provisional
state, and what each decision blocks. Answers go back through chat. Existing
trackers stay authoritative. A hosting guide can serve the page at a stable
private URL through Show Me and Tailscale Serve. Open pages pick up each new
snapshot on their own and keep your place; publishing one in the background
needs explicitly requested host scheduling.
trackers stay authoritative. The page is a private Claude artifact, so the chief
runs in Claude and republishes it after each commit that changes it. On reload,
the page restores your tab, open sections, selected decision, and scroll
position; local browser tests cover this. In Claude's web viewer, an open page
moved to a newly published version without a reload, kept its selected tab, and
loaded its icons. The iPhone app is unchecked.

[Swarm](skills/swarm/SKILL.md) is a bounded tool any session can use. For example,
“Swarm these three possible causes and return evidence” partitions the question
Expand Down
25 changes: 16 additions & 9 deletions SOURCES.md
Original file line number Diff line number Diff line change
Expand Up @@ -450,23 +450,30 @@ base and provisional bases, and linearizes fork-and-join inputs into reviewable
stacks. Existing trackers stay authoritative; there is no mandatory wiki,
Linear, Slack, cloud runtime, or recurring schedule.

The original command center renders a checked JSON snapshot of the program
record (`scripts/render_dashboard.py`, `assets/command-center.html`) with
The program record is one file, `program.json`, organized by topic: program
facts, `decisions` (each decision from arrival to answer or withdrawal), `work`
(each row with its scope, sources, and owner with that owner's plumbing),
tradeoffs, discoveries, landings, and waits. The renderer picks what the page
shows. Owners write only their own report files. The program folder is a git
repo; its last commit is the record and its log the history. The original command center
(`scripts/render_dashboard.py`, `assets/command-center.html`) renders it with
Needs you before Work, collapsible sections, per-row observation times, copyable
decision prompts that name one recorded option, judgment calls ahead of quick
picks with a batch prompt, reopenable chief-resolved decisions, and accepted
tradeoffs. Each work row is one outcome, follow-up, or support task, counted once,
with its finish line, results to open, provisional condition, owner, and PR
layers; each decision names the outcomes it blocks and the phase each waits at.
Below 900px it stays one column; wider, Needs you is a dual-pane inbox and Work a
board of stage lanes. It replaces the supplied archive's Claude artifact database
and integrations; no archive code or prose is copied. Validation rejects
board of stage lanes. It replaces the supplied archive's artifact database and
integrations; no archive code or prose is copied. Validation rejects
provisional landings, PRs on a Try, stacks on another owner's unmerged layer
without a provisional mark, and terminal rows with active blockers. Delivery is
hosted only; `references/hosting.md` serves the page through Show Me's web
sharing, including Tailscale Serve. A hosted page polls its own URL and swaps in
a newer published digest without a server push, keeping the viewer's place per
URL in session storage; the chief stays the only publisher.
without a provisional mark, terminal rows with active blockers, entries that
name missing work, and fields the schema doesn't name. The command center is a private Claude artifact per program,
so only a Claude session can be chief. `render_dashboard.py --page` renders the
last commit beside the last published page, says whether it changed, and stamps
the commit time that changed it; the chief republishes on a change and on every
resume and chief move. The page keeps the viewer's place in session storage;
the chief stays the only writer.

## implement

Expand Down
40 changes: 21 additions & 19 deletions skills/chief-of-staff/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ comparison through [Swarm](../swarm/SKILL.md) or [Arena](../arena/SKILL.md).
The user can enter an owner's session directly; reconcile their new direction
before you dispatch work that depends on it.

Only a Claude session with the artifact tools can be chief, since the chief
publishes the command center; elsewhere, ask the user to start the chief in one.
Only the record's current chief acts on program items and writes the record.
Check it before acting on a request, direction, report, event, or pasted answer;
a thread that is not the chief forwards the item under
Expand Down Expand Up @@ -72,9 +74,9 @@ unmerged work: update the record and owners, then reconcile pending landings
under [withdraw a release](references/landing.md#withdraw-a-release).

Set up the [program record](references/program-state.md) before you start or
adopt owners, so another chief could resume from it. Reuse the project's
tracker; without one, keep a task-owned file record. A coordination request
needs no new tracker. When a small choice will recur, propose a
adopt owners, so another chief could resume from it. Work items stay in the
project's tracker, and a coordination request needs no new one. When a small
choice will recur, propose a
[standing approval](references/decisions.md#standing-approvals) with the plan;
owners start without waiting for the answer.

Expand All @@ -101,8 +103,8 @@ top-level sessions, as in a plain Claude Code or Codex session, start a
delegated owner in its own host-managed worktree instead. Use
[Orchestration](../orchestration/SKILL.md) for model choice and concurrency.
Give each branch one writer, and serialize stack operations and shared
resources. Record each owner with its start mode, route, and report path. Only
you start program owners.
resources. Record each owner with its start mode and route. Only you start
program owners.

Each owner checks its access and proof before work runs unattended, under
Implement's [prepare](../implement/SKILL.md#2-state-the-finish-line-and-prepare).
Expand Down Expand Up @@ -212,13 +214,12 @@ user, its requested PR state, or a repair with its owner.

## 5. Show and continue

Keep the [command center](references/program-state.md#command-center) current
and host it through [dashboard hosting](references/hosting.md), never inline. It
shows every outcome once, with its finish line, results and how to open them,
provisional condition, owner, layers, and what each Need blocks. Results come
from owners' Shown reports: link what they showed, say what opening it needs,
and name the gaps. Refresh on meaningful results and decisions, and show
freshness honestly.
[Publish the command center](references/hosting.md) after each record write and
give the user its link, never an inline render. It shows every outcome once,
with its finish line, results and how to open them, provisional condition,
owner, layers, and what each Need blocks. Results come from owners' Shown reports: link what they
showed, say what opening it needs, and name the gaps. Keep each row's
observation time honest.

Answers arrive through chat or the host's input tools. Record each under
[record an answer](references/decisions.md#record-an-answer), then update the
Expand All @@ -228,8 +229,8 @@ Choose user updates from the owner reports: what the user can now use, try,
decide, or unblock. Keep routine checks, recovery, and unchanged milestones in
the record, and answer direct questions in the detail asked.

Done when the hosted view matches the record and shows every result and open
Need.
Done when the published page matches the last commit and shows every result
and open Need.

## 6. Settle or resume

Expand All @@ -246,9 +247,10 @@ usable outcomes and their links. Name the decisions you resolved so the user can
reopen them. Clean up through [Repo Cleanup](../repo-cleanup/SKILL.md) once
retained work and active dependents no longer need the resources.

Before pausing or moving, bring the record current. On resume, reconcile it with
current files, threads, and forge state before dispatching. To move the program
to another thread or take over from a chief that cannot act, follow
[chief move](references/chief-move.md). This skill supplies no background
wakeups: native goal tracking, recurring refreshes, and other automation need an
Before pausing or moving, bring the record current. On resume, start from the
record's `next`, reconcile the record with current files, threads, and forge
state, and [publish the page](references/hosting.md), before dispatching. To
move the program to another thread or take over from a chief that cannot act,
follow [chief move](references/chief-move.md). This skill supplies no background
wakeups: native goal tracking, recurring checks, and other automation need an
explicit user request and host support.
4 changes: 2 additions & 2 deletions skills/chief-of-staff/agents/openai.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
interface:
display_name: "Chief of Staff"
short_description: "Carry several outcomes through Implement owners"
default_prompt: "Use $chief-of-staff to carry these outcomes to their finish lines through Implement owners and keep the command center current."
short_description: "Carry several outcomes through Implement owners; the chief runs in Claude"
default_prompt: "Use $chief-of-staff to carry these outcomes to their finish lines through Implement owners."
102 changes: 6 additions & 96 deletions skills/chief-of-staff/assets/command-center.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!doctype html>
<html lang="en" data-snapshot="<!--SNAPSHOT-->" data-template="<!--TEMPLATE-->">
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
Expand Down Expand Up @@ -257,7 +257,6 @@
</head>
<body>
<main><!--CONTENT--></main>
<p id="snapshot-status" class="sr-only" role="status"></p>
<script id="lucide-icons" async src="https://cdn.jsdelivr.net/npm/lucide@1.67.0/dist/umd/lucide.min.js" integrity="sha384-xrRF3kGz5GMZeUudxpTxrrjy0NRy/+U3dHY72SdMEgy9OsrO9x8mCw6DCD8m+jjS" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script>
const iconScript = document.getElementById('lucide-icons');
Expand Down Expand Up @@ -361,14 +360,13 @@
renderIcons();
}

const viewStorageKey = `command-center-view:${location.href.split('#')[0]}`;
const viewStorageKey = 'command-center-view';
const scrollAreas = {
page: {find: () => document.scrollingElement, edge: 'top', anchors: '.card[id], summary[id]'},
board: {find: () => document.querySelector('.stage-board'), edge: 'left', anchors: '.stage-board > .collapsible > details > summary'},
inbox: {find: () => document.querySelector('.inbox-list'), edge: 'top', anchors: '.inbox-row'},
};
const anchorsKept = 6;
const focusable = 'a, button, summary';
const lastScroll = {}; // An area in a hidden tab has no box to read or scroll, so its position waits here.

const scrollOffset = edge => edge === 'top' ? 'scrollTop' : 'scrollLeft';
Expand Down Expand Up @@ -420,20 +418,6 @@
}
}

const controlIdentity = control => control.getAttribute('aria-label') ?? control.getAttribute('href') ?? control.id;
const controlsIn = owner => [owner, ...owner.querySelectorAll(focusable)];

function captureFocus() {
const active = document.activeElement;
const owner = active?.closest('main [id]');
return owner && {owner: owner.id, control: controlIdentity(active)};
}

function restoreFocus(saved) {
const owner = document.getElementById(saved?.owner);
if (owner) controlsIn(owner).find(control => controlIdentity(control) === saved.control)?.focus({preventScroll: true});
}

function captureView() {
return {
tab: document.querySelector('[role="tab"][aria-selected="true"]')?.id,
Expand Down Expand Up @@ -471,92 +455,18 @@
}
}

const inputQuietPeriod = 2000;
const heldInteractionLimit = 120000;
let lastInput = -Infinity;
let pointerHeld = false;
const noteInput = () => { lastInput = performance.now(); };
const listenerOptions = {capture: true, passive: true};
for (const type of ['pointerdown', 'keydown', 'scroll']) addEventListener(type, noteInput, listenerOptions);
addEventListener('pointerdown', () => { pointerHeld = true; }, listenerOptions);
for (const type of ['pointerup', 'pointercancel']) addEventListener(type, () => { pointerHeld = false; }, listenerOptions);
addEventListener('pagehide', saveView);

function userIsActive() {
const sinceInput = performance.now() - lastInput;
const holding = pointerHeld || document.activeElement?.matches('textarea') || getSelection()?.isCollapsed === false;
return sinceInput < inputQuietPeriod || (holding && sinceInput < heldInteractionLimit);
}

function parsePublishedPage(source) {
if (!source.trimEnd().endsWith('</html>')) return null;
const page = new DOMParser().parseFromString(source, 'text/html');
const {snapshot, template} = page.documentElement.dataset;
const main = page.querySelector('main');
if (!snapshot || !template || !main) return null;
return {snapshot, template, main, title: page.title};
}

function showSnapshot(published) {
const view = captureView();
const focus = captureFocus();
document.querySelector('main').replaceWith(published.main);
document.title = published.title;
connectContent();
restoreView(view);
restoreFocus(focus);
document.documentElement.dataset.snapshot = published.snapshot;
document.getElementById('snapshot-status').textContent = `Snapshot updated at ${new Date().toLocaleTimeString()}.`;
}

function applyPublishedPage(published) {
if (published.template === document.documentElement.dataset.template) {
showSnapshot(published);
} else if (history.state?.reloadedFor !== published.snapshot) {
history.replaceState({reloadedFor: published.snapshot}, '');
location.reload();
}
}

const checkInterval = 20000;
const checkTimeout = 10000;
let activeCheck;
let nextCheck;

async function checkForSnapshot() {
clearTimeout(nextCheck);
activeCheck?.abort();
if (document.hidden) return;
const check = activeCheck = new AbortController();
const timeout = setTimeout(() => check.abort(), checkTimeout);
try {
const response = await fetch(location.href, {cache: 'reload', signal: check.signal});
const published = response.ok ? parsePublishedPage(await response.text()) : null;
if (published && published.snapshot !== document.documentElement.dataset.snapshot && !userIsActive()) applyPublishedPage(published);
} catch {
// The page keeps its snapshot and the next check tries again.
} finally {
clearTimeout(timeout);
if (check === activeCheck) nextCheck = setTimeout(checkForSnapshot, checkInterval);
}
}

addEventListener('pageshow', event => {
describeTimes();
if (event.persisted) checkForSnapshot();
});
addEventListener('pageshow', describeTimes);
document.addEventListener('visibilitychange', () => {
if (document.hidden) { saveView(); return; }
describeTimes();
checkForSnapshot();
if (document.hidden) saveView();
else describeTimes();
});

const savedView = loadView();
if (savedView) history.scrollRestoration = 'manual';
for (const name of Object.keys(scrollAreas)) {
if (isSavedScroll(savedView?.scroll?.[name])) lastScroll[name] = savedView.scroll[name];
}
nextCheck = setTimeout(checkForSnapshot, checkInterval);
connectContent();
restoreView(savedView);
</script>
Expand Down
Loading
Loading