What can break — and what tells you

Aga, 13 Aug 2026: *"I also need to know what can break. If something breaks, you will
know."*

Two people now work the same estate from two machines. Everything below is a failure

mode that has actually happened or been measured, not a theoretical one. Each entry

says what it looks like, why it happens, and what catches it.

The one-command answer to "is this checkout healthy?" is /doctor

(python3 tools/ops/fleet_doctor.py). The estate-wide answer is

python3 tools/ops/estate_selfcheck.py.


1. 🔴 The silent class — things that look fine and are not

These are the dangerous ones. A loud failure gets fixed; a quiet one ships.

A number that is stale but stamped fresh

What happens: refresh_cockpit_daily.py needs twelve tools that deliberately do not

travel to a collaborator's checkout. Every one of those calls is soft-failed, so run from

a Codespace it does not crash — it produces a cockpit whose YouTube, Retention, Jobs,

Email, Social and Console panes keep their old committed values, stamps

<!--STAMP--> with "auto-refreshed today", and pushes.

Caught by: a host guard. The refresh now refuses to run anywhere except the sync hub

and prints what to do instead.

If you need a number corrected from a Codespace: fix the pull tool (the fix syncs to

both sides), edit canonical_strategy.json (human-owned), or record it with learn.py.

An edit inside a <!--MARKER--> block

What happens: your text survives the sync, then the 06:00 job overwrites it. Losing it

is indistinguishable from success.

Why: marker interiors are machine-owned and rewritten from data every morning.

The rule: canonical_facts.json is machine-owned — never hand-edit.

canonical_strategy.json is human-owned — edit freely, it renders into the markers.

They sit in the same directory and nothing in the file says which is which.

Loud counterpart: damaging or deleting a marker hard-fails the whole run. Only the

silent case needs care.

A credential that exists but is dead

What happens: a presence check passes; the failure surfaces days later inside a fleet

run, far from the cause.

Caught by: /doctor authenticates every checkable credential against its live API.

Five (Amplitude, Google Ads, Apple Search Ads, App Store Connect, AppsFlyer) need a JWT or

OAuth exchange to probe, so they read PRESENT (not live-checked) — and that phrasing

is deliberate. Never report those as verified.

A guard that passes because it stopped looking

What happens: a scan whose pattern silently stops matching reports a clean estate.

Measured: the learning ledger's carrier alarm once counted 4 carriers on a day it had

none, because the substring learn.py also matches selflearn.py.

Caught by: every guard here is driven against fixtures in both directions on each run —

the privacy scanner is fed seven real founder-figure shapes it must catch and thirteen

legitimate ones it must not.


2. The sync between the two machines

A merge REFUSED (not a conflict)

Looks like: the sync stops, both directions freeze.

Why: incoming commits touch a file with uncommitted local changes on the hub — often

because a generator rewrote a shared file. Nothing is in disagreement.

Fix: run a full sync (push first). The error now says REFUSED and names the files.

Was: two permanent landmines — context/GROWTH_CONTEXT.md and

.github/workflows/deploy.yml were tracked but unmanifested, so they showed dirty

forever and any edit would freeze the sync. Both are in the manifest now.

A genuine CONFLICT

Both sides changed the same lines within one 15-minute window. The sync aborts, the work

tree is untouched, and the cockpit watchdog emails. This needs a human — it is correct

behaviour, not a bug.

Two people appending to the same ledger

LEARNINGS.jsonl and COLLAB_LOG.jsonl are merge=union in .gitattributes, so

simultaneous appends never conflict. Cost: interleaved lines may not be in timestamp

order. Every row carries its own ts; sort, never assume file order.

The hub is off (Aga travelling)

Cross-sync pauses. Nothing is lost — commits queue in git and flow on the next run.

Phase G moves the scheduled sync onto GitHub's servers to remove this dependency.


3. Publishing

A deploy that is refused is the completeness gate working. build_docs_site will not

publish a partial tree, because a partial publish deletes the docs it could not build.

Run /doctor and read the DOC ESTATE section rather than working around it.

Two publishers, one estate. Both machines can /estate-sync. Pull first, publish

deliberately — Cloudflare Pages' free tier caps at 500 builds/month and deploy_guard

debounces bursts.

Status codes are useless for verifying a doc is live. Cloudflare Pages serves

index.html for any unknown path, so a missing doc returns 200 with the index in it.

Only a content-aware check finds it.


4. Credentials and access

If this happensWhat to do
A credential leaks, or access must endpython3 tools/ops/codespace_secrets.py revoke --yes — removes all 49 in one command. Then remove the collaborator.
Meta reads as brokenCheck for (#4) Application request limit reached — that is a rate limit wearing HTTP 403. Retry later; the token is fine.
WordPress reads as broken from a scriptSend a real User-Agent. The WAF 403s Python-urllib while curl gets 200 on the same credentials — this reported three valid logins as dead.
A Cloudflare token "fails verification"An account-scoped token fails /user/tokens/verify while working perfectly. Probe an account endpoint.
The Codespace comes up under-provisioned.env.example is executable configurationpostCreate.sh reads it to decide what to write. If it is stale, credentials go missing with no error.
You want to re-run setupCreate a fresh Codespace. gh codespace ssh does not inherit the injected secrets, so re-running postCreate.sh over ssh writes zero credentials and destroys the good .env.local.
Claude asks Nic to log inThe CLAUDE_CODE_OAUTH_TOKEN secret is missing or expired. Aga re-runs claude setup-token.

Billing: Codespaces usage on this repo is paid by caliacademy (Aga's account), not Nic's.


5. The privacy boundary

Never leaves Aga's machine: tools/ops/FLEET_LOG.jsonl (it carries CFO/cap-table rows

and a row naming Nic's own salary, legal name and Wise transfer), the founder c-suite seats

(tma-cfo, tma-ceo, tma-cxo, tma-coo, tma-founder-coach, tma-pm-lead),

EXPERT_PROMPTS/FINANCE, money-briefs/, the agent governance charter, and the war room.

Enforced, not remembered: fleet_log.py selfcheck scans the entire shared set on every

estate run and fails on any bank-cash, burn or runway figure. Operating metrics —

cash pace, the $15K target, cash-per-sale — pass clean; that distinction is the whole

point and it is fixture-tested both ways.

🔴 Adding a file to growth_manifest.txt is a disclosure decision. Content-audit it

first. Withholding a file is not the same as removing a number: the same cash and burn

figures had also been pasted into six other shared files.


6. Known open items

  • A YouTube write token is in git history (token_youtube_tma_write.json,
  • youtube.force-ssl — edit rights over 869 videos). Untracked now, but untracking does

    not erase history. Rotation is Aga's decision and has not been done.

  • /metrics-check cites three scripts that do not exist (pull_analytics.py,
  • build_report.py, raw_ga4.json). Surfaced by the new CITED-PATHS check.

  • Instagram insights need a token that does not exist yet (FLEET-85). Every other
  • read source works with no new secret.