Fleet Uptime Monitor

Skill packs › Skill

Fleet Uptime Monitor

Probe the BlitzAdmin/LSS fleet for HTTP 5xx outages and draft (never send) an alert on a fleet-wide failure. Use for scheduled fleet uptime checks or when asked “are our sites up / is the fleet down”.

Skill file fleet-uptime-monitor.md · last updated Aug 14, 2026

How to run it. Download any pack from the skill-pack directory, unzip it into your Claude project folder, and this skill is one of the files inside. You do not paste it anywhere — the agent reads it when the job calls for it.

Catch a fleet-wide outage within the run interval instead of by luck. This exists because on 2026-07-21 ~150 client sites returned HTTP 500 and nothing noticed until an unrelated daily job tripped over it. It generalizes the single-site philmershon-uptime-check to the whole fleet.

Hard rules: read-only probing; never send email — drafts only; healthy = ONE line, verbose only when something is actually broken; never write a date/status you didn’t verify.

Step 1 — Build the probe set (union TWO rosters — never trust one)

Read both on the LOCAL Mac via Desktop Commander and probe the union. A single roster is a single point of blindness: on 2026-07-27 the credential cache was found 111 days stale, which had been silently hiding 18 live client sites (incl. piotrzawislak.com, leopohlmann.com, matspohlmann.com) from every uptime probe since they launched.

Roster Path Field Caveat
Credential cache /Users/dennisyu/Documents/Claude/blitzadmin_site_cache.json sites[] where status ∈ (active,installed) Written by refresh-site-cache, which needs an interactive BlitzAdmin browser token — goes stale silently whenever nobody logs in
Publishing targets /Users/dennisyu/Documents/Claude/Projects/Personal Brand Website/Fleet-Publishing-System/targets.json targets[] No token required; this is the roster that keeps moving
  • Report the age of BOTH files in every close-out line — healthy runs included. A roster that stopped updating is itself the incident; it must never be able to rot invisibly again. Flag any file >30 days old explicitly.
  • Never silently fall back. If one roster is missing/unreadable, probe the other and say which one is gone. If both are gone, probe the 3 anchors alone and say so loudly.
  • Probe set = the 3 ALWAYS-anchors plus a random sample of ~15 domains from the union (rotate the sample each run):
  • ALWAYS anchors: localservicespotlight.com, blitzmetrics.com, dennisyu.com (blitzmetrics.com + dennisyu.com sit on different IPs and act as controls — if they’re up but a shared-host cohort is down, it’s a host-level fault, not the whole internet).

Step 2 — Probe real HTTP status (free, local curl — NOT DataForSEO units)

Run on the LOCAL Mac via Desktop Commander start_process (never the sandbox). The status CODE is what matters — the 2026-07-21 fatal returned a fully-rendered HTML body WITH a 500, so “did a body come back” is the wrong test: ` for u in <domains>; do printf “%s ” “$u”; curl -sS -o /dev/null -w “%{http_code}\n” –max-time 20 “https://$u/”; done ` Record domain → front-page status.

Step 2b — Cloak probe: EVERY domain, EVERY run, ungated (never behind a 5xx)

A status code cannot see a cloaker. Probe every domain from Step 2 with a second identity and an impossible-path .xml canary:

` GB=’Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)’ curl -sS -o /dev/null -w “%{http_code} %{size_download}\n” -A “$GB” –max-time 15 “https://$u/” curl -sS -w “\n%{http_code} %{size_download} %{content_type}\n” -A “$GB” –max-time 15 \ “https://$u/blitz-canary-9174-do-not-create.xml” `

The canary MUST return 404. A 200 on a path that has never existed means something is intercepting requests in front of WordPress. Alert on either:

  • canary body contains product/category, manufacturer-site, or product-similar-image, or
  • the Googlebot body is large and its <title> is something the site does not sell (compare Googlebot <title> to the browser <title> — a mismatch is the tell).

Ignore a status split alone: 202/200 with a ~169-byte body and the site’s real title is a CDN bot challenge, not a compromise (two false positives, 2026-07-27).

An EMPTY <title> is not evidence — never score it clean. If either title is "", the discriminator is unusable and the answer is UNRESOLVED, not clean. On a vhost where every PHP route 500s, both bodies are 0 bytes, both titles are "", and "" == "" scores as a match. That is how owenhemsath.com was recorded “clean (titles match, 0 spam markers)” for three days while serving Googlebot a 533 KB spam sitemap (2026-08-01).

Step 2c — robots.txt shape + backdoor path (payload-independent; MANDATORY on the full union)

Run fleet_robots_sweep.py. It exists because the canary and the title fallback both go blind on a vhost whose PHP is dead, and because the spam-marker list is payload-family-specific — the owenhemsath payload carried zero of the three known markers.

Flag on the SHAPE of /robots.txt, never on a payload signature (shape survives a new payload):

  • more than 2 Sitemap: directives (a normal WordPress robots.txt has one), or
  • any query-string sitemap form (/?sitemapindex.xml, /?sitemap531.xml), or
  • any sitemap naming a non-WordPress .php file (/buy.php?sitemap462.xml).

Then probe the advertised path with both identities: an impossible path answering 200 with >50 <loc> is a cloak regardless of which UA sees it.

robots.txt is a static file served by Caddy’s file_server, so it still answers 200 on exactly the vhosts where PHP is dead — it is readable precisely where every other detector fails.

Known backdoor filename: buy.php, with per-site sitemap numbers (462, 602). It is NOT on the 7/31 remediation hunt list (about.php, radio.php, index.php, content.php, lock360.php, admin.php, wp-login.php).

Re-scope whenever you write a new detector. Never carry the old scope number forward — “1 of 137” was reported truthfully every 3 hours while the real figure was 3 of 137.

Why this is Step 2b and not part of Step 3. It was written on 2026-07-27 as a Step-3 addition — gated behind “non-healthy” — and that gate made it dead code. On 2026-07-29 the three infected sites had their HTTP 500s repaired while the injector was left running. They began returning 200 to humans, so they never reached Step 3 again, and this monitor reported them healthy every 3 hours for a day and a half while site:philmershon.com filled with counterfeit-goods listings. The 500 was the disguise; removing the 500 removed the only signal a status-code monitor could see. A check that can only fire on an already-broken site cannot catch a cloaker on a working one — see the standing rule a check that cannot fail is not a check.

