Skip to content

Keep the chief's record in one committed file and publish it as a Claude artifact - #172

Merged
sreejithraman merged 16 commits into
mainfrom
agent/chief-single-state
Nov 10, 2026
Merged

sreejithraman merged 16 commits into
mainfrom
agent/chief-single-state

Conversation

@sreejithraman

Copy link
Copy Markdown
Owner

The chief's command center fell behind its record. The chief kept two hand-made copies, record.md and program.json, and republished the page only when it judged a refresh worth it. This PR makes the page a mechanical result of one committed record.

What changes

  • One record in git. program.json holds the whole program, organized by topic: program facts, decisions[], and work[] rows that carry their owner, scope, and sources. The program folder is a git repo, and HEAD:program.json is the record. Owners still write only reports/<ID>.md.
  • A fixed write procedure. Settle any uncommitted change with the user, edit, run render_dashboard.py --check, read git diff, commit, publish. On a git or publish error the chief stops and reports.
  • The command center is a private Claude artifact.
    • --page renders HEAD to a .next file and prints changed when it differs from the last published page.
    • The chief publishes on changed, then moves .next into place. A failed publish shows changed again on the next write.
    • Record-only commits leave the page alone.
    • The skill states only when and what to publish; agents already know the artifact tools.
  • No hand-set timestamps. The page's Updated time comes from the commit that changed it. Each row keeps its own checkedAt.
  • Stricter --check.
    • It rejects unknown keys, which catches half-migrated files.
    • It reads --revision from HEAD.
    • It refuses to overwrite the state file.
    • It keeps every coordination rule, including scrap-or-rebase holds on dead bases.
  • Chief moves. Only a Claude session with artifact tools can be chief. A new chief confirms it can read the artifact. The old chief hands off right after its commit, and the new chief publishes.
  • Removed:
    • record.md and the separate snapshot;
    • the static server, staging, and swap steps;
    • the refresh judgment;
    • page polling;
    • the Tailscale hosting steps.
  • Browser tests use Playwright's Chromium when it is cached, so check.py no longer leaks a branded-Chrome code-sign clone on every run.

Verification

  • python3 scripts/check.py passes. The command-center browser tests now load pages from a loopback server, which fixed a reload test that failed about 1 run in 8. Full runs went from 12 failures in 40 to 0 in 40.
  • The real publish loop ran against a private example artifact:
    • A display commit published.
    • A record-only commit didn't publish.
    • A skipped publish stayed changed.
    • The Updated time matched the display commit.
  • In claude.ai's viewer:
    • The page renders, and its icons load.
    • An open tab moved to the new version without a reload and kept its selected tab.
  • Ten review passes (Primary, Thermo, Ponytail, Adversarial) plus fresh-session dry runs. The last pass found nothing material.
  • Not checked: the Claude iPhone app.

After merge

The live seatcommons program still uses the old format: record.md plus the old program.json. It needs a one-time migration into the new file in a git repo, then its first artifact publish.

🤖 Generated with Claude Code

sreejithraman and others added 16 commits November 9, 2026 17:10
…n request

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…Chrome clones

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- serve_dashboard.py caches the last rendered HTML and, on any read or
  render failure, serves it under an escaped banner with a new digest,
  without rendering again. One lock serializes read, render, and cache,
  so an older request can't replace a newer page.
- updatedAt now means the last write that changed what the page shows;
  record-only writes keep it, so open pages stay put.
- A program.json that fails --check is repaired by the thread whose own
  last write named it chief; other threads report the break.
- Browser tests run on the real DashboardServer with a small handler hook
  for faults; COMMAND_CENTER_CHROME is gone; render_dashboard.py reuses
  obj() and one missing-work check.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Record writes now edit a copy of program.json in a unique temp file, keep the
replaced file as .program.json.prev, and pass --check --previous, which fails
when an append-only record list loses entries. The dashboard server reads its
template once at startup. Resume and chief moves check the hosted page and
restart the server on its recorded port when it is down. The example hosting
entry uses an allowed Tailscale port.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The program folder is now a git repository. Each write is checked, moved
into place, reviewed with git diff, and committed with a message naming
its event. Repair authority comes from record.chief in the file or its
last commit, and every resume or chief move takes the page down and hosts
it again on the same HTTPS port. This drops --previous, check_history,
.program.json.prev, the memory-based repair rule, and the conditional
same-port restart, plus the test Host's fake template hook.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e down

Each record write now starts by checking that program.json matches its last
commit, and stops on any git error. Repairs restore from HEAD. Every git
command runs with -C. --check rejects any layout other than indent-2 JSON and
prints the command that fixes it. The server always binds an OS-picked port.
Host again starts the new server before it stops the old one, and reuses the
HTTPS port only when the mapping is still ours or the port is free.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…cts)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The program folder's last commit is the record. After each commit,
render_dashboard.py --page renders HEAD to a page file and says whether
it changed; the chief republishes one private artifact per program from
that file. Remove the loopback server, Tailscale hosting, the error
banner, and the page's polling, and test the page from a rendered file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Drop the display/record split: program facts sit at the top level, one
decisions list carries each Need from arrival to answer or withdrawal,
and each work row holds its scope, sources, and owner object. The page
stamps its Updated time from the commit that changed it, so no field is
hand-set. --page stages HEAD beside the last published page and stays
changed until the chief publishes and moves it over; --revision reads
HEAD; the old chief hands off a move without publishing, and the new
chief confirms artifact access before the move completes. Tests now run
one case per validator rule plus in-browser behavior.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- --check rejects unknown fields in every object, naming each path.
- An answered decision keeps holding work on a dead base until its
  restack is recorded.
- Answered decisions record the answering message in answerSource;
  withdrawing a settled decision moves its answer into rationale.
- Provisional Land reads as Try without a stored effectiveFinishLine;
  drop owner.report, owner.model, waits[].watcher, and the prose rule list.
- Resume starts from next; the kickoff passes requirements.
- Serve browser-test pages over loopback http: Chrome sometimes drops a
  file:// page's sessionStorage on reload, which failed the corrupt-storage
  test in 12 of 40 full runs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Answered Needs keep their blocks forever, so counting them as a hold
switched off the dead-base and closed-parent checks for good. Only open
Needs and a row's `blocked` field hold now; clearing `blocked` ends it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`blocked` no longer holds a row on a dead base or a closed parent, so
unrelated blocker text can't stand in for the user's choice. A rebase
answer now moves the row off the dead base in the same commit: retarget
or drop `stackedOn` and `provisional.on`, and set `base`. The owner's
restack report then updates `head`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A "rebase onto X" answer retargets stackedOn and provisional to X's layer
and outcome, and drops them only for the target branch. --check rejects a
live layer whose base is another layer's branch without stackedOn naming it.
An emptied provisional.on ends provisional as for a landed base.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sreejithraman
sreejithraman merged commit e38599b into main Nov 10, 2026
3 checks passed
@sreejithraman
sreejithraman deleted the agent/chief-single-state branch November 10, 2026 06:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant