Skill packs › Skill
Boil the Ocean — operating principles for persistent agents
Skill file boil-the-ocean.md · last updated Sep 14, 2026
This file is the operating layer beneath all ten skills in this pack: it changes nothing about WHAT each skill does and everything about HOW an agent runs it. It matters now because the agents running these skills — Claude Fable 5 and similarly capable models from OpenAI and Google — loop, self-correct, hold memory, and finish end-to-end, so stopping at 90% stopped being a constraint and became a choice.
The principle
Retire “don’t boil the ocean.” That advice kept teams focused when implementation was expensive. Garry Tan’s February 2026 post (https://garryslist.org/posts/boil-the-ocean) names the turn: AI compresses implementation time 10–100x, so raise ambitions 10x and take on the whole problem.
The working rule: always prefer the complete approach over the 90% shortcut — full coverage, edge cases, error paths, the test, the doc. The delta between “mostly done” and “done” now costs seconds, so the marginal cost of completeness is near zero.
This was already the house rule. Dennis has taught “always boil the ocean — because good enough is not enough” for years; in his words: “do the whole thing, do it right, with tests and docs; ship the finished product, not a plan.” Persistent agents are the first workers that can hold that bar on every run — without fatigue, without being asked twice.
How to run every skill now (Fable 5 and friends)
Seven rules. They apply to every skill in this pack, on every run.
- Loop until the Definition of done passes. Every skill states its output or done-bar. Run, check, fix, run again — 90% is a draft, not a deliverable.
- Self-verify every run. Before reporting done, grade your own output against the skill’s QA checklist or Definition of done, line by line. No gut calls.
- Use memory to compound. Read prior outputs, notes, and the upstream skills’ results before starting. Never start from scratch twice — the second run begins where the first ended.
- Document every run as a meta-article example. What happened, what broke, what you’d change — the write-up is the fuel for recursive self-improvement (below).
- Prefer full coverage. Handle the edge cases, write the test, write the doc. Sweep every source, not the first page of results.
- Persist through obstacles. Fix root causes, not workarounds — a workaround is a deviation you re-pay on every future run.
- When blocked, surface it and keep moving. Name the blocker, what you need, and what you did instead — then finish everything that isn’t blocked.
Recursive self-improvement
Do → Document → QA → Example (Meta-Article) → Improve (SOP update) → ↺
Every run of every skill feeds this loop: do the task per the SOP, document the run, QA it against the canonical instructions, publish the run as a worked example, fold the fixes back into the skill, run again. Each cycle the library gets sharper and needs less of you.
The loop is the recursive-self-improvement-qa skill (step 10 of this pack); https://blitzmetrics.com/knowledge-system-maintenance documents the maintenance standard behind it.
Why this is positive-sum
- Ephemeralization (Buckminster Fuller): do more and more with less and less until you do everything with nothing. Every documented, agent-run skill takes fewer human hours per result each cycle.
- Jevons Paradox for intelligence: when intelligence gets cheap, the work doesn’t shrink — the amount of work worth doing explodes. Efficiency means more usage, more clients served, more jobs. Not fewer.
- The chain: documented skills → agents that run them → a marketplace where those agents work and eventually earn → operators freed for the judgment work only they can do.
- The mission: that chain is the engine behind Dennis’s goal of creating a million jobs — completeness at near-zero marginal cost, multiplied across everyone who installs the library.
Notes — Dennis’s method
- Dennis’s rule, verbatim: “The marginal cost of completeness is near zero with AI — do the whole thing, do it right, with tests and docs; ship the finished product, not a plan.”
- This file governs execution; each skill still owns its inputs, steps, and outputs. Where they meet, the skill’s Definition of done wins — this file just forbids stopping short of it.
- Boil the ocean on coverage, proof, and verification — never on adjectives, scope creep, or invented work. Completeness is not padding.
- Rules 1 and 2 travel together: a persistent agent that loops without verifying just automates its own mistakes.
- Install this file alongside the ten skills (Project knowledge, Skills, or your
memory/folder) so every chat that runs a skill runs it this way. - Date every run. “We keep the skills current” is a claim; a dated meta-article trail is the proof.
Model landscape — kept current (August 1, 2026)
Reviewed the past month’s releases. The persistent-agent thesis this file is built on is now shipping infrastructure, not a bet:
- Claude Opus 5 (July 24, 2026) added a 1M-token context window and a per-turn reasoning-effort dial — low, medium, high, xhigh. Treat that dial as a routing lever, not a preference: raise effort for the one hard call inside a skill instead of moving the whole run to a pricier model, and drop it for the mechanical steps. Escalate effort before you escalate model.
- Claude Sonnet 5 (June 30, 2026) is the cheap agentic workhorse — it plans, drives browsers and terminals, and self-checks. Route bulk skill runs here: draft, collect, sweep, first pass. It is now the sensible default for any step that chains several tool calls; smaller models stay right for single-shot classification and extraction inside a step.
- Claude Fable 5 returned July 1, 2026 after a two-and-a-half-week export-control suspension. It stays the ceiling for verification and the hard calls — and that outage is the lesson worth keeping. Never let a scheduled run hard-depend on a single model. A routing table without a fallback tier is a 4am job that fails at 4am for reasons nobody in the building controls.
- Scheduled agent tasks now run in the cloud (July 7, 2026) — a recurring job keeps going with the laptop closed. The carve-out matters more than the headline: work that touches local files or drives a browser still needs the desktop app open. Know which of your scheduled skills are which before you assume the schedule is covered.
- The MCP spec revision dated 2026-07-28 moved to a stateless core and is not fully backward compatible. Before upgrading any connector a skill depends on, check which revision it speaks — a routine version bump is no longer automatically safe.
- Every major vendor now ships this file’s assumptions. OpenAI’s GPT-5.6 (July 9, 2026) exposes Sol, Terra, and Luna as an explicit cheap-to-flagship ladder and adds programmatic tool calling, where the model writes a small program to coordinate tools instead of round-tripping each one. Google’s Gemini 3.6 Flash and 3.5 Flash-Lite (July 21, 2026) fold computer use in as a built-in tool. A tiered ladder, real tool use, long-horizon runs — that is the shape of the whole field now, not one vendor’s bet. Gemini 3.5 Pro had not shipped as of this review.
- Managed Agents run skills on a schedule with vault-stored secrets and browser/CLI access — this file’s “persistent, looping agent” is a product surface, not just a way of working. This library is itself kept current by one.
Rule of thumb after this month: pick the cheapest tier that clears the bar, turn the effort dial before you turn to a bigger model, and give every scheduled job a fallback. See model-judgment for the full routing ladder.
Definitive article & links
- The source idea: https://garryslist.org/posts/boil-the-ocean — Garry Tan, “Boil the ocean” (Feb 2026)
- Keeping the library current: https://blitzmetrics.com/knowledge-system-maintenance
- The engine the skills run on: https://blitzmetrics.com/content-factory/
- The standard at scale — the BlitzMetrics Task Library: https://blitzmetrics.com/task-library-dashboard/
- Applies to: all ten skills,
personal-brand-strategistthroughrecursive-self-improvement-qa.
Learned in the field
Appended automatically by the self-improvement loop (Skill-Learnings/): dated lessons from real runs. Newest at the bottom.
<!– learning:2026-08-09-measure-before-you-write-a-field –> August 9, 2026 (from: sigrun-website-request-intake (second pass) — closing the og:image gap on christineboersdoets.nl and solhalvorsdatter.com)
A fill and an overwrite look identical afterwards. Only the measurement taken before the write can tell them apart.
The og:image gap on two client sites had been parked on a human for six days: “open Rank Math → Titles & Meta → Social and set a default OpenGraph image.” One fetch of the rendered <head> showed Rank Math’s social module was already active and emitting seven og tags — it was short exactly one input, an image. Rank Math falls back to a page’s featured image, and the purpose-built 1200×630 social cards had been sitting in each media library attached to nothing. Setting featured_media over /wp/v2 with credentials we already held closed the gap on 22 URLs without anyone’s wp-admin.
Then it broke something, and the only reason that was caught is a listing pass taken minutes earlier.
Pages on these sites render no featured image; posts do, and so does the blog index. So writing the social card onto the posts silently replaced the wide hero photo at the top of every article with a share card. The write looked like filling an empty field. It was an overwrite. A listing pass a few minutes earlier had recorded featured=7 on Sol’s posts, which is what made the difference visible — and the same evidence told me what to restore.
Rules this produces
- Record the current value of any field you are about to set, in the same run, even when you are confident it is empty. After the write, “was empty” and “was something else” are indistinguishable.
- Blast radius is a property of the template, not of the field. The same field was invisible on pages and visible on posts, on one site, in one theme. The generalisable check is counting the rendered markup on the specific URL before and after — not reasoning about what the CMS “usually” renders.
- A stale ask parked on a human deserves one measurement before it is re-sent. The question is not “has someone done it yet” but “is a human still the only thing that can do it.” This one had stopped being true days earlier.
- A timeout mid-loop is a fact about the observer. When the bulk write hit the tool cap partway through, the right move was to re-query live state — which is precisely what revealed that one site had finished, the other had stopped mid-list, and the posts carried a different pre-existing image.
- Prove an “it cannot be automated” claim from a second direction before repeating it. “Permalinks need wp-admin” had rested on one missing settings key. Enumerating the install’s whole REST namespace list and the Abilities API’s actual abilities (both read-only) settled it — and incidentally produced structural proof that Rank Math was absent from that install, which a visual check had only hinted at.
The verification shape that worked
Every write was followed by a rendered re-fetch of that exact URL with a cache-buster, counting og:image, twitter:image and wp-post-image/wp-block-post-featured-image, with an automatic revert branch on either “an image now renders that did not before” or “og:image is still absent.” The revert branch is what makes the write safe to attempt at all: a change that cannot be self-detected as harmful should not be made in an unattended run.
<!– learning:2026-08-09-an-empty-capture-proves-nothing –> August 9, 2026 (from: grokipedia-fleet — an armed fetch hook captured nothing on an edit that had already landed)
A silent instrument is not a negative reading. Absence of your evidence is not evidence of absence.
Grokipedia’s edit modal looks identical whether a submission succeeded or was refused. We solved that months ago by arming a fetch hook before clicking Submit and reading the POST body, and wrote the rule down in the skill:
{"success":true,"id":"<uuid>"}is the only proof. Empty__cap= blocked client-side, nothing
created, safe to retry later.
On 2026-08-09 the Escape Fitness edit came back with an empty __cap from a correctly-armed hook, and the modal cleared exactly the way a refused attempt does. By our own written rule that was a non-event and the next step was to retry. The account said otherwise: Total Edits had moved 8 to 9. The edit had landed. A retry would have filed the same correction twice on a client’s page.
Four minutes later the Matthew Januszek edit did capture a fetch POST, id and all. Same site, same modal, same session, same hook — one submission visible to the instrument and one not. So the application does not use a single transport for this action, and a hook on fetch alone (or on XMLHttpRequest alone, or on both — sendBeacon exists too) is a coin flip dressed up as proof.
The deeper error is in the shape of the rule, not the choice of transport. It assigned meaning to silence. A positive capture is real evidence: something happened and here is its id. A negative capture is the instrument saying “I saw nothing”, which is consistent with both “nothing happened” and “it happened somewhere I wasn’t looking.” Those two are not distinguishable from inside the hook, and no amount of hooking more transports fixes that — it only narrows the gap while leaving the logic backwards.
The check. For any write whose success you cannot see directly, find the counter the system keeps — a total, a list, a row count, a version number — and read it before and after. Your own capture is corroboration. Theirs is the verdict. If the system exposes no such tally, say so in the report rather than promoting your instrument to an authority it has not earned.
The tell. Any rule of the form “if I didn’t observe X, then X didn’t happen.” Also: retry logic gated on a negative observation from a probe you built. That is the moment a monitoring gap turns into a duplicate write, which is worse than the original uncertainty because it is now visible to a client.
Related. Same family as [[checks-must-be-able-to-fail]] and [[failed-probe-must-not-look-clean]], and the second instance in this single run of the broader rule [[the-account-is-the-ledger-your-notes-are-a-guess]] — earlier the same day, a run’s own notes recorded one submission where the account showed three. Twice in one run, trusting the artifact we generated over the tally the system keeps would have caused a duplicate write.
What shipped: the task prompt’s edit step now reads the Total Edits counter before and after every submission and treats a captured id as corroboration only; the hook was extended to XMLHttpRequest while explicitly demoting it below the counter; and pages.json records the Escape Fitness edit as confirmed by the counter, with a note naming the wrong rule so the next run cannot inherit it.
<!– learning:2026-08-09-fix-the-harm-not-the-story-you-told-about-it –> August 9, 2026 (from: skill-pack-propagation daily run — a second run of the day was one line away from erasing the first run’s log)
When you fix an incident, fix the HARM it caused, not the story you told about how it happened.
On 2026-08-03 two copies of the daily runner ran at once. The written-up harm had two distinct parts: they published to the same eight live sites concurrently, and the second copy’s : > "$LOG" erased the first copy’s log, so the surviving record under-reported the day by 107 skill-file updates. The remedy shipped was a single-instance lock, and it is a good lock — 16 test cases, atomic mkdir, stale-PID reclaim, taken deliberately before the truncate so a refused copy cannot destroy a running copy’s log.
It fixed the first harm completely and the second one not at all. The log is named for the DATE, so any second run of the day — an hour later, no concurrency, lock free, taken and released politely — still truncates its predecessor’s record. On 2026-08-09 a manual run finished at 03:11:30 having published ten surfaces and pushed commit a251cdd, and the scheduled run at 04:55 was one line away from deleting the only account of it.
The incident had been narrated as “two copies ran at once”, so the fix was scoped to concurrency. But the loss of the log was never caused by simultaneity; it was caused by a truncate against a name that repeats. Simultaneity was just the shape it arrived in. Six days of green runs went past, and the very comment block explaining the erasure sat four lines above the unguarded : > "$LOG" that would do it again.
The check. After writing an incident up, read your own account back and list the harms as separate line items — not the sequence of events, the damage. For each one, name the line of code that now prevents it. Any harm whose answer is the same mechanism as another harm’s deserves a second look: one guard covering two failures usually means one failure was assumed away. In this case “concurrent publish” and “log erased” both pointed at the lock, and only the first was true.
The tell. A fix aimed at the circumstances of an incident rather than at its effect reads as a rule about when the bad thing may happen (“never run two at once”). A fix aimed at the effect reads as a rule about what is now impossible (“a completed log is never overwritten”). Prefer the second: circumstances recur in shapes you did not enumerate.
What shipped: tools/rotate_run_log.py moves an existing non-empty log to <stem>.runN.log (max+1, never backfilling a gap, refuses to clobber) immediately before the truncate, and tools/test_rotate_run_log.py (20 cases) runs as a pre-flight gate and asserts both the behaviour and the wiring — including that the call sits before the truncate in the runner, because a fix recorded in prose is not a fix applied to the runner. It earned its place on the first live run: 28,581 bytes of the 03:11 run preserved as _daily-run-2026-08-09.run1.log instead of deleted.
<!– learning:2026-08-09-the-account-is-the-ledger-your-notes-are-a-guess –> August 9, 2026 (from: grokipedia-fleet monthly run — the run’s own notes said 1 submission, the account said 3)
When a system holds your history, read the system. Your notes stop at the moment you stopped.
The Grokipedia fleet run submits a capped drip of three requests a month, and it writes what it submitted into pages.json. On 2026-08-09 the run was interrupted after the submissions landed but before it finished. pages.json recorded one submission, Dennis Yu, complete with a captured POST id — exactly the evidence discipline the skill demands, and entirely correct as far as it went.
The account showed three. Article Requests had moved 107 to 110, and the activity feed listed Dennis Yu, Michael Krigsman and CXOTalk at 27, 26 and 25 minutes old. The next agent to pick the job up read the notes, saw one submission against a budget of three, and was two steps from spending a budget that was already gone.
The notes were not wrong. They were truncated — they described the run up to the last moment the run was alive to write anything down. Every record a process keeps about itself has this property, and it is worst precisely when the process died, which is exactly when someone else comes looking.
The same run had already been bitten by the deeper version of this. The August 2 entry announced that Grokipedia “generates pages about our people WITHOUT us” and led with a client, Matthew Januszek. We had requested Matthew Januszek ourselves on January 23. The ledger recording that had been sitting in our own account since January, unopened, for the entire month the claim stood in a changelog and an email to ops. A whole narrative about an external system’s behaviour, built without asking the external system what it had on file.
The check. Before you act on a count of something you did — submissions, posts, invites, uploads, API calls — ask the system that holds it. Not the log you wrote, not the memory file, not the summary from last run. If the external system exposes a total, read the total and diff it against your record; a mismatch is information, and the direction tells you which way you were wrong. If it exposes no total, that absence is worth naming in the report rather than papering over with your own tally.
The tell. Any sentence of the form “we have submitted N so far” where N came from a file you wrote. Also: a plan whose next step depends on remaining budget, quota, or rate limit computed from local state. Those numbers live somewhere authoritative, and it is nearly always one cheap read away.
Related, and the reason this keeps recurring: the same failure shape as the eleven days the agent-runtime plan spent naming a human blocker that had already cleared — a git ls-remote would have said so on day one — and as the weekly Dorine check that compared a source against its own snapshot and never against the clock. In all three, the local record was internally consistent and externally stale. Consistency is not currency.
What shipped: the skill’s submission rule now says to count the budget from the live Article Requests total at the start of the run and before each submission, not from the run’s notes; the two undocumented submissions were recovered from the account and written back into pages.json with a reconciliation block naming how they were found; and the ordering rule the ledger disproved (person-before-company) was corrected in the scheduled prompt so the next run cannot inherit it.
<!– learning:2026-08-10-a-stable-set-that-moves-is-a-different-finding –> August 10, 2026 (from: anthony-hilb-seo-tracker, week 8 — a page left Google’s index and the tracker could only see it because it re-checks the whole set, not the known-bad subset)
Track state history, not current state — “6 pages unindexed” and “a page just fell out” are different diagnoses wearing the same number
For seven weeks this tracker reported the same fact: 10 of 16 published guides are in Google’s index, 6 are not. On week 8 it reported 9 and 7. One page — indexed on 27 July and again on 3 August — had been dropped.
The number moved by one. The diagnosis moved much further than that.
- “Six pages Google never selected” is a story about pages. It invites page-level fixes:
more words, better internal links, a distinctiveness pass. (This tracker recommended exactly that on 27 July, then withdrew it on 3 August after measuring that the unindexed pages were no thinner than the indexed ones.)
- “A page Google indexed and then dropped” is a story about the site. Google evaluated
this domain, made a decision, and reversed it. No page-level edit explains a reversal on a page that is HTTP 200, index, follow, self-canonical and 1,777 words.
The second reading only exists because the run had a prior observation of that specific URL in a specific state. Had the tracker checked only the six known-bad URLs — the efficient thing to do, and the tempting thing after five identical weeks — the drop would have been invisible, and the report would have said “no change” for the eighth time on the week the account’s story actually changed.
Rules:
- Re-check the whole tracked set every run, never the subset that was failing. The known-bad
items are the ones least likely to teach you something new; the known-good ones are where regressions hide. An inherited “problem list” silently becomes the definition of what the agent can notice.
- Record per-item state history, not just the current tally. A snapshot that says
indexed: 9, not_indexed: 7 cannot answer “which one changed, and when?” — the question that distinguishes one page wobbling from the start of a slide. Store the per-URL verdict with a date so the next run can diff it, rather than re-deriving the diff from prose in last week’s report.
- **A metric that has been flat for N weeks is not evidence that it is stable. It is evidence
that you have been measuring one thing.** Five flat weeks here preceded an indexation finding; two more preceded a backlink-classification finding; the eighth produced a deindexation. Each time, “nothing moved” was true of the number being reported and false of the account.
Corollary, from the same run: a forced degradation can be an upgrade — check before you apologise for it
The Ahrefs unit pool was exhausted mid-month (104,072 of 100,000 workspace units, shared across seven client trackers), so DR, organic keywords and organic traffic were simply unavailable. The reflex is to treat that as a diminished run and say so apologetically.
Two things were true instead:
- The lost metric was already worthless here. DR had been formally retired as a progress
metric for this site the week before, once 96% of its referring domains turned out to be link farms. Losing a number you had already stopped trusting costs nothing.
- The substitute was better than the original. The tracker normally answers “do we rank for
these 7 target topics?” from Ahrefs’ organic-keywords estimate. With Ahrefs down, it asked Google, live, pages 1–2, testing the SERP HTML for a scheme-qualified URL. That is the search engine’s own answer rather than a vendor’s model of it. The method is being kept after the quota resets.
Rule: when an outage forces a substitute method, evaluate the substitute on its merits before labelling the run degraded. Vendor APIs are convenient proxies for questions the primary source will often answer directly and better. An outage is a free, forced experiment in whether the convenient path was also the correct one — and sometimes the fallback should become the default.
And the half that must not be softened: where a metric genuinely could not be measured, write null and say “unavailable.” Do not carry the prior week’s value forward into the slot, and do not let it appear in a table beside measured numbers. A tracker whose whole job is detecting movement cannot afford a number that looks measured and is actually a memory. This is the same family as “distinguish ‘answered no’ from ‘did not answer'” — one code path, two opposite facts.
<!– learning:2026-08-10-coverage-metric-from-a-frozen-file –> August 10, 2026 (from: weekly-fleet-hub-audit 2026-08-10)
A coverage number must be derived from the system it claims to describe, never from a file that only a human edits. If the source never changes, the metric cannot go up when the work lands and cannot go down when access is lost — it is a constant wearing a KPI’s clothes.
Found 2026-08-10: the fleet pulse reported “verified in Search Console: 9/92” every week from a CSV last written 2026-07-17. On 08-03 it printed 9 while gsc-enrichment.json did not exist at all — the number had zero live evidence behind it. When this run actually pulled Search Console, two of that CSV’s eleven VERIFIED rows (jasongamato.com, piotrzawislak.com) returned “you don’t have access” on every resource_id form. So the frozen source was not merely stale, it was wrong, and it was wrong in the flattering direction.
The fix is a resolution order that records HOW each fact is known, not just the fact:
- live — we pulled real numbers for it this run. Proof of access.
- lost — we tried this run and were refused. Proof of NO access, and it must outrank any stored claim.
- claim — neither; fall back to the stored file, but label it, and report claims separately from proven coverage.
Report the three counts side by side. The moment claim is visible as its own number, nobody can mistake an assertion for a measurement, and a property that quietly falls out shows up as lost in the same week rather than being carried forward forever by a CSV.
Two collection gotchas worth carrying:
Search Console resource_id form. Our fleet properties are URL-prefix (resource_id=https%3A%2F%2F<domain>%2F), not domain properties. Querying one as sc-domain:<domain> returns Google’s “Oops, you don’t have access to this property” page — byte-identical to the response for a property you genuinely lost. Guessing the wrong form therefore manufactures a false regression. Try BOTH forms before recording any property as lost; only a domain that refuses every form is actually gone.
Vendor quota is a single point of failure for the whole proof layer. This run the Ahrefs workspace sat at 104,072 of 100,000 monthly units, nine days from reset, so every proof call returned “API units limit reached”. Do not paper over that by writing another vendor’s numbers into the same fields: DataForSEO’s backlink rank is 0–1000 and Ahrefs’ Domain Rating is 0–100, and DataForSEO’s referring-domain counts run roughly 10x lower on this fleet because it does not index the nofollow spam blast Ahrefs counts. Silently swapping them would have rendered as a fleet-wide authority collapse that never happened. Keep the second source in its own file with its scale documented, leave the primary values untouched and stamped with the date they were actually measured, and say plainly in the report that Proof contributed zero week-over-week delta this run.
<!– learning:2026-08-10-page-one-is-not-the-ledger –> August 10, 2026 (from: grokipedia-fleet — six-page submission history read as if it had one page, for seven months)
A paginated list read to the end of page one is a partial read that feels complete.
Our Grokipedia account holds 110 article requests, shown 20 at a time across six pages. Every run of this job read page one, built a list called already_rejected_do_not_resubmit_without_new_evidence, and then reasoned from it with confidence. The list had twelve entries. The real number is forty.
The cost was not abstract. On 2026-08-09 we posted to a client’s Basecamp project telling him that his company was the way into the encyclopedia, because the company gets accepted where the individual gets rejected. Showcase Remodels had been submitted on 25 January and rejected. It was on page two. He read a confident recommendation to try the one thing we had already tried and been refused.
What makes this failure mode nasty is that page one is not obviously partial. It fills the screen, it is sorted newest-first so it looks current, and the twelve rejections it did contain made the list feel researched rather than truncated. Nothing about the artifact announces “there are five more pages of me.” Compare a truncated file read, which usually leaves a visible seam.
The check. Before treating any list as complete, find its declared total and reconcile. Grokipedia printed “21–40 of 110” at the bottom of the page the whole time — the count was sitting there in the footer while we generated a twelve-item list from it. Ask: what does the system say the total is, how many did I actually read, and do those match? If there is no declared total, page until a page comes back short, and say in the report how far you got.
The tell. Any assertion of the form “we have never tried X” or “the complete set is Y” derived from a UI that paginates, an API with a default limit, a search that caps at 20 results, or a scroll container. Also: a list whose length is suspiciously close to a round number — twelve rejections out of an unknown total should have prompted the question, and forty out of a stated 110 would not have.
Related. This is the third instance in two days of the same underlying error, which is why it is worth its own note rather than a line in a changelog: [[the-account-is-the-ledger-your-notes-are-a-guess]] (our notes said one submission, the account said three) and [[an-empty-capture-proves-nothing]] (our instrument saw nothing, so we concluded nothing happened). All three are the same shape — an artifact we produced, treated as authoritative over the system’s own record. Notes, hooks, and first pages are all partial views that feel total.
What shipped: the task prompt now has a dedicated step, ahead of any submission, that reads all six pages via ?page=N and refreshes the stored decided-set; pages.json carries _full_ledger_2026_08_10 with all 40 rejections, all 29 creations, and the pagination recipe; and the client who got the wrong recommendation was told directly, leading with our error rather than the platform’s.
<!– learning:2026-08-10-redirect-hides-a-dead-site –> August 10, 2026 (from: weekly-fleet-hub-audit 2026-08-10)
Resolve the FINAL host of every site you audit, and compare it to the domain you asked for. HTTP libraries follow redirects silently, so a domain that has been pointed at a different site returns a clean 200 with a full, healthy page — and every downstream check then grades the DESTINATION while printing the SOURCE’s name.
Found 2026-08-10: archiepadley.com 200s straight through to dennisyu.com. The weekly foundation audit had been scoring Dennis Yu’s homepage as Archie Padley’s site — green on homepage_up, has_title, entity_schema and sameas_links — for a personal brand site that does not exist. The audit had no way to notice, because none of its checks ever asked “whose page is this?”
Two rules fall out of this, and they generalize past redirects:
- Compare the answer’s identity to the question’s identity.
urlopen(...).geturl()/curl -w '%{url_effective}'costs nothing. Any fetch whose final host differs from the requested host is a different subject: report it as REDIRECTED and stop, rather than scoring it. The same applies to a canonical tag pointing off-domain, an OG:url naming another site, and a Person schema whose name is not the site’s person.
- A green foundation score is not an identity check. Title, meta description, schema presence and sameAs count all pass on a page about the wrong human. On the same run
azuifeachor.com— Azu Ifeachor’s site — was publishing a page at slugabout-felix-fagbuyi, all foundation checks green. For an entity home, serving a second person’s name is the most damaging on-page defect there is, because it teaches Google and every LLM the wrong entity, and it survives an all-green report indefinitely.
When you add the identity check, make it precise before you ship it. The first version compared token sets, which reads the concatenated domain ayeshafarrukh.com and the hyphenated slug about-ayesha-farrukh as two different people — 2 real hits inside 4, a 50% false-positive rate on the one signal meant to be exact. Compare on the squashed letter-stream as well as tokens. And keep the noisy corroborating signal subordinate to the precise one: flagging any foreign person-name in homepage copy put 83 of 92 sites on the list, because podcast sites legitimately name their guests — a list that long is indistinguishable from no list at all.
One more trap in the same check: a consuming regex for capitalised name pairs reads “About Felix Fagbuyi” as (About, Felix), discards it as a stop word, resumes past “Felix”, and can never form (Felix, Fagbuyi) — the exact name the scanner exists to catch becomes invisible. Use a lookahead so matches overlap.
<!– learning:2026-08-11-a-gate-run-only-in-clean-conditions-tests-nobody –> August 11, 2026 (from: skill-pack-propagation daily run)
A gate that only ever runs in cleaner conditions than production is testing a program nobody executes
The daily skill-pack job did not run on 2026-08-11. Not “ran and failed a step” — it refused during pre-flight, published nothing, wrote no log, took no lock, and left a 45-byte console containing one line. Meanwhile every gate the pipeline owns was green, on that same machine, that same minute, when run by hand.
Auto-detach had shipped at 05:58 the previous day. 08-11 was its first scheduled morning.
The chain. The launcher exports SPP_DETACHED_CHILD=1 into its child — correctly, to stop the child (whose stdout is a file, which is also “not a terminal”) from detaching forever. The child’s pre-flight then runs tools/test_detach.sh, and two of that file’s cases assert the no-flag default by spawning the runner and reading its answer. The spawned runner inherited the fence. It reported MODE: foreground, which is correct for that environment and wrong for the assertion, and the pre-flight refused to start the pipeline.
Why nobody saw it. The same test file politely backed up _daily-run-<date>.console at entry and mv‘d the copy back at exit — a file it also let its --detach children truncate. That file is the live run’s fd 1. mv replaces the inode, so from that moment the running pre-flight was appending to an unlinked inode. Its refusal message — twenty-five lines naming the exact failing assertion — went nowhere. What stayed on disk was the 45-byte copy taken before the failure: a run that reads as though it simply stopped after one line.
The rules.
- A gate must be exercised in production’s environment, not a cleaner one. Every gate here
was run attached, from a terminal, with a clean env — the one way the real job never runs. If the real invocation differs from the tested invocation in any dimension (detached, piped, cron, different cwd, inherited exports), that dimension is untested. New gate: tools/test_preflight_under_detach.sh launches the runner through the real auto-detach path and asserts the whole pre-flight comes back clean.
- **A test that asserts a DEFAULT must set every input that default depends on — including the
ones it would otherwise inherit.* An inherited variable is an input nobody wrote down, so every reader assumes the clean value they see in their own shell. The case is really measuring its caller. Unset it at the top; let the one case that is *about the variable set it explicitly. (Written into the new gate too — which then made the identical mistake on its first run, inside the hour, and was caught by its own “am I really driving the path I claim to be driving?” assertion. Include that assertion in anything that spawns its subject.)
- Stdout is not an artifact. It is a handle to whatever the caller chose; it can be closed,
truncated, or substituted underneath you, and the process cannot tell. Anything that must survive to be read tomorrow gets appended to a file the process names itself. Append, never rewrite — >> cannot orphan another writer’s descriptor the way mv just did.
- Never back up and restore a file you do not own exclusively. Copy-then-
mv-back looks
considerate and is the opposite: it silently substitutes the inode under every process holding it open. If a helper needs scratch space, give it scratch space (SPP_CONSOLE), and assert afterwards that the caller’s file kept the same inode and the same size.
- Anchor structural checks on the construct, not on a line window.
grep -A 3became
-A 6 after a comment pushed the target line out of range; on 08-11 an eight-line comment blew past -A 6 and re-armed the identical false alarm in the same file. Widening a window is a longer fuse, not a fix. Ask for “the first executable line inside this block that does X” and let people write as many comments as the code deserves.
- A derivation is only as good as the claim that its source is the population. A new check
derived “slugs that must resolve” from the scheduled-task inventory and flagged 29 of 31 tasks — all false, because a task files under the skill slugs it exercised, not its own name. The real population was on disk elsewhere (the notes’ own skills: history). A false alarm wearing the words of a real regression is the most expensive kind: ship it as a blocking gate and it trains its readers to ignore the pipeline. Scope the hard assertion to what is actionable today (can the inbox drain?) and demote the rest to advisory.
- Rank a failure by DIRECTION. This one removed output. No error, no log, no lock — the
artifact left behind looked like a short, quiet, successful morning. Anything that makes output disappear deserves more scrutiny than anything that adds output, because the reader’s instinct on seeing less is relief.
<!– learning:2026-09-03-nine-ways-a-run-goes-wrong –> September 3, 2026 (from: RCA/RCF on the 2 Sep 2026 master-guide rebuild (Claude Cowork))
Nine ways a run goes wrong, and the tell for each
One session on September 2, 2026 rebuilt the MASTER AI Agent Implementation Guide, reconciled the skill marketplace, shipped a congruency gate — and made twelve distinct mistakes doing it. Twelve mistakes, nine root causes. Not one of them was exotic. Every one was the run trusting something it had not checked: a list, a memory, a cache, a click, a machine.
The master is AGENT-FAILURE-MODES.md in agent-runtime, with the incident behind each rule. Run python3 tools/agent_preflight.py before a substantive job and all nine print into your context, along with the surface map and the list of things this house has already automated.
- F1 Wrong authority — count against the master, not the nearest list. *Tell: you
are about to compare two numbers and you have not said out loud which one is canonical.* The run reported an appendix as contradicting the marketplace. It did not; the comparison had been made against a directory listing instead of the bundle manifest. Same shape twice in one session: overlapping skill bundles were diagnosed as a defect in the source when the real cause was two surfaces reading two different registries.
- F2 Unread context — read the file before you brief off it. *Tell: you are about
to state a client’s location, name, status or history from memory.* A research subagent was briefed that a roofing client is in Columbia, South Carolina. It is in Alpharetta, Georgia, and that was already written down. The subagent caught it. A downstream agent catching your fact is not a safety margin.
- F3 Human as a workaround — grep the house before you ask a person. *Tell: you are
about to write “can you paste this” or “please do this manually.”* An MCP call was refused, so the run asked Dennis to paste an audit into Basecamp by hand. The house had already built, authorised and documented a Basecamp write path. A refusal on one route is not a wall. canonical/CAPABILITY-INDEX.md now lists every path we have, and a test asserts each one still exists.
- **F4 Unverified write — assert the match count, then read it back at the
destination.* *Tell: any replace, sed or publish whose result you have not re-read from where it landed. Three in one session: a phase-ordered swap whose match keys no longer existed, a pattern with escaped quotes tested against literal curly quotes, and a file written in place while the sync client was not running. All three printed success. re.sub and str.replace return the input unchanged on zero matches, so a substitution is only a substitution if you assert how many it made. tools/lint_silent_replace.py now scans for the shape.
- F5 Stale read — a cached fetch is not the live page. *Tell: you are verifying
something you published in the last fifteen minutes.* Stale public pages were blamed on a CDN. The fetch tool’s own fifteen-minute cache was serving the old HTML; the live pages had been correct the whole time. Verify with a cache-busting request from the machine that owns the surface, never with the tool that may have cached it.
- F6 Unlooked-at output — open the artifact you are about to ship. *Tell: you built
a PDF, deck or page and have not viewed a single rendered page of it.* Tables shipped with columns so narrow that words broke mid-token: NOT_M ET, CONTRADICTE D. The build log was green.
- **F7 Stale cross-reference — change the baseline and every pointer into it is now
suspect.* *Tell: you edited an upstream table, list, ordering or version number. Text rebased onto a new baseline still cited the old row numbers. Same family as a mirror on a slower cadence than its source is not a mirror, it is a fork.
- F8 Wrong surface — the shell you have is not the machine you mean. *Tell: a path
exists in your head but you have not run pwd on the surface that owns it.* Cycles burned driving a desktop app that computer use cannot target, and a repo declared missing because the device shell only mounts connected folders. Found twice more while writing this note, which is how you know the class is real.
- F9 No abort rule — two failed interactions on a live document is a stop. *Tell: a
click or keystroke did not land where you expected, twice.* Two find-and-replace attempts put focus in a document body instead of the search field, and the next keystroke was cmd+a. One more and a live document would have been wiped. Back out, confirm the document is intact, reach it through an API.
The meta-rule. Six of the nine have a machine behind them now; three are checklist only, and the preflight prints which are which rather than showing a green light for all nine. A rule enforced by nobody has to say so — otherwise the checklist reads like coverage it does not have.
Learned September 3, 2026.
<!– learning:2026-09-04-done-means-you-looked-where-it-landed –> September 4, 2026 (from: Definition-of-done RCA/RCF, Claude Cowork, after Dennis named the loop)
Done means you looked at it where it landed and attached what you saw
On September 4, 2026 Dennis said the quiet part: “Every time an agent does something I have to remember to ask it, ‘Can you check to make sure you actually did it?’ That makes me a QA agent serving other agents.” And the same for people — they don’t understand, they’re sloppy, or they didn’t check.
The rule already existed three times over. outbound-action-closeout says read back from the source system. This skill says grade your own output. Every good task prompt says verify with a live request. Dennis was still the checker. A “done” with no proof looked exactly like a “done” with proof, so the only thing that could tell them apart was a human opening the link, and that human was the one whose time costs the most. A rule that says “verify” with no consequence for skipping it makes the boss the consequence.
The measurement, taken the day the rule was written: 102 agent completions in three days across Basecamp and the claims ledger, 5 with proof that opened, 96 with none. By platform: Cursor Grok 27, Claude Cowork 22, Grok Bot 18, Codex 17, Claude 6, Kimi 3. Nobody was lying. Nobody was checking either.
The rule. Every completion — a Basecamp comment, an agent note, a closed to-do, a run summary, a message to a teammate — ends with one line before the receipt:
Verified: <what you opened> — <what you saw> — <when>
or, honestly,
Verified: NO — <what is missing and who can supply it>
Proof is something a stranger could open and see what you saw. Sent is the message in the recipient’s thread, not your outbox. Posted is the comment when you read the thread back. Published is the live URL fetched fresh with a phrase from the new content in it. Merged is the SHA on the branch it was meant for, or the PR showing MERGED. Fixed is the broken thing exercised again and working. Delivered is the file opened in the destination folder. A verb is not a proof. “Verified: yes” is not a proof. “See above” is not a proof — nothing can open “above.”
The consequence, which is what was missing. tools/closer.py in agent-runtime runs every morning, reads every “done” of the last two days, opens every proof, and walks the ladder decided on September 2: miss 1, reopened on the same thread with the missing item named; miss 2 at 48 hours, escalated to the owner’s function; miss 3 at 96 hours, reassigned and flagged — and that flag is the only thing Dennis sees, once, in his Daily Operator Brief. An honest Verified: NO is never a miss; it is an open blocker with a name on it. Completions from before the rule are baseline, counted and never reopened.
Why this is the root-cause fix and not another checklist line. Content and Checklist were already there. Software was not — nothing could refuse a done that carried no proof. Now the proof is part of the output by definition, a machine can tell the difference, and the machine is the checker. The same closer reopens a person’s to-do the same way it reopens an agent’s comment. Same rule, same words, same script.
Learned September 4, 2026.
Showing the 11 most recent field lessons of 62. This skill is one of the most-used in the system, so it collects a lesson from almost every run. The complete history ships inside the skill file itself — download any pack and open boil-the-ocean.md.
Other skills: ai-search-visibility · client-access-checklist · client-relationship-cadence · content-agent · content-factory · definitive-article-writer
The full run order is on the skill-pack directory. Every skill here is one task from the Task Library — the library is the catalogue of what can be done; a pack is the subset you install; an agent is who runs it.
Where this sits in the system
Context is the information the worker needs. A skill describes how to do a task. A pack groups skills. Work happens when an authorized agent runs a job with the agreed inputs, checks the result, and saves the work record. Installing a skill does not start an agent or create a schedule.
- Context — the verified facts, goals, evidence, decisions, and working state kept in user-owned Markdown, Obsidian, Drive, or Git so any authorized model can pick up the same work.
- Skill — the written method for a task; use the linked Task Library (our directory of tasks and recipes) for its current tasks and state.
- PackYOU ARE HERE — a selection of skills; follow the current installation guide for setup and access.
- Agent — the AI worker carrying out an authorized task with the required access and tools.
- Job — an authorized execution with a trigger, inputs, checks, and a saved result; recurring work also needs an actual configured schedule.
- Proof — a written meta article (a record of one task run) for every execution, including partial or failed runs; public release follows recorded authority, and verified lessons improve the recipe.
The map: The System · every asset: Asset Tracker · next door: Asset Tracker.