Verification after any claimed cleanup is this one command, not the homepage: the canary must 404.

Force-verify list — probe these every run regardless of sampling: philmershon.com, dylan-haugen.com, theathletespotlight.com. These are the 2026-07-27 infection cohort. They stay on this list after they clear, because a clear does not mean safe: this injector reinfects. dylan-haugen.com was verified canary-404 at 2026-07-31T23:35Z and was reinfected by 2026-08-01 ~04:11Z — the cleanup held about 4.5 hours. philmershon.com cleared 7/31T23:35Z, then threw a 500 across every PHP route within ~95 minutes, then recovered again by 08-01T04:40Z. Do not read a green result on any of these three as closed; read it as clean at the timestamp of that probe. Removal from this list requires a human decision after the root cause and persistence mechanism are identified on-box — not a passing canary.

Step 2d — web shell markers on every body, ungated (structural; fleet_shell_sweep.py)

Run fleet_shell_sweep.py. fleet_probe.probe() also scores every body it fetches, anywhere in the system, so this cannot be skipped by forgetting to call it.

Flag on the controls a file manager needs in order to BE a file manager — ≥2 distinct of: function picked(, name="getupload", name="get_url", name="dpath", Current Path :, /wp-sites/live/. Exactly one marker is PARTIAL — a trigger to LOOK, never a verdict.

Never infer identity from a byte count. azuifeachor.com’s shell was prepended in front of the real 251,647-byte homepage; the 00:45Z run compared 251,647 → 9,968 and concluded the big number was the clean page and the shell had just arrived. Both bodies carried all six controls. Read bodies.

A negative-control fixture must come from a different, known-clean subject. Using the site under investigation as the “clean” side is a check that cannot fail — that is how the infected 251,647-byte capture was nearly enshrined as the definition of clean.

Layer discriminators (run all three; two must PASS): a static file served by Caddy’s file_server (robots.txt, a theme/core asset) — clean means the injection is not at the vhost layer; /wp-cron.php — clean means it is not an INI auto_prepend_file; /wp-json/ — poisoned means it is inside the WordPress bootstrap, and means the REST API is emitting invalid JSON to every integration. A cached poisoned copy on disk (WP Rocket’s cached@<epoch> footer decodes to the generation time) dates the compromise and must be purged after removal, never before.

Step 3 — Deep-probe anything non-healthy

Healthy = 200, or 301/302 to the same site. For any domain returning 4xx/5xx (or a timeout), deep-probe to classify the layer. Always probe all six — the two CONTROLS are what make the diagnosis, and skipping them produced a wrong call on 2026-07-27:

Probe Why
/ the symptom
/wp-json/ and /wp-login.php main-query vs. non-main-query routes
a static asset (theme CSS or /wp-content/uploads/) is Caddy/the web server alive
/wp-cron.php — CONTROL runs wp-load.php, which loads every active plugin + the theme’s functions.php. If anything fataled at load, this 500s too. 200 here disproves “WordPress/PHP is fataling.”
a deliberately nonexistent URL — CONTROL must return 404. A nonexistent URL has no content to render and no template to fail in, so a 500 here means the HTTP status pipeline itself is broken, not rendering.

Also dig +short <domain> for the IP, so you can spot a shared-host pattern, and record whether the failing response carries the origin’s own headers (via: Caddy, content-type).

Interpretation:

  • /wp-cron.php 5xx and static asset 200 → WordPress/PHP really is fataling at load (the 2026-07-21 signature: a network-activated plugin hooking parse_request/wp/template_redirect).
  • /wp-cron.php 200 but / and the nonexistent URL both 5xx → NOT a plugin fatal. Every plugin loads fine; the request dies during PHP shutdown / response completion, after content is generated. wp-cron.php escapes it by die()ing early under DOING_CRON. Suspects, in order: a stale persistent object-cache drop-in (wp-content/object-cache.php) pointing at a dead Redis/Memcached; PHP-FPM segfaulting at shutdown (grep -iE 'SIGSEGV|signal 11'); a fatal in a registered shutdown hook; index.php left chattr +i by a prior incident response (check lsattra +i file cannot be overwritten even as root, so a “we restored it” step fails silently).
  • Failing response has no via: Caddy / no content-type while healthy responses on the same host do, and HEAD returns 200 while GET returns 4xx → an edge/proxy layer in front of the origin is blocking; the request never reaches the site. WAF / bot-mitigation / firewall rule. Fix is in the host or CDN panel — there is nothing to fix inside WordPress.
  • Everything including the static asset fails → server/network-level (host or DNS), not application. If it’s one or a few sites, resolve the A-record first: an off-fleet IP means that domain isn’t pointed at us (see the NET-by-breadth learning below).
  • Only /wp-json/ or a store path failing → partial breakage, still urgent.

Never report a site healthy on the front page alone. A 500 can return a fully-rendered HTML body — philmershon.com looked perfect in a browser for five days while every crawler and integration saw a 500. Status code is the signal; the nonexistent-URL 404 is the confirmation.

Step 3b — Re-probe the open watchlist, every run

Read /Users/dennisyu/Documents/Claude/Projects/Personal Brand Website/fleet-watchlist.json (create it if absent: {"open": []}). Every domain in open[] gets the full Step 3 deep probe on every run, regardless of sampling — these are known-broken sites with a human on the hook.

  • Still broken → one line per site in the close-out with its day-count. Do not re-draft the same alert; escalate the day-count instead.
  • Recovered (front page 200/301 and nonexistent URL 404 and, for anything with a cloak signature, the Step-2b canary returning 404) → say so explicitly, note how long it was down, and remove it from open[].
  • Never clear a cloak entry on uptime alone. philmershon.com, dylan-haugen.com and theathletespotlight.com came back to 200 on 2026-07-29 with the injector still live; a status-only recovery test would have closed all three. They stay on the watchlist until the canary 404s.
  • Any domain that triggers Step 5 gets added to open[] with {"domain", "since", "signature", "owner_thread"}.

This is the point of the watchlist: recovery gets reported automatically, so nobody has to chase status. Boomerang is then only chasing human answers, not “is it back up yet.”

Step 4 — Severity

  • All healthy → Step 6 (one line).
  • 1 site 5xx → single-site alert.
  • ≥3 sites 5xx, OR a shared-IP cohort down while a co-hosted/control anchor is 200FLEET-WIDE OUTAGE (the 2026-07-21 pattern). Escalate: expand the sample to ~30 domains to estimate blast radius.
  • Any Step-2b cloak hit → P1, outranks every uptime finding, even if every site returns 200. A cloaked site is worse than a down site: it damages the client’s index while looking fine to them. Sweep the full union roster (not the sample) with the canary to establish scope, capture the canary body + both homepage bodies + headers as evidence, and escalate to the web team. Never attempt to clean it over REST — the injector sits in front of PHP, an application password cannot reach it, and poking it destroys evidence.

Step 5 — On any failure: draft + record (NEVER send)

Never overwrite a prior run’s evidence, and never stage a second alert for the same day. (Hardened 2026-08-02 after the 2026-07-31 collision: two same-day runs of this every-3h task overwrote each other’s incident file and staged two contradictory drafts in one inbox — the exact “cries wolf” failure this monitor exists to prevent. §1 and §2 below are the fix.)

  1. Write an incident file named INCIDENT-fleet-<YYYY-MM-DD>-<HHMM>Z.mdappend the UTC time of THIS run to the filename; the timestamp is mandatory because this task runs every 3h and a date-only name silently overwrites the earlier run. Put it in /Users/dennisyu/Documents/Claude/Projects/Personal Brand Website/ in the style of INCIDENT-fleet-500-2026-07-21.md: the down/healthy table, the layer diagnosis, the shared-IP finding, likely cause, and the next human step (log into wp-admin and bisect network-activated plugins; or read wp-content/debug.log). Before writing, glob for any earlier INCIDENT-fleet-<same-date>*.md; if one exists, READ it and carry its still-open findings forward into this file (merge, never drop) so a later run can never erase an earlier run’s discovery.
  2. Stage the Gmail alert as a DRAFT to the owner address configured for this agent (connector create_draft, never send; never a hardcoded address — this skill ships in downloadable packs). First list existing drafts (list_drafts, query subject:"Fleet uptime" newer_than:1d). If a fleet-uptime draft already exists for today, UPDATE that one draft to supersede it (update_draft) — do NOT create a second. Exactly one fleet-uptime draft per day, always the current merged picture; two contradictory drafts in one inbox is the failure mode. Subject ⚠️ Fleet uptime — <N> sites <code>/cloak (<date>); body = the domain→status→layer table, the one-line diagnosis, blast-radius estimate, and the note that download zips under /wp-content/uploads/ may still serve even while pages are dark (QR-to-zip unaffected; QR-to-page dark).
  3. Report the single draft id and the incident filename. There must be exactly one of each per day; if you had to merge an earlier incident or update an earlier draft, say so in the close-out.

Step 6 — Healthy close-out (the usual case)

One line only, and it MUST carry both roster ages so a frozen roster can’t hide behind a green result:

Fleet healthy — 18/18 probed 200/301, 18/18 canary 404, shell 0/<N> read <R>/<N> (anchors + 15 of <N> union), checked <time>. Rosters: cache <D>d, targets <D>d.

The canary count is not optional in this line. If it is missing, the run did not check for cloaking and the green result only covers uptime — say that explicitly rather than printing a bare “healthy”.

Neither is the shell count, and it must carry its read coverage as a fraction. A scope number is a property of the instrument as much as of the world: report shell 0/137 (read 128/137), never a bare 0. Domains that were never read are not clean — 2026-08-02 printed “clean 136” while 77 were never fetched. Say which detector produced each number.

Append ⚠️ <roster> <D>d stale for any roster over 30 days. Nothing longer. Do not draft anything — a stale roster is reported here and alerted by refresh-site-cache itself; drafting every 3h would just train you to ignore it.


Registered as a Cowork skill 2026-07-25 (fleet-uptime-monitor) and wired to the fleet-uptime-monitor scheduled task (every 3h). Generalizes philmershon-uptime-check; both should call this one rather than re-deriving the probe logic.

Updated 2026-07-27: probe set now unions the credential cache with targets.json, and every close-out reports both roster ages. Cause — a healthy 18/18 run was passing while the credential cache sat 111 days stale, so 18 live client sites had never once been probed. Green results are only trustworthy if you also know the list was current.

Updated 2026-07-30: Step 2b (cloak probe) is now ungated and runs on every domain every run, and the healthy close-out must report a canary count alongside the status count. Cause — the cloak check written on 2026-07-27 was placed inside Step 3, which only runs on non-healthy sites. When the three infected sites’ HTTP 500s were repaired on 7/29 the injector was left running, so they returned 200, skipped Step 3, and this monitor called them healthy every 3 hours. The 09:52 run on 7/30 went further and cleared two of them from the watchlist as RECOVERED on a status-only test. Step 3b now forbids clearing a cloak entry on anything but a 404 canary.

Updated 2026-07-28: /wp-cron.php and a nonexistent-URL-expects-404 are now MANDATORY control probes in Step 3, and the interpretation table branches on them. Cause — this monitor diagnosed philmershon.com and dylan-haugen.com as a plugin-load fatal; /wp-cron.php returning 200 disproved that, and the real fault was shutdown-phase. Added Step 3b: an explicit watchlist (fleet-watchlist.json) of open incidents that is deep-probed on every run, so recovery is reported automatically rather than chased by email.

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-06-a-check-that-silently-stops-applying –> August 6, 2026 (from: fleet-uptime-monitor 04:04Z scheduled run)

The fourth failure mode: a check that silently STOPS APPLYING to a subject it already matched — and it hides inside a number that goes DOWN

This file already carries three members of the family, all about a detector being wrong about the world: a check that cannot fail (2026-07-29), a check that cannot pass (2026-08-01), a check that cannot run (2026-08-05). This run paid for the fourth, and it is the hardest to see, because the other three produce a wrong answer while this one produces a smaller one — and a count that falls reads as remediation.

What happened. fleet_parked_sweep.py shipped at 02:10Z the same day, flagging coreyjacobs.com as PARKED. Three hours later the same detector, on the same fleet, printed PARKED/STUB: 3 of 174 against Section A’s 4. Nothing had been fixed. coreyjacobs.com answered 308 / 64B instead of 200 / 114B on one request, and the phase-1 screen read:

`python if r[“code”].startswith(“2”) and 0 < sz < SCREEN_MAX_BYTES: `

A 308 is not a 2xx. The row was counted in screened, produced no candidate, and entered no residual bucket — it left the measurement without leaving a trace. A hand probe ninety seconds later returned 200/114B with /lander carrying LANDER_SYSTEM, _trfd, ap:"parking": unchanged, still parked. The domain has four anycast A records on GoDaddy’s parking edge and does not answer identically every request. The detector was reading the weather, not the site.

Three defects composed into one wrong number, each already written down here:

  1. screened was never decomposed into candidates + excluded and nothing asserted the

sum (2026-08-03, assert that the buckets add up). Every printed line stayed true while a member disappeared.

  1. A status code was doing a body’s job. probe() already scores parked_verdict on

every body, ungated — and read_home() dropped the field, so the only consumer re-derived a weaker signal beside the engine. Third appearance of a guard that lives in the engine does not protect the code written beside it.

  1. The excluded shape was not exotic; **a redirect is a parking edge’s own normal

behaviour. The rule budget for the new detector’s first false result on the day you ship it held — but it arrived as a false negative**, and a false positive is loud while a false negative is a number nobody argues with.

The rule. A detector is not finished when it fires. It is finished when the set of things it can stop firing on is bounded and reported. Any predicate that filters a population must publish its residual bucket with a reason per entry, and assert the partition, or the day it starts excluding a real member is the day the count quietly becomes a lie. Corollary for readers: a scope number that falls is a claim requiring the same proof as a scope number that rises — membership diff, not delta.

The residual bucket repaid its cost in forty minutes. The moment the fix started printing excluded-but-near-threshold rows, six domains appeared just over the 3 KB screen; four were the same page — SeedProd coming-soon splashes serving 200 on every path with an empty <title>. That is a third shape of “a 200 that means the client cannot see their site” (after the off-site 301 and registrar parking). Verified not a cloak: byte-identical to browser and Googlebot, canary 200 with 0 <loc> and 0 spam markers, on-fleet IP. It is our own deliberate pre-launch state, so it was closed with a detector and put in an INFO tier that is explicitly NOT wired into is_dark()a number that moves the healthy count must mean something is WRONG; this one means something is PLANNED. Its scope this run prints NOT MEASURED, not 0, because the screen input predates it.

And the fourth consecutive run in which the verifier inherited the defect it was written to catch. The close-out check recomputes every published number from the raw JSONs. It reported robots BEACON_ONLY got=0 claimed=1 * MISMATCH * — on a site with a live attacker-planted beacon. The key is robots_beacon_only; the checker looked up beacon_only / BEACON_ONLY through a tolerant fallback chain that returned None, or [] made it a list, and len([]) == 0 was reported as a measurement. Believing it would have rewritten a true finding to zero in the name of rigour. Same shape as the 2026-07-27 header-case bug and the 2026-08-01 parse default: a lookup miss returning a plausible zero instead of an exception. After 08-02 keep_body, 08-02’s positive control, and 08-06 Section A’s missing PARKED column, the pattern is stable enough to state:

**The code you write to check a run is written under the same assumptions as the run, and
is therefore the likeliest place for the run’s own defect to reappear. Give every verifier
a control that must fire, and make every lookup it performs fail loudly.**

Receipts: INCIDENT-fleet-2026-08-06.md Section B (appended; Section A verified byte-identical, md5 bf5331a4…, before and after); is_candidate() extracted with the engine verdict as branch 1 and the shape screen deliberately status-blind as branch 2; screen() residual bucket + partition assertion exiting 4 INADMISSIBLE; fleet_parked.coming_soon_verdict() + pin_coming_soon() + 2 real captures from two different subjects (negative controls include a GoDaddy lander, so the two classes are proven not to collapse); test_fleet_parked_screen.py 21 assertions reproducing the old predicate verbatim and asserting the two disagree; test_fleet_coming_soon.py 35 assertions, every positive paired with a mutation; 252 offline assertions green across 8 suites; evidence-2026-08-06/0445utc/ including the pre-fix parked scope for diffing; draft r-6883944337977481475 updated (never duplicated, never sent) and verified by listing the mailbox before being written down. Learned August 6, 2026.

<!– learning:2026-08-08-the-predicate-not-the-stage-and-not-the-copy –> August 8, 2026 (from: fleet-uptime-monitor 13:05Z scheduled run)

“Fix the predicate, not the stage” was written into this system ten hours earlier. The fix went into the predicate — and the file sitting next to it kept its own copy.

This file already carries four members of the engine-guard family: keep_body (08-02), the positive control (08-02), the missing PARKED column (08-06), and the residual bucket (08-06). This run paid for the fifth, and it is the cheapest one to have prevented, because the correct predicate had been written that same day, in the same directory, with the lesson attached to it.

What happened. The end-of-run re-probe printed three rows:

` coreyjacobs.com 308 64 read shell=clean parked=clean parkernathans.com 500 3454 read shell=clean parked=clean roofinglaunch.co 301 66 read shell=clean parked=clean `

coreyjacobs.com had been scored PARKED by the parked sweep forty minutes earlier in the same run. parkernathans.com is the hostile redirect gateway. roofinglaunch.co is a known dead-endpoint redirect. All three read as healthy. fleet_end_reprobe.one() had

`python read = not (r[“code”] in (“000″,”PARSE_FAIL”) or not body.strip()) `

which admits every 3xx and every 5xx. fleet_parked.read_ok() — written at 03:04Z the same day, for this exact bug one stage upstream, with the lesson recorded verbatim as “coreyjacobs.com, SECOND time. The 08-06 fix was applied to the SCREEN; the same domain fell through the same crack at the VERDICT one stage later. Fix the PREDICATE, not the stage.” — already handles all of it. Third strike on the same domain.

Rules.

  1. **”Fix the predicate, not the stage” has a second half: and then delete every other copy

of it.** A predicate extracted into a canonical function is only canonical once nothing else restates it. Extraction without a grep for the old shape leaves the bug live in however many files already had it, and those files are exactly the verifiers — the ones whose output nobody re-checks. Ship the extraction and the call-site sweep as one change, and pin it with a test that reads the source and asserts the restatement is not executable anywhere.

  1. **The new detector’s first false result arrives on the day you ship it — including when

the “detector” is a bug fix. Following the redirect hop, correct in itself, made archiepadley.com print 200 / 465,573B / clean: dennisyu.com’s homepage, filed under archiepadley’s name.* That is *a negative control drawn from the subject (2026-08-02) inverted — a measurement attributed to the wrong subject. A row is not just a verdict, it is a verdict about someone. Any probe that can leave the domain must carry the subject it actually measured, and say so loudly when they differ.

  1. The harness reproduces the harness’s own lessons, immediately. Three defects appeared

while writing the 77-assertion suite, each already documented here: the first mutant run crashed rather than failing, skipping every later assertion (a test that crashes is ABSENT, not failing — 08-08 03:04Z, reproduced within eleven hours); one assertion went red because the stub hardcoded the verdict rather than computing it, a fixture that cannot fail, and the subject under test was fine; and a hand-written fixture collapsed two classes the system deliberately keeps apart (PARKED vs STUB_REDIRECT), fixed by using the real captured 114-byte and 709-byte bodies. Budget for the harness costing as much care as the thing it checks, and always run the mutant to completion — a mutant that dies at assertion 3 has told you nothing about assertions 4 through 77.

  1. A count that does not move is worth the same proof as one that does. Every figure this

run was identical to the 10:06Z run except config-leak read coverage (159 → 158/174). The membership diff — 0 new, 0 was-leaking-now-unreadable — is what makes “unchanged” different from “we stopped being able to see it”.

  1. Verify escalation against the mailbox, with a control on the query itself.

subject:"Fleet uptime" newer_than:7d returned empty. Before believing it, newer_than:14d unfiltered returned 27 drafts, proving the query path works — otherwise an empty result is indistinguishable from the lookup miss that returned a plausible zero on 08-06. The 08-06 draft ID recorded in the watchlist is not in the mailbox, and whether it was sent or deleted cannot be read off a draft listing — so it was recorded as undetermined rather than resolved in whichever direction was more comfortable.

Receipts: INCIDENT-fleet-2026-08-08-1006Z.md Section C (appended; Sections A+B verified md5 9db6033282716ac8ccb9b56295be4878 before and after); fleet_end_reprobe.py imports read_ok, follows one hop, reports not_a_read / measured_subject / offsite, and renames its local bool to read_path_ok so the canonical predicate cannot be shadowed; test_fleet_end_reprobe.py new, 77 offline assertions, no network, sub-second, old predicate reproduced verbatim and asserted to disagree, mutant fails 21 of 77 and runs to completion; 11 offline suites green; evidence-2026-08-08/1400utc/; watchlist keys detector_repairs_2026_08_08_1405Z, hand_resolved_2026_08_08_1400Z, escalation_integrity_2026_08_08_1405Z. No draft staged — nothing changed, and the mailbox was checked rather than assumed. Learned August 8, 2026.

<!– learning:2026-08-08-a-bucket-named-coverage-loss-is-where-outages-hide –> August 8, 2026 (from: fleet-uptime-monitor 22:04Z scheduled run)

A bucket named “coverage loss” is where a client-visible outage goes to be ignored. And the monitor read a six-hour-old number as its own.

The finding. chuckthokey.com returned 000 to every HTTPS probe and the sweep filed it cloak-degraded. That is the honest label for we could not measure this, and it is exactly wrong here:

` A chuckthokey.com -> 34.199.192.119 (OUR FLEET IP) NS ns-.awsdns- (Route53) :80 GET / -> 200 67,894B <title>Chuck Thokey</title> Via: 0.0 Caddy :443 GET / -> 000 0B TCP CONNECTS, then alert 80 (internal_error), no cert offered CONTROL, same IP, same second: SNI=dennisyu.com -> CN=dennisyu.com, Let’s Encrypt, verify 0 `

A fully built client site, on our own infrastructure, 100% invisible over HTTPS because Caddy has no certificate for the hostname. It was a GoDaddy parking lander on 08-06, so it was cut over to our fleet since and its TLS was never provisioned — an onboarding gap, and ours to fix in minutes.

This is the fourth shape of a status code cannot tell you whether a client can see their site — after the off-site 301 (archiepadley.com), the dead-endpoint redirect (roofinglaunch.co) and registrar parking. The first three answer with a status code. This one answers with none, and that is worse, because 000 already had a bucket and the bucket sounded like a limitation of the instrument rather than a fact about the world.

Rules.

  1. Decompose every “could not measure” bucket, or it becomes a hiding place. Unread /

unreachable / degraded are honest labels for a mixture, and a mixture reported as one number is a place where the worst member is invisible. Twenty-one domains sat in this fleet’s dark bucket; decomposing them cost one detector and returned exactly one live outage we own and twenty that are off-fleet, unregistered, or have no A record. The difference between “25 unreadable” and “1 of them is a client’s site and it’s ours” is the entire value of the run.

  1. 000 is not one thing. TCP refused, TLS refused for this SNI, no A record,

NXDOMAIN, and a healthy site behind a missing certificate all produce it. Distinguishing them needs three cheap observations the sweep already had the right to make: resolve the name, handshake with SNI against a control on the same IP, and try :80.

  1. **A control on the same IP separates “our host is broken” from “this vhost is broken”

by construction.** One s_client with -servername dennisyu.com against the same address turned a symptom into a diagnosis and named the owner. Without it the finding is “a site is down”; with it, it is “Caddy needs a certificate for this hostname”.

  1. A domain’s STATE can change without any roster noticing. chuckthokey.com moved

from a registrar parking page to our fleet between two runs, and no roster event marked it. Re-scope on a change in the WORLD (2026-08-05) now has a companion: re-classify a domain whose observations change shape, even when its roster membership does not.

And the monitor read a six-hour-old number as its own. At 22:18Z, while the robots sweep was still on pass 2, this run’s monitoring read .run0808c_shell.log:

` SHELL : 0 [] clean : 150 read coverage 150/174 | elapsed 361.4s `

Complete, plausible, three columns — and dated 16:16:35Z. The 16:04Z run had used the same .run0808c_ prefix; six of its logs sat at the exact paths this run would later write, and the shell sweep had not started. Caught only because a 361-second sweep cannot finish inside a stage that has not begun.

This is the fix that went into the incident file on 2026-08-02 — “append the UTC time of THIS run to the filename; a date-only name silently overwrites the earlier run” — and never reached the run logs. Fix the predicate, not the stage, and then delete every other copy of it (08-08 13:05Z), one door over, six hours later.

  1. **Every artifact a run writes needs the run’s timestamp in its name, not just the ones

that burned you.** When a rule is written for one file type, grep for every other place the same naming happens. The incident file was hardened and the six sibling logs beside it were not.

  1. The reader-side guard is the one that catches it. A write-time collision check

cannot fire for a stage that has not started — there is nothing to collide with, only an old file. read_current_run_log() rejects any log whose mtime predates the run start, and distinguishes stage has not started from stage produced nothing. Both must raise; neither may return a number.

The new detector shipped with the sixth empty-operand bug, and I had just read the other five. classify() mapped resolver rcode UNKNOWN to NO_A_RECORD, so the first pass reported owenhemsath.com NO_A_RECORD — a domain that has resolved to the fleet IP throughout an 8-day open incident — plus four others. UNKNOWN means dig did not answer: a fact about the instrument, not the zone. Joining empty <title> (08-01), the unreachable except (07-29), unreadable-counted-clean (08-02), lookup-miss-returns-zero (08-06) and partially-read-counted-scored (08-08 19:04Z).

  1. Reading the failure-mode list is not a control against it. Five prior members of

this exact family were read in order to run this task correctly, and the sixth was written an hour later. Only a control is a control. Every .get(key, default) and every “unknown” enum value in a classifier is a candidate: ask what the default means when the lookup FAILED, and if the answer is a verdict, it is a bug.

The detector’s other false positive was self-inflicted request volume, and the guard was free. Run concurrently with the config-leak sweep, the triage scored jeromeadams.com and jackwendt.com DEAD_BOTH_PORTS flagged OURS — while this same run’s sweep, 20 minutes earlier, had measured them 200/344,995B and 403.

  1. **A run holds more than one measurement of the same subject; make the detectors read

each other.* contradicts_sweep() downgrades to UNRESOLVED any dark verdict that disagrees with a status code measured by the same run. It is cheaper than a re-probe and it fires before a false escalation, not after. Exempt the classes that are *expected to disagree — TLS_NOT_PROVISIONED is dark on :443 precisely while :80 answers — and pin that exemption with a test asserting the real finding survives reconciliation, or the guard eats the signal it was built to protect.

Two harness defects, both already in this file. A mutant crashed on a KeyError instead of failing (a test that crashes is ABSENT, not failing), and two assertions in test_fleet_runlog.py were written inside an except branch, so they silently stopped applying in mutant mode — 36 assertions where the normal run had 38. That is the 2026-08-06 check that silently stops applying defect, inside the test written to prevent it. Compare the assertion COUNT between normal and mutant runs; a mutant that runs fewer assertions is not a weaker signal, it is a missing one.

Also this run, and the reason it was not a bare “healthy”: config-leak and delegation are reported NOT MEASURED, never zero. Origin throughput fell ~9× mid-run (dennisyu.com 465,573B in 1.7s at 22:05Z, 14.9s at 23:20Z) while off-fleet controls stayed fast — so, unlike 19:50Z, not a local network fault, and no truncation. Step 3b is mandatory and was queued behind the config sweep, so the sweep was killed (confirmed dead by process table, not assumed) and the deep probe run. When a run cannot do everything, drop the extension and keep the mandatory step — and name what was dropped in the close-out rather than letting a previous run’s figure stand in for it.

No draft was staged: no Gmail connector exists in this session. Recorded plainly, because the alternative is the failure this file has paid for twice. There was no write, so there is no claim — and the chuckthokey.com finding is therefore not escalated, which is said in the close-out rather than buried here.

Receipts: INCIDENT-fleet-2026-08-08-1006Z.md Section F (appended; Sections A–E verified md5 9760a4a848bd30119e7595d1d1522b86 on the first 1,278 lines before and after the write); fleet_runlog.py + test_fleet_runlog.py (38 assertions, mutant fails 8, runs to completion); fleet_dark_triage.py + test_fleet_dark_triage.py (66 assertions, mutant fails 34, runs to completion; positive direction pinned on the real 22:23Z captured bytes so provisioning the certificate cannot disarm it); 14 offline suites green; watchlist keys scope_2026_08_08_2335Z, not_measured_2026_08_08_2335Z, force_verify_2026_08_08_2207Z, detector_repairs_2026_08_08_2335Z, end_of_run_reprobe_2026_08_08_2331Z, hand_resolved_2026_08_08_2335Z, escalation_integrity_2026_08_08_2335Z. Learned August 8, 2026.

<!– learning:2026-08-09-a-green-result-that-cannot-be-otherwise –> August 9, 2026 (from: fleet-uptime-monitor 01:04Z scheduled run)

19 of 174 domains had a cloak verdict that could never have been anything but green — and the detector written to say so shipped disarmed, reporting 0

This file already carries a check that cannot fail (2026-07-29), cannot pass (2026-08-01), cannot run (2026-08-05) and silently stops applying (2026-08-06). This run found the first one at fleet scale, and then reproduced the third one inside the fix for the first, forty minutes apart.

The finding. fleet_robots_sweep printed, truthfully as far as it knew:

` of which parsed a real robots.txt : 127 of which proved NO robots.txt : 23 (definitive negative, not an assumption) `

Only 3 were definitive negatives. The other 20 answered 200 with a full HTML page where /robots.txt should be. chuckthokey.com returned 67,915 bytes byte-identical (md5 cefecf3b620ab1c9e59c12acd9749583) to its own homepage. The origin has no 404: it serves the home page for every unknown path.

That single property disarms both payload-independent cloak detectors at once. The canary can never 404, so a cloak can never be excluded; and the robots detector reads the homepage, finds 0 Sitemap: lines, and scores clean. Both then report green. Measured, not inferred — 20 serial canary probes returned 19 blind and one real 404 (mikekuhn.us, the control proving the probe still discriminates; its robots detector is blind while its canary is armed, because the verdict is per-PATH, not per-domain).

Rules.

  1. **A green verdict is worth exactly as much as its ability to have been red. Publish that

ability as a number. “Canary clean 25/38” says nothing until you also know on how many of those the canary could have failed. Every detector should report an armed count** beside its clean count; a clean result on an unarmed check is not evidence, and it is indistinguishable from evidence in any summary line.

  1. **A “definitive negative” is a claim about the instrument, and it needs the same proof as

a positive.* The engine’s own why string called a real 404 and a soft-404 HTML template by the identical phrase, one layer below the sweep that printed it. *That is where the false claim was born — a wrong number in a report is usually a wrong noun in a library.

  1. **A suite that exercises the predicate on real bytes proves the predicate. It proves

nothing about whether the predicate is ever handed those bytes. The new detector passed 49 assertions and reported 0 of 174 in production, because read_robots() blanks body to "" on every state but parsed, so all 20 catch-alls arrived as empty strings and came back UNREADABLE. Ship the extraction and an end-to-end assertion through the real call path with a stubbed engine, or the detector’s first production run is its first test*. Engine-side data-hiding is invisible to a unit test that constructs its own inputs — the fourth form of *a guard that lives in the engine does not protect the code written beside it.

  1. What caught it was not the tests. It was **re-deriving the scope after the detector

changed (2026-08-02) and holding an independent measurement of the same fact in the same run** (2026-08-08) — 20 by hand against the detector’s 0. Keep both habits even when the detector is new and its suite is green; especially then.

  1. Bounded evidence must travel with its true size. probe_body keeps 8,000 chars and

raw_size keeps the real byte count, and catchall_verdict(size=…) is explicit, so a truncated slice can never make a 92 KB catch-all page look small. A detector that judges a response by the length of the slice it was given is measuring the plumbing.

  1. A partition assertion pays for itself on the code that ships it. The new

parsed + no_robots + catchall + unreadable == n check fired on its first execution: 194 != 174, because the catch-alls sat in catchall and in unreadable. Without it the run publishes UNREADABLE 44 — overstating coverage loss by 20 and hiding the new bucket inside an old one, the exact 2026-08-08 failure. Corrected scope: 127 + 3 + 20 + 24 = 174, and real read coverage 130/174 (75%), not the 150/174 (86%) this monitor had been reporting.

  1. Do not re-probe a stable property as if it were a transient miss. The serial re-probe

exists because a parallel miss is not a measurement. A catch-all is not a miss; it will answer 200 again every time. Excluding it saved 20 requests per run and stopped a stable fact from being laundered through a pass named “retry”.

  1. The mutant crashed instead of failing, again. Adding size= left the stub without it

TypeError inside robots_pass → the suite died at assertion 51 and said nothing about 51–60. A test that crashes is ABSENT, not failing, reproduced within the hour of reading it. And the mutant patched only fleet_softcatchall.catchall_verdict while the sweep had imported the symbol at load, so the end-to-end assertions would have run the real predicate under the mutant and passed — a check that cannot fail, inside the mutant itself. When you patch a function for a mutant, patch every binding that already imported it, and compare assertion counts between modes.

  1. Correct a stale claim in the report, not just in the log. This run’s incident file

said config-leak and delegation were NOT MEASURED — true when written, false eleven minutes later when both finished. A stale sentence in a report is the same defect as a six-hour-old number in a log (2026-08-08); it was rewritten rather than left standing.

Also this run: chuckthokey.com RECOVERED — the certificate found missing at 22:23Z was issued at 23:01:59Z and :443 now serves the real site on all layers; whether a human or Caddy’s on-demand TLS did it is recorded as undetermined rather than claimed. It stays on the watchlist under a different signature, because it is a charter member of the catch-all cohort. All three force-verify domains (philmershon.com, dylan-haugen.com, theathletespotlight.com) returned canary 404 — armed, and clean at this timestamp only. The chain order was changed so the mandatory Step 3b deep probe runs ahead of the optional config-leak and delegation sweeps; on 08-08 it was queued behind them and a sweep had to be killed to reach it. No draft was staged — no Gmail connector exists in this session — so the fleet-wide detector-blindness finding is recorded, not escalated, and that is said in the close-out rather than buried here.

Receipts: INCIDENT-fleet-2026-08-09-0104Z.md (new, Section A, append-only); fleet_softcatchall.py + test_fleet_softcatchall.py (65 offline assertions, mutant fails 29, runs to completion with the same 65); 4 new fixtures with negative controls drawn from different subjects, one deliberately small; fleet_probe.read_robots() returning probe_body/raw_size for every state; fleet_robots_sweep.py new SOFT-404 CATCH-ALL bucket + partition assertion + both mis-bucketing sites fixed; fleet_sweep.py cloak_check_armed + a separate structurally-blind count; 15 offline suites green; watchlist keys softcatchall_scope_2026_08_09_0104Z, detector_repairs_2026_08_09_0104Z, membership_diff_2026_08_09_0104Z, force_verify_2026_08_09_0104Z, escalation_integrity_2026_08_09_0104Z. Learned August 9, 2026.

<!– 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-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:

  1. 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.
  1. 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 slug about-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-an-alarm-a-file-copy-can-switch-off –> August 11, 2026 (from: fleet-uptime-monitor 10:04Z daily run)

An alarm that a file copy can switch off, and it fails in the alarm’s own direction

blitzadmin_site_cache.json had reported 123.2d ⚠️ STALE in every close-out since 2026-07-27. On 2026-08-11 it would have reported 0.1d, and the warning would simply not have appeared. Nothing refreshed it:

` stat birth 2026-08-11T08:38:36Z == mtime, new inode 214067473 <- CREATED this morning inside the file: cached_at 2026-04-07T06:47:47Z <- 126.2 days old membership vs the April 8 backup: 159 sites, 0 added, 0 removed, 0 key changes `

The age check was (now - os.path.getmtime(path)) / 86400 — a measurement of when the container was last written, published as how old the roster is. That check exists because on 2026-07-27 a 111-day-stale cache was found hiding 18 live client sites behind a green result.

The rules.

  1. **Age a data file by its OWN declared timestamp; mtime is corroboration, never the

measurement.** Any roster, cache, baseline or export that a monitor trusts should carry and publish its generation time. Where the two disagree by more than a day the file was copied, restored or touched — report that as its own verdict (MTIME_MASKED_STALE), not as freshness.

  1. A file with no internal timestamp is UNDATED, not fresh. Publish the number *with

its source attached* — 126.2d [content] vs 10.0d [mtime-only] — so a reader can never mistake one kind of age for the other. targets.json has carried no stamp at all for the whole life of this monitor and nobody knew.

  1. Rank failure modes by DIRECTION, not by size. A false alarm is loud and gets fixed.

This one made a number go DOWN and a warning DISAPPEAR: nothing on screen to question, and the report looks cleaner than yesterday’s. Any defect that removes output is worth more scrutiny than one that adds it, because the reader’s natural reaction to a vanished warning is relief.

  1. A lookup that FAILED must never return a number. Missing file, unparseable JSON,

junk in the timestamp field — all UNREADABLE/UNDATED with age = None, never 0. (Sixth member of this family: empty <title> 08-01, unreachable except 07-29, unreadable-counted-clean 08-02, lookup-miss-returns-zero 08-06, UNKNOWNNO_A_RECORD 08-08.)

  1. Read the declared stamp at TOP LEVEL only. A recursive search finds a per-row

last_seen from today and reports the freshest row as the freshness of the file — a measurement attributed to the wrong subject.

A green verdict is only worth its ability to have been red — and that column is what caught the new detector

The same run shipped a new SPLIT-DNS detector and its first production sweep reported ARMED 0 of 16 — not one verdict could have come out red, on a fleet containing its own known positive. It was reading page bodies from the probe engine’s temp file, which the engine reclaims unless asked for keep_body=True, so every response scored “not a read”. Its 83-assertion suite was green and knew nothing, because every section stubbed the step that was broken.

  1. Publish an ARMED count beside every clean count, for every detector. Had this one

printed only HITS 0 it would have closed an owed item with a detector that cannot detect, and the close-out would have been true and worthless. The armed column is not a footnote; it is the difference between evidence and a green pixel.

  1. **A suite that constructs its own inputs proves the predicate and nothing about whether

the predicate is ever handed those bytes.* Every new detector needs at least one assertion that drives real captured bytes through the real call path with only the *transport stubbed. Engine-side data hiding is invisible to any test that builds its own rows. (Third occurrence: read_robots blanking body on 08-09, and this.)

  1. Reading the failure-mode list is not a control against it. Both defects above are

documented in this very file and were read, in order to run the task correctly, roughly ninety minutes before being shipped again. Only a control is a control.

A verifier that counts must first assert what it is counting

The close-out checker recomputes every published number from the raw artifacts. It reported COMING_SOON got=5 claimed=4 * MISMATCH * — the producer was right; the checker had called len() on a dict (pinned/measured/hits/read/total) instead of on its hits list.

  1. The mismatch was luck, and that is the lesson. The dict happens to have five keys.

With four it would have printed got=4 claimed=4 OK — an agreement computed on entirely the wrong object, recorded as verification and never questioned. len() answers for any sized object, and it answers plausibly. Assert the SHAPE before counting, or an agreement is indistinguishable in the report from a correct one.

Smaller, and cheap to avoid

  1. zsh does not word-split unquoted parameter expansions. python3 deep.py $WL

passed a 19-domain list as ONE argv element and the run probed a single nonexistent hostname. It was visible only because the probe refused to score an unresolved host as anything but coverage loss. Use ${=VAR} or an explicit array, and prefer tools that refuse to produce a clean row from a subject they could not resolve.

  1. A redirect is what the visitor gets. Excluding 3xx from a comparison as “not a

read” let two addresses serving a parking stub and two answering a 308 score as AGREEMENT. Reads, hard client-visible failures (TLS/refused) and redirects are all comparable; only a transient 000 is coverage loss, and it must stay excluded so packet loss cannot manufacture a finding.

  1. Keep the defective artifact. The broken sweep’s JSON and log are retained under a

DEFECTIVE name beside the corrected ones. A record that hides its own revision is worth less than one that shows it.

Showing the 7 most recent field lessons of 28. 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 fleet-uptime-monitor.md.


Other skills: ai-search-visibility · boil-the-ocean · client-access-checklist · client-relationship-cadence · content-agent · content-factory

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 what the worker knows. A skill is the method it follows. A pack is a folder of methods. None of them does any work alone. Work happens when a job runs the right skill against the right context on a schedule, checks its output, and leaves a receipt the next worker can inspect.

  1. 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.
  2. Skill — one task, written down to a standard, so an agent can run it without you in the room. There are 239 of them.
  3. PackYOU ARE HERE — those skills bundled into a download you install in one paste.
  4. Agent — a named role with a job description — not a chat window you retype every morning.
  5. Job — a schedule, a QA cycle, and somewhere to keep working files. Miss any of the three and nothing runs twice.
  6. Proof — every finished run written up in public, and the lesson pushed back into the skill.

The map: The System · every asset: Asset Tracker · next door: Asset Tracker.

Scroll to Top