Status: draft · Created: 2026-06-21 · Owner: Theo
This document specifies the three chat commands that move a TooToo change from the dev sandbox out to every published copy:
edit in dev → promote → sync → publish
(to prod) (to forks) (to the web)
You type a single word in chat. Claude performs the whole stage and reports back.
- Dev is the source of truth. All changes are made in the component sources under
pushme-pullyou-tootoo/tootoo-dev/src/— the JS insrc/js/, the CSS, the HTML components, and the config — then built withassemble.ps1intotootoo-dev/index.htmland tested locally viafile://. Only then are they promoted. Bothindex.htmlfiles — dev and production — are generated by the assembler, never hand-edited. (Editing an assembledindex.htmldirectly is lost on the nextassemble.ps1run.) index.htmlis identical across every repo except its config block. Dev and production differ by exactly the config block — dev'sconfig.js(Object.assign( CONFIG, { appName: 'TooToo Dev', owner: 'Pushme-Pullyou', repo: 'tootoo', … } )) bakes in the dev identity + a default repo so the standalone file is testable on its own, while production uses the emptyconfig.prod.jsand stays neutral. (<title>is set dynamically at runtime, so it is already identical in both — there is no<title>TooToo Dev</title>to revert.) Therevisedstamp lives in the sharedsrc/index.template.html, so oncepromotereassembles both, dev and production carry the same date.assemble.ps1 -Prodperforms the config swap deterministically —promoteruns it rather than copying + hand-reconciling.- Per-fork customization lives in
tootoo.config.js, not inindex.html. Every downstream repo has its owntootoo.config.jscarrying its branding (owner/repo, appName, theme, etc.). No pipeline command ever touchestootoo.config.js. This is what makessynca safe plain copy.
| Role | File | Identity (appName) |
Edited by |
|---|---|---|---|
| Dev | tootoo-dev/src/** → tootoo-dev/index.html |
TooToo Dev |
You (edit src/, run assemble.ps1) |
| Production | index.html (this repo) |
TooToo |
promote only |
| Downstream | <repo>/index.html (×N) |
TooToo |
sync only |
Regenerates this repo's production index.html from the dev component sources.
Preconditions
tootoo-dev/src/holds the change you want to ship, andassemble.ps1builds atootoo-dev/index.htmlyou've tested viafile://.
Steps
-
Stamp current Pacific time →
TS(YYYY-MM-DD HH:MM) andTSF(YYYY-MM-DD-HH-MM). -
Back up current production: copy
index.html→.archive/index-<TSF>.html. -
Stamp
revised→ set<meta name="revised" content="TS">insrc/index.template.html. It is shared by both builds, so dev and production end up with the same date. -
Regenerate both builds from source:
pwsh -File tootoo-dev/assemble.ps1→ refreshestootoo-dev/index.htmlwith the new stamp.pwsh -File tootoo-dev/assemble.ps1 -Prod -OutFile index.html→ writes production. The-Prodassembler swaps dev'sconfig.jsfor the emptyconfig.prod.js(so production carries no baked repo or identity and stays neutral) and emits the canonical format directly — no hand-copy, no manual config reconcile, and no whitespace drift.<title>needs no change — both builds areTooToo, set dynamically at runtime.
✅ Production is generated, not hand-edited. Because
-Prodproduces the neutral build deterministically, production stays byte-identical to the dev build except the config block. The old copy-then-reconcile step (and its appName-revert footgun) is gone. Verify withdiff -w index.html tootoo-dev/index.html→ the only hunk is the config block. -
Write a changelog entry (decision: option C — auto-authored). Prepend a dated bullet to the
## Change Logsection ofREADME.md, newest on top. Wording is derived from the dev README journal and thegit diffof the change. Date heading formatYYYY-MM-DD. -
Refresh the screenshot — render the just-promoted production
index.htmland overwritetootoo-screenshot.jpeg(repo root, the README's hero image). Backs up the old image to.archive/tootoo-screenshot-<TSF>.jpegfirst. Exact commands in Appendix A. Skip this step when invoked aspromote no-shot— use that for invisible changes that don't alter the UI. -
Report: the backup path(s) created, whether the screenshot refreshed, and a
git diff --statof production.
Does NOT: commit, push, or touch tootoo.config.js. Git stays manual.
Result: production index.html == dev index.html, except the config block. The
diff is always small and reviewable.
Fans the freshly promoted production index.html out to every vendored copy.
Preconditions
- A
promotehas run (production is current). - Confirmation prompt before writing (this touches many repos).
Steps
- Pre-flight (read-only): for each target, compute whether its
index.htmlalready equals canonical and whether it has uncommitted changes. Print the plan and confirm before writing. - For each target not already identical:
a. Copy canonical
index.html→<repo>/index.html(overwrite). b. Leave everything else untouched —tootoo.config.js,LICENSE, etc. - Report a table: each target — updated / already identical / missing.
Safety model — overwrite even "dirty" targets. Downstream index.html is
generated, never hand-edited (invariant #2), so an uncommitted index.html is
just a prior sync that was never published — safe to replace. Each target is its
own git repo, so the last committed version is always recoverable. Sync touches
only index.html, so unrelated uncommitted work (e.g. tootoo.config.js,
LICENSE) is never at risk. The pre-flight reports dirty status for visibility,
not to skip.
Does NOT: commit, push, or modify any tootoo.config.js.
Commits and pushes every repo changed by promote/sync so each GitHub Pages
site rebuilds. This is the only command that performs git pushes.
⚠️ Exception to the standing "don't commit or push" rule. This command exists because you explicitly asked for it. It still requires an explicit confirmation each run, and reports per-repo status. It never uses--forceand never bypasses hooks.
Preconditions
- Commit message is derived from the changelog — the dated entry
promotewrote toREADME.mdis the commit message, shared across all repos in the run. - Explicit "yes, publish N repos" confirmation listing the affected repos.
Eligibility: publish acts only on targets that are git repos. All 14
current targets are git repos. If a non-repo target is ever added it is sync-only
and auto-skipped here — no special-casing needed.
Steps
- Determine the affected set: canonical (from
promote) + every git-repo target updated bysync. - For each repo, in order:
a.
git add index.html(+README.mdandtootoo-screenshot.jpegfor the canonical repo, sincepromotemay have changed both; alsoPIPELINE.md/tootoo-dev/on canonical when those changed). b.git commit -m "<message>". c.git push origin HEAD(current branch — repos vary betweenmainandmaster). d. If the push is rejected (non-fast-forward) — the remote has commits the clone lacks —git fetchthengit rebasethe publish commit onto the remote, preserving the remote's other changes and re-asserting canonicalindex.htmlon any conflict, then push again. Never force-push. - Report a table: repo, commit SHA, push result (ok / failed / nothing to commit). Continue past a failed repo; never abort the batch silently.
Does NOT: force-push or rewrite already-pushed history, push
tootoo.config.js changes you didn't make, or auto-resolve real conflicts in
non-index.html files (those are reported for you to handle).
GitHub Pages redeploys automatically on push, so "live" follows from the push.
Discovered 2026-06-21 by scanning for TooToo builds (TOOTOO_CONFIG /
appName: 'TooToo') that are not the canonical repo. All have their own
tootoo.config.js.
All 14 are git repos, so every one is both a sync and a publish target.
g:\My Drive\2026-theo-github\heritage-happenings.github.io\index.html
G:\My Drive\2026-theo-github\heritage-happenings-documents\index.html
g:\My Drive\2026-theo-github\pushme-pullyou-assets\index.html
g:\My Drive\2026-theo-github\pushme-pullyou-github\index.html
g:\My Drive\2026-theo-github\theo-armour-2025\index.html
g:\My Drive\2026-theo-github\theo-armour-2026\index.html
g:\My Drive\2026-theo-github\theo-armour-aa\index.html
g:\My Drive\2026-theo-github\theo-armour-agenda\index.html
g:\My Drive\2026-theo-github\theo-armour-genealogy\index.html
g:\My Drive\2026-theo-github\theo-armour-pages\index.html
g:\My Drive\2026-theo-github\theo-armour-qdata\index.html
g:\My Drive\2026-theo-github\theo-armour-sandbox\index.html
g:\My Drive\2026-theo-github\theo-armour-wikitheo\index.html
j:\My Drive\tech\index.html
qUE#@kZ4Um3tuhfI*h7^4JXQ8E8UT8Zr
heritage-happenings-documents — added 2026-08-09 as the 14th target. TooToo
installs at the repo root, replacing a stub index.html that redirected to a
./tootoo/ folder which never existed (the stub is archived beside it as
index-redirect-<TSF>.html). Its first tootoo.config.js was written during that
sync — the one and only time a pipeline command creates one; no later command touches
it. The repo's pre-existing .tootoo/ folder is an older, unrelated viewer, not a
sync target.
tech — moved from i: to j:\My Drive\tech (2026-08-09). Its Google Drive
letter can shift between machines/remounts, so a "missing" report for this target
usually means a changed drive letter, not a deleted fork — check the other letters
before treating it as gone.
The canonical list of record is this file. When a new fork is added, add it here.
- Every published
index.htmlis byte-identical to canonical production. - All per-repo difference lives in
tootoo.config.js, never inindex.html. - Dev differs from production by exactly the config block (
config.jsvsconfig.prod.js). Both readrevisedfrom the sharedsrc/index.template.html, so after a promote they carry the same stamp. - No command edits
tootoo.config.js. - Only
publishperforms git writes; it always confirms first.
i:\My Drive\tech— included as a full sync + publish target (it is a git repo).heritage-happenings.github.io— added 2026-06-21 as the 13th target. It's a TooToo build with its owntootoo.config.js, and a git repo. The first scan reported it as a bare relativeindex.html(it's the working dir), which got misread as a phantom file at the container root — there is noindex.htmlat the container root itself.- Dirty downstream
index.html— overwrite it. Downstreamindex.htmlis generated, never hand-edited, so an uncommitted copy is a stale prior sync, not work to preserve (git keeps each repo's last commit; sync touches onlyindex.html). Resolved 2026-06-21 after the first real sync found 4 such repos. - Branches — all repos push to their default branch. No
gh-pagesspecial-casing for now. - Commit messages — derived from the changelog:
publishuses the dated entry thatpromotewrote toREADME.mdas the commit message for the run.
Uses the installed Chrome + built-in .NET image conversion. No installs, no server. Output keeps the existing filename so no links change.
- Subject: the app browsing its own repo —
?owner=pushme-pullyou&repo=tootoo. - Why
--virtual-time-budget: it pauses the capture until the GitHub fetch resolves, so the shot shows the rendered tree + README, not "Loading tree…". - Why
--user-data-dir(a fresh temp profile): without it, if any Chrome window is already open the launcher just routes to that running session ("Opening in existing browser session") and writes no PNG. A throwaway profile forces a separate headless instance. - Do NOT pass
--run-all-compositor-stages-before-draw: combined with--virtual-time-budgeton current--headless=newit hangs and produces no PNG. Verified 2026-06-26 — dropping it captures reliably. - Format: Chrome emits PNG; we convert to JPEG q90 to preserve
tootoo-screenshot.jpeg(smaller, no README edit).
$chrome = "C:\Program Files\Google\Chrome\Application\chrome.exe"
$root = "G:\My Drive\2026-theo-github\pushme-pullyou-tootoo"
$tmp = "$root\tootoo-dev\_shot.png"
$final = "$root\tootoo-screenshot.jpeg"
$url = "file:///G:/My%20Drive/2026-theo-github/pushme-pullyou-tootoo/index.html?owner=pushme-pullyou&repo=tootoo"
# 1. back up the current image (TSF = yyyy-MM-dd-HH-mm, Pacific)
Copy-Item $final "$root\.archive\tootoo-screenshot-<TSF>.jpeg"
# 2. render to PNG (fresh profile forces a headless instance; waits for the async fetch)
& $chrome --headless=new --disable-gpu --no-sandbox --user-data-dir="$env:TEMP\tootoo_shot" `
--allow-file-access-from-files --virtual-time-budget=20000 `
--screenshot="$tmp" --window-size=1440,900 "$url"
# 3. convert PNG -> JPEG q90, overwrite the asset, drop the temp
Add-Type -AssemblyName System.Drawing
$img = [System.Drawing.Image]::FromFile($tmp)
$enc = [System.Drawing.Imaging.ImageCodecInfo]::GetImageEncoders() | ? { $_.MimeType -eq 'image/jpeg' }
$ps = New-Object System.Drawing.Imaging.EncoderParameters 1
$ps.Param[0] = New-Object System.Drawing.Imaging.EncoderParameter ([System.Drawing.Imaging.Encoder]::Quality, [long]90)
$img.Save($final, $enc, $ps); $img.Dispose()
Remove-Item $tmp