Cut What Was Not Earned
Four repos, one habit: an artifact has to earn its claim or it gets cut. The day was mostly the cutting.
Four repos on one day and the same answer all of them: either the artifact earns its place on the page, or the page gets rewritten. The day was mostly the rewriting.
diagnostic-pro: copy that earns what it claims
The hero said “Get a 2,000+ word report” and all three meta descriptions said the same thing. Length reads as homework to someone holding a repair quote they cannot evaluate, so the customer wants to know whether to say yes, not how many paragraphs the answer comes in. Cut it. The copy now leads with the answer: the most likely cause with a confidence number, what a fair price looks like, and the questions to ask at the counter. $4.99 stays as the only figure. The persona’s Number Anchor rule is the reason it stays alone, and “37 Categories” did not replace it because the count does not reconcile cleanly from the frontend (six or fifteen entries depending how you count them, while the 37 figure refers to the backend universal_equipment_registry taxonomy, a different set). A number I cannot verify does not get on the landing page. Verified: npm run build succeeds in 19.84s, grep for any surviving word-count promise across src and index.html returns clean, and the case-insensitive sweep caught a “2,000+ Words” stat badge in the three-column hero row that the first case-sensitive pass had missed.
The persona’s positioning angle, “the Walk-In Playbook”, earns its keep here. Every other tool tells you what is probably wrong; we also tell you what to say at the counter. That is the differentiator and it needs no figure to be true. The price ratio that used to anchor the closing line is demoted because competitive search showed quote-auditing is the most crowded claim in the category (Service Buddy, Mechanic On Tap, MECH AI all sell it), and price is copyable while mechanism is not. Every post becomes one rule: take a section and show it.
The rename of the report’s customer-facing headings was the same habit on a different artifact. “4. SHOP INTERROGATION, 5 technical questions to expose incompetence” and “7. RIPOFF DETECTION, parts cannon indicators” were inside the shipped product, both caught by independent review of the brand work. The enemy is information asymmetry, not tradespeople, and shop owners are themselves customers. One screenshot of “expose incompetence” does real damage. Renamed every heading the customer sees (the cover, the section names, the sub-line copy), kept every internal key the same, because the JSON contract is persisted in the analyses table and renaming those keys would orphan every historical report. The headings are the parsing contract between the prompt and the renderers, so prompt, parser, and both PDF generators are changed atomically here. Splitting them would break report rendering. Section 5 already coaches the customer to “frame questions as curiosity not accusations”, so sections 4 and 7 were the outliers, not the standard.
The brand work that day: a positioning document built with the Vibe Marketing Skills suite, a persona warmer that turned the Guide archetype into the Friend Who Happens to Be a Mechanic (warm AND direct, contractions always, exclamation points allowed but rare and never in a selling register), and a sixth signature pattern called The Reassure, the rule that names the confusing thing and tells the reader it is normal not to know it. The banned-word lists and the no-invented-numbers rule were deliberately not touched, because friendliness and honesty are separate dials and only one was turned. Warmth is not softness, and a voice with no conviction has no standing to tell someone not to authorize $1,850 of work. Friendliness without honesty is the most likely direction for hype to re-enter, and the voice profile now says so explicitly. The seventh pattern, The Backbone, gained four spine ON/OFF pairs (angry mechanic, “this is just ChatGPT”, the shop turned out to be right, a refund demand over a true number) so warm has a counterweight.
diagnostic-pro: rate limits that were one giant bucket
The non-payment smoke script refused to run against production, and the reason it refused was a separate bug it caught. index.js set no trust proxy, so behind Caddy Express resolved req.ip to the proxy’s own address, and every express-rate-limit bucket collapsed into a single global counter. submissionLimiter was 10 submissions per minute across the entire site rather than per client. analysisLimiter 5, evidenceLimiter 30, generalLimiter 60. One noisy visitor, or trivially cheap scripted abuse, could deny service to every paying customer at once. Found while writing the smoke script, which is why the script refuses to run against production now.
Chose the integer 1 over true, and the distinction is the security-relevant part. With trust proxy: true Express takes the leftmost X-Forwarded-For entry, which is entirely attacker-supplied, so anyone could send a random header value and mint a fresh rate-limit bucket per request and bypass every limiter. The integer counts one hop in from the right, the value Caddy appended, so a spoofed prefix is ignored. Verified there is exactly one hop: the Caddy block reverse_proxies straight to 127.0.0.1:8089, the container publishes on loopback only, and diagnosticpro.io resolves directly to the VPS with no CDN in front.
Wrote four new tests proving both directions: req.ip resolves to the real client behind one proxy, a spoofed X-Forwarded-For prefix is ignored, one client exhausting its budget does not lock out a different client, and a client cannot escape its own limit by rotating the header. Two documenting guards also live there: one asserting that true WOULD trust the spoofed value, and one reproducing the pre-fix collapse, so a future change in either direction fails loudly. Full backend suite 177/177.
diagnostic-pro: a deploy that had not deployed
test.diagnosticpro.io returned 404 on POST /evidence/:id/document while production returned 400. Root cause was not a bad deploy but no deploy at all. diagnosticpro-backend-test was created by hand on 2026-07-23, with an empty project.config_files label, and appeared in no compose file, so the deploy’s docker compose up -d --build, which only ever knew about backend, could not see it, adopt it, or replace it. It sat pinned to a superseded image id for three weeks while prod moved on. It could never become current. The frontend was already in sync: Caddy’s test block serves the same /srv/diagnosticpro/dist and its @api matcher already included /evidence*.
Made the test backend a compose-managed service with the same image tag as backend and no second build: (both surfaces are provably one build, and the deploy does not pay for a second cold texlive layer), its own diagnosticpro-test-data volume, and env_file: [{path: .env.test, required: false}]. Chose NO profiles: deliberately: the VPS runs a bare up, so a profiled service would never start and would reintroduce the exact drift this fixes. Chose required: false so a missing test env degrades that container instead of failing the compose run and taking the prod deploy with it.
Threaded GIT_SHA build-arg through to Dockerfile ARG to GET /healthz .gitSha, so a probe can compare what was just deployed. Comparing image ids proves only that both backends are equally stale; comparing shas proves they are current. Updated routes.reports.test.js to assert the healthz body in the same commit, otherwise it would ship red.
deploy.yml gained a blocking test-surface-parity job: the test host must report the sha just deployed AND still answer the evidence document route. The absence of any such check is why a backend rotted for three weeks behind a green pipeline. Verified end-to-end before commit: full backend suite 171/171 with the gitSha change, the smoke ran submission created to document attached to listed back to wrong token rejected 401 to deleted 204 to 0 rows remain, compose config validates and resolves exactly two services, and the collision risk was reproduced on this box AND on the VPS, including the case where prod’s own config changes and compose logs Recreate, prod kept its id, stayed up, kept its old config.
A separate CI fix on the same pass: the route-presence step failed on its first real run against a healthy surface. It printed nothing at all before exiting 1, which is the tell: the failure was in read -r code ctype < <(curl ...), not in the case statement it guards. curl -w emits no trailing newline, so read reaches EOF and returns non-zero even though it assigned both variables successfully. Under set -euo pipefail that non-zero return kills the step. The gate reported a regression that did not exist, while the surface it checks was in fact fixed. Chose command substitution plus parameter expansion over || true on the read, because suppressing the exit status would also suppress a genuine read failure; this way there is no ambiguous status to swallow. A check that cries wolf is worse than no check, because the next red run gets ignored.
claude-code-plugins: things that should not exist
The curated skills index was carrying an 11.79 MB compiled macOS x86_64 Mach-O binary, six external-mirror skills that should never have been republished under our name, and a 28.5 MB projection of src/data/ that was byte-identical to the canonical (proven by identical git blob SHAs, not inferred). All three were tracked copies of things whose existence depended on nobody checking.
The binary was a NUL-byte sniff rather than an extension denylist, so genuinely empty text files stay mirrorable (skill-creator/scripts/init.py and agency-os/state/.gitkeep are both 0 bytes and must survive) and future blobs are caught by shape not by someone remembering to add a pattern. skills/.curated/ exists so skills.sh can INDEX skill text, and it does not crawl plugins/**/skills/, so a compiled macOS x86_64 Mach-O is not indexable; mirroring it only inflated the tracked payload. The source plugin is untouched and still ships the binary to real users; only the text index drops it.
The six mirrored skills were caught by walking every ancestor of a skill path looking for .source.json, not by special-casing .codex and .claude, because the rule is any ancestor owns the marker, not these two directory names. The rule is what matters, so the next vendoring convention cannot slip through. _plugin_root() splits on /skills/, so a skill vendored at plugins/<cat>/<plugin>/.codex/skills/<name> resolved to <plugin>/.codex, one level BELOW where .source.json lives, and the marker was missed. The six removed skills were drive-automation-session, hermes-tweet, mnemos, pr-to-spec, run-automation-suite, run-interactive-cli-session. 1921 -> 1915. The 0.3% shrink is far above the 0.5 SHRINK_FLOOR_RATIO, so no --allow-shrink was needed. A latent loop-termination defect was caught by a test that would hang before the fix: Path('/').parent == Path('/'), so while node != Path(".") never terminated on an absolute path. It was unreachable only because the sole call site happens to pass a repo-relative path.
The projection had a sister commit that moved build-hf-dataset.py to read canonical src/data instead of public/data. The independent review of the untrack-PR caught the original proof table as false: build-hf-dataset.py:33 read the projection, and its own docstring lists it as an input. Reading the canonical file is strictly more correct under the same PR’s own thesis (one fact, one claimant), matches the sibling INDEX line which already reads src/data, and removes the fresh-clone failure the untracking would otherwise introduce for that operator tool. It is invoked by no workflow and no npm script, so CI was never at risk; the defect was the false claim, not a broken build. The gate’s git ls-files -- <glob> resolved relative to the CWD, so running it from a subdirectory reported OK while the projection was tracked. Chose :(top) over resolving paths in JS because it is git’s own root-anchoring primitive.
Also cut the stray auto-generated plugins/ai-agency/tonone/.claude-plugin/CLAUDE.md (319 bytes of <claude-mem-context> activity-table exhaust, not plugin guidance, not upstream content), and added the matching exclude in sources.yaml so the deletion survives the documented off-ramp. Sync-external.mjs returns early on curated: true (no clone, no filtering, no orphan prune), so the exclude by itself is inert while frozen, and the delete by itself evaporates the moment the freeze lifts. Chose both because neither works alone here. The delete removes it now precisely because the mirror is frozen; the exclude keeps it gone afterwards. Rejected unfreezing-and-resyncing: tonone carries curated: true because it is locally hardened past upstream, so a sync in that state would revert the hardening, which is the exact loss class the freeze exists to prevent. Also reverted the auto-bumper’s 0.9.21 to 0.9.22 version change on the same PR because publishing packages is outside this sprint’s authority and a mirrored plugin should not get a release for removing upstream’s own session-tool exhaust.
The day’s reverse version: a deliberate filing of master modernization blueprint (727), benchmark (728), and ADR (729) into the single global sequence, plus nine owner-mandated ratification corrections applied as documentation only. Every headline number was re-measured at HEAD with the SHA and the cohort definitions embedded so the correction is self-proving: 962 A/B failing, not 963; 132 A-graded CONFIRMED; 469 has no measured source and every near-miss was excluded rather than substituted. Three totals are now named separately and never merged: SKILL.md 7,433 over 3,680 rows, agent lane 253 over 353 files, terminal marketplace headline 7,687. A corrected number that cannot be re-derived is the same defect wearing a new value. The RTM’s dead ADR citations were repaired in the same arc: four WONT rows now link their real ADR and both prose references name the actual filing pattern. None of the four links resolved before; all four do now.
intent-os: coverage that refuses to invent
B2.2 ownership coverage shipped with a third state the pre-implementation guardian review forced. The first version was about to publish either 0% covered (implying 191 repositories failed a check nobody ran) or 100%-of-zero (implying an achievement nobody earned), because the declaration store is legitimately empty today and the B2.1 validator returns ok:true over an absent store. Both available answers were false, so the projection carries a third state, not_yet_governed, which emits no percentage at all. The schema, not just the code, forbids a ratio whose denominator is zero, and forbids a coverage block while ungoverned. Two new negative fixture witnesses (51 invalid fixtures, up from 50).
The second trap was the denominator itself. The validator’s summary reports registry_records: 0 whenever the store is absent, because it short-circuits before loading the registry. Consuming that field would have published “0 discovered repositories” while 191 sit on disk. The discovered count is therefore read from the registry directory only, and a drill asserts the divergence so a future refactor cannot quietly re-introduce the fabricated number.
Chose composing over collecting: no network, no gh, no curl, no runtime state, no cron, every input is committed, so the page is reproducible from a checkout. Chose importing DL/030 ruling 5’s backup predicate from the B2.1 validator via importlib over re-expressing it here, because a ratified rule with two implementations has one implementation and one bug. Chose extending the existing gated harness over standing up a second proof script in the same directory.
now-lms: routes that route
Two issue templates, not one, because the two report types have different triage paths and different owners. bug_report.yml asks URL, account, browser, blocking. content_error.yml is the higher-stakes one because people are studying for a real Anthropic exam against this content, so a bad answer key teaches the wrong thing. config.yml routes “how do I” questions to WhatsApp and password resets to email, so neither lands in the tracker. Both templates warn against pasting credentials. Verified: both files parse as valid YAML.
the recurring beat
Claude Opus 5 on diagnostic-pro (the brand doc, the report rename, the trust-proxy fix), Claude Sonnet 5 on claude-code-plugins (the untracking arc and the planning filings), Claude Fable 5 on intent-os (the B2.2 composer). Session signal for this date was absent from the analyzer, so the collaboration beat is thinner than usual and that is the finding: most of the work came from operator reads of the code rather than from transcripts.
The day rhymes with two weeks of the same frame: every-fix-failed-in-the-shape-of-the-bug, the-agents-mistakes-were-the-fast-ones, three-copies-of-the-key-none-of-the-passphrase, a-dead-socket-is-not-a-dead-host, the-status-nothing-could-write-to. Either the frame keeps recurring because it is a real defect class, or it is now the first thing I look for, and a day of four repos is exactly the sort of evidence that would look convincing either way. I do not have a way to tell yet.