Client Access Checklist

Skill packs › Skill

Client Access ChecklistIN EVERY PACK

The standard set of accounts and permissions we capture before any content ships — Search Console, CMS admin, GA4, Tag Manager, DNS, Google Business Profile, video, social, ads. Use at kickoff on any new client or site, whenever a build is about to publish, and whenever you are asked “do we have access to X”. Turns access from something we remember into something we audit.

Skill file client-access-checklist.md · last updated Aug 2, 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. This one ships in every pack.

Use this when starting any engagement, standing up any site, or about to publish content anywhere. Also use it the moment you catch yourself about to write a “needs the client’s login” list — generate that list from here rather than from memory.

The rule this skill enforces: a build is not done when the content is live. It is done when the content is live and we can see what it does.

Why (read this once, it changes how you work)

We published sixteen articles to a client site on June 14, 2026. Six of them were never indexed by Google. We did not find out for six weeks, because that site had no Search Console property — and it had no Search Console property because our coverage audits took their list of sites from the hosting fleet, and this was a site we publish to but do not host. It was structurally incapable of showing up as a gap.

The same audit found that site had no analytics of any kind. A fleet-wide sweep found 127 more properties in the same condition, 50 of them on our own hosting.

Nobody skipped a step. The step wasn’t on the list. This skill is the list.

The nine rows

Rows 1–4 are the gate. Content does not ship until they are green.

# Access What “green” means
1 Google Search Console Property verified · our operating account added · sitemap submitted, Success
2 CMS / website admin Admin login and an application password in .credentials.json so agents publish without a human
3 Google Analytics 4 Property live, tag firing, internal traffic filtered, linked to GSC
4 Google Tag Manager Container published with a real ID — an empty container is a false green
5 Registrar + DNS Login or a named responsive contact; record where DNS is hosted
6 Google Business Profile Claimed; we hold manager access (this is NOT the same as a personal Knowledge Panel)
7 YouTube / video Channel access or a reliable export path — this is the raw material for repurposing
8 Social profiles Handles confirmed and recorded for sameAs; access where amplification is in scope
9 Ad accounts Business Manager access with the right role, if ads are in scope

Steps

  1. Check the register first. Access-Checklist/ACCESS-REGISTER.json may already have the

property. Then check .credentials.json for an application password before asking anyone for a login — we frequently already have access nobody remembered.

  1. Run the audit for this domain: python3 Access-Checklist/access_audit.py --domain <domain>.

It reports verification evidence, analytics, sitemap, robots reference, and publish access from public HTML in seconds. Do this before asking the client for anything, so you ask only for what is genuinely missing.

  1. Ask once, ask completely. One message listing every missing row with the exact emails and

roles. Ten small asks over six weeks is how access work dies.

  1. Verify Search Console per blitzmetrics.com/verify-client-sites-google-search-console/:

URL-prefix property → copy the HTML-tag token → paste into the site’s head → Verify → submit sitemap.

  1. Add users. The site owner’s Google account = Owner. Your operating/service account = Full.

Your own account = Full. ⚠ The Add-user click is a HUMAN step — agents do not modify access controls. Prepare everything, then hand off. (This file ships in public download packs. The specific role addresses we use are in the internal runbook — Access-Checklist/ACCESS-CHECKLIST.md — never in a distributed skill.)

  1. Install GA4 and GTM before the first article, not after.
  2. Record everything in the register and .credentials.json, then re-run the audit to confirm

the rows went green. Access you did not record is access you do not have.

  1. Route what you cannot get. Blocked is a status, not an ending. Anything an agent cannot do

goes to your ops owner as a written to-do with a FULL spec — not a hint. Host or server work goes to whoever owns the server, in writing. Name the owner and name a date: a blocker with an owner and a date is progress; a blocker in your head is not. (Internal routing targets live in the internal runbook, not in this shipped file.)

Definition of done

  • [ ] Every one of the nine rows has a status and an owner — including the ones we chose to skip
  • [ ] Rows 1–4 green, or a routed blocker naming exactly what we cannot see until it clears
  • [ ] Property present in ACCESS-REGISTER.json; credentials in .credentials.json
  • [ ] access_audit.py --domain <domain> re-run and the gaps it reports match what you expect
  • [ ] Sitemap submitted, and referenced in robots.txt
  • [ ] The client-facing “what we need from you” list was generated from this checklist, not memory

Traps met in the field

  • Rank Math’s updateSettings REST endpoint cannot safely write one setting. It replaces three

whole option groups and will destroy settings. Use a browser REST nonce or the bm-site-verification module. Never inject a verification token through it.

  • Block-theme sites with no SEO plugin have no head-injection route over REST. That is what

GSC-Fleet-Coverage/bm-site-verification.php exists for — built and tested, pending deploy.

  • Some of our own sites 403 Google’s Google-Site-Verification/1.0 fetcher while serving

Googlebot normally. Allowlist the user agent before blaming the token.

  • Flush the sitemap cache before submitting. One site listed 1,059 URLs against 1,447 published

articles; submitting as-is would have hidden 393 articles.

  • An empty GTM container reads as installed. Check for a real GTM-XXXXXX ID, not just the

presence of gtm.js.

  • A missing verification meta tag does not prove there is no Search Console — DNS TXT, GA, and

GTM verification leave no HTML trace. GSC_UNCONFIRMED means check the account.

Related

  • weekly-brand-maa.md — the weekly loop that consumes this access; its indexation check is what

exposed the gap

  • measurement-analytics.md — what to do with the data once you can see it
  • personal-brand-website-agent.md — the build this checklist gates
  • TaskLibrary/skills/digital-plumbing/ — task-level SOPs for each row
  • boil-the-ocean.md — why the answer is the finished access, not a plan to get it

Learned in the field

Appended automatically by the self-improvement loop (Skill-Learnings/): dated lessons from real runs. Newest at the bottom.

<!– learning:2026-07-28-a-shipped-skill-must-not-carry-your-own-address –> July 28, 2026 (from: skill-pack-propagation daily run, July 28, 2026)

A skill that ships must not carry your address, your staff, or your routing

client-access-checklist was mandated into every pack on July 27, 2026 and went live in all seven public downloads. Written for internal use, it told the reader to add access@localservicespotlight.com and 668sierra@gmail.com as Full users on the client’s Search Console, and to route blocked work to a named staff member and an internal team alias.

weekly-brand-maa was worse in effect: it instructed the agent to “always send one combined summary email to Dennis (668sierra@gmail.com).” Every workshop attendee who installed that pack had an agent whose weekly job was to email us about their clients.

That is not only a privacy leak; it is a functional bug. An instruction that names a specific person is correct in exactly one installation and wrong in every other one.

Rules:

  1. **Before a skill is mandated into distributed packs, read it as a stranger who just

downloaded it.* Every “we”, “our account”, named person and internal alias is a defect. Ask: *if a competitor installed this, what did I just hand them, and who would it email?

  1. Addresses, owners and destinations are CONFIGURATION, not content. Say “the owner

address configured for this agent”; keep the actual values in the internal runbook and the credentials file — the two places that never ship.

  1. Grep the built artifact, not the source folder. These files were fine in the folder they

were written for; the defect only exists once the mandate copies them somewhere else. Add the sweep to the run: fetch each LIVE download and search it for your own addresses. That check takes seconds and is the only one that reflects what a stranger actually receives.

  1. Generalising a skill for distribution is part of mandating it, not a follow-up. The

mandate that copies a file into ten packs is the moment its audience changes.

Learned July 28, 2026.

<!– learning:2026-07-28-an-app-password-belongs-to-whoever-minted-it –> July 28, 2026 (from: sigrun-website-request-intake, jagodapasko.com credential handoff, July 28, 2026)

On July 28, 2026 ops handed over a WordPress Application Password in the same message as a username, and the two did not go together — the key had been minted under admin, not under the member address quoted beside it. Authenticating as the named user returned rest_not_logged_in and looked exactly like a bad credential. It was a good credential pointed at the wrong name.

Probe before you diagnose. Run ?rest_route=/wp/v2/users/me&context=edit across every plausible username — the site admin, the member’s address, the account the site was provisioned under — before reporting a key as broken. The response names the real owner and its capabilities in one call.

Two related rules for fleet WordPress work:

  1. permalink_structure cannot be set through the API. It is not in wp/v2/settings, and

authenticated XML-RPC is blocked at the fleet edge (400 on any credentialed call, while unauthenticated system.listMethods returns 200). Pretty permalinks are a wp-admin-only step, so ask for them at provisioning time instead of discovering it after launch — a site left on plain permalinks serves every page as ?page_id=N and drops a typed /about/ on the homepage.

  1. Rapid sequential requests trip the fleet WAF into serving a 4,735-byte stub with HTTP 200

on every URL, which reads as a total outage. Space requests a few seconds apart, send a full Chrome User-Agent, and re-fetch once before believing a bad result.

<!– learning:2026-07-28-the-credential-may-already-be-in-your-inbox –> July 28, 2026 (from: sigrun-website-request-intake, jagodapasko.com, July 28, 2026)

On July 28, 2026 a run reported a member’s site as blocked on a missing WordPress Application Password for the fifth straight day. The member had emailed that exact password sixteen hours earlier, replying on the same thread the DNS instructions went out on. The run had checked the ops Basecamp thread and the credentials file, and stopped there. A finished website sat unpublished for a day because nobody read the member’s own reply.

When a task is blocked on something a person was asked to send, search every channel that person can reply on before declaring the block. Concretely, before reporting any credential as missing: search the mail thread the request went out on, search the member’s address for the last 72 hours, check the ops channel, and check the credentials file. Say which channels were searched in the status report, so the next run can see the gap rather than inherit it.

Two things that followed from checking:

  • The member’s own key authenticated as her administrator account, while the ops-issued

key authenticated as the shared fleet admin. Prefer the member’s own credential for writes to the member’s own site — it keeps authorship on her rather than on the service account, which is the same problem the admin-authorship fleet fix exists to clean up.

  • Two valid keys arrived from two directions within a day because nobody recorded that the

first had landed. Record the credential and its channel the moment it arrives, even when the publish happens later.

<!– learning:2026-07-29-a-plugin-namespace-can-be-blocked-while-wp-v2-is-fine –> July 29, 2026 (from: publishing blitzmetrics.com/persistent-agents/, July 29, 2026)

A plugin’s REST namespace can be blocked while wp/v2 works perfectly

Setting the SEO title and meta description on a new blitzmetrics.com article returned 403 on every POST to /wp-json/rankmath/v1/updateMeta and /updateRedirection — while POSTs to /wp-json/wp/v2/posts/<id> on the same host, same Application Password, same full browser header set, returned 200 all day. Probed both ways, JSON body and form-encoded, GET vs POST: GET /wp-json/rankmath/v1/ is 200, every POST under it is 403, and the body is the host’s own styled 403 page rather than a WordPress JSON error. That is a WAF rule scoped to a namespace, not a broken credential and not a capability problem.

The second trap was worse. RankMath does not register rank_math_title / rank_math_description with show_in_rest, so writing them through wp/v2’s meta field returns HTTP 200 and stores nothing. Reading the post back with context=edit shows meta: {}. A publish job that trusts the 200 reports the SEO as set forever.

Rules:

  1. Diagnose a 403 by namespace and method before blaming the credential. `GET

/wp-json/<ns>/` plus a no-op POST tells you in two calls whether it is auth, capability, or an edge rule. Twelve days were once lost calling a WAF rule a broken app password.

  1. After any meta write, read it back. If the field comes back empty, the write was

accepted and discarded. A 200 is a receipt for a request, not evidence of a change.

  1. Know the fallbacks before you need them. RankMath derives the meta description from the

post excerpt and the SEO title from the post title when its own fields are empty — both writable through plain wp/v2. Setting excerpt produced the exact intended <meta name="description"> in the rendered head. Verify in the head, not the API response.

  1. On WP Engine, purge before you verify. POST /wp-json/wpe/cache-plugin/v1/clear_all_caches.

A page trashed through the API kept returning 200 to an anonymous fetch until the cache was cleared, which reads exactly like a failed delete and invites a second, wrong repair.

Learned July 29, 2026.

<!– learning:2026-07-29-a-blocker-with-a-deadline-becomes-a-todo-on-that-date –> July 29, 2026 (from: sigrun-website-request-intake daily run, July 29, 2026)

A status line repeated daily is not an escalation. Christine Boers-Doets’s site was finished on July 8, 2026 and sat serving the WordPress sample page for twelve days waiting on one WordPress Application Password — four clicks by one person. Across five consecutive daily runs it appeared as a bullet in a status comment on a thread that person reads, and across five consecutive days nothing happened. The member emailed twice asking why her URL did not work; her programme lead escalated on her behalf. Every report was accurate and every report was ignorable, because a bullet in a status update has no owner, no due date and no place it shows up again tomorrow.

The rule: when a run identifies work it cannot do itself, it names the person, writes the exact steps, and sets the date on which a status line becomes a task. On that date it files the task — for us, a Basecamp to-do assigned to the named owner, carrying the full spec (which site, which screen, what to name the key, what to paste back, what happens once it lands). July 28’s run wrote the deadline into the ledger — “if it is not posted by the 7/29 run, escalate it to a to-do” — and July 29’s run fired it. That handoff is the whole mechanism: the run that spots the block sets the trigger, the next run pulls it. Without the written trigger the item just ages inside green reports.

Two details that make the to-do land instead of adding noise. Write the ask as the smallest true unit of work — “four clicks, here they are” beats “unblock Christine” — because an owner estimates before they act. And carry forward what the last identical task taught: on the previous site the key was minted under a different WordPress user than the username quoted beside it, so the to-do asks for the username too.

<!– learning:2026-07-29-read-the-plugin-source-before-writing-to-its-table –> July 29, 2026 (from: collapsing 24 redirect chains across localservicespotlight.com + dennisyu.com, July 29, 2026)

When a plugin’s REST API is undocumented, download the plugin and read it

Two redirect tables needed surgical edits and neither API was documented in a way that answered the only question that mattered: what happens to the fields I do not send? Guessing against a live table with 34 and 192 rules on it was not acceptable, and probing by trial risked detaching real redirects carrying thousands of hits.

So both plugins were downloaded from the wordpress.org repo at the exact installed version and read. Ten minutes, and it turned two unknowns into contracts:

  • RankMath Redirections has no route that LISTS redirections. The read path is

status/exportSettings (redirections ride along in the export). The write path is updateRedirection, which is really the post-metabox save handler: it rebuilds the rule from (redirection_id, url_to, sources, header_code). Omit redirectionSources and the source is rebuilt EMPTY, silently detaching the rule from the URL it exists to catch. It also rejects an empty objectID, which a standalone rule does not have — pass an id matching no post, because a real one makes that post’s metabox claim it owns a redirect.

  • Redirection plugin (redirection/v1/redirect) sanitises the whole payload and then does a

full row UPDATE. A partial patch drops every field you left out. The payload has to be the GET item echoed back with only the target changed — and hits / last_access must be OMITTED, because the sanitiser maps them onto last_count / last_access and rewrites the counters.

  • Its import path is safe but useless for repairs: set_redirections() skips any row whose

source already matches an existing rule, so re-importing corrected copies changes nothing.

Rules:

  1. Read the source before the first write, not after the first surprise. curl the versioned

zip from wordpress.org, unzip, read the sanitiser and the update method. Cheaper than one bad write to a production table.

  1. Find out whether update means PATCH or REPLACE. If the model sanitises into a fresh array

and calls $wpdb->update with it, every field you omit is erased.

  1. Audit every redirect engine a site has, not the first one you find. dennisyu.com runs

RankMath Redirections AND the Redirection plugin at once. X-Redirect-By proved the plugin wins; fixing the RankMath copies changed nothing a visitor could see — 2 of 13 “fixes” held and the rest still measured 3-4 hops. X-Redirect-By on the response is the ground truth for who is actually in charge.

  1. Never auto-rewrite a regex rule. Its source is a pattern, not a URL, so hops cannot be

measured, and its target may contain capture groups. Report it and stop.

  1. A rule pointing at itself is a live infinite loop, not a chain. Detect “never lands after

N hops” as its own class and never auto-repair it — the correct destination is an editorial decision. One such rule had taken 12,190 hits.

  1. Check the trailing slash on high-volume rules. A target of /$1 where WordPress canonical

wants /$1/ costs an extra 301 on every request. On the busiest rule on the site that was 829,576 requests each paying for a hop nobody needed.

Learned July 29, 2026.

<!– learning:2026-07-30-a-credential-that-arrives-is-not-a-credential-that-works –> July 30, 2026 (from: Sigrun website-request intake, July 30, 2026 — Christine Boers-Doets application password)

A credential that ARRIVES is not a credential that WORKS. Christine Boers-Doets’ WordPress Application Password was the single blocker on a finished site for 13 days. The morning it finally came in — pasted by Muzamil in an email alongside Jagoda’s — the reflex was to mark the blocker cleared, store the key, and publish. That would have been wrong for the second time on this same project. The key does not authenticate. Tested against the live .nl install under 15 usernames (the two real users the REST API lists, plus admin, operations, blitzadmin and nine more): every one returns rest_not_logged_in, the same 401 a request with no credentials gets.

The rule: probe a freshly-received credential against the LIVE target before you advance a status, store it, or report the block cleared. And probe it with a check you have PROVEN can both pass and fail in the same breath — a check that cannot fail is not a check. Here the proof was a positive and a negative control run right next to the real test: Jagoda’s key returned 200 on /wp/v2/users/me?context=edit and on /wp/v2/settings, and a deliberately bogus key returned 401. Only against those two poles does Christine’s key returning 401 mean something. Without them, 401 could just as easily have been the whole site rejecting Basic auth, and “her key is bad” would have been an unfounded guess.

The same-batch control is what turned a vague “the credential doesn’t work” into a precise, actionable finding. Jagoda’s key came from the identical email and authenticated cleanly — so it is not the email, not the paste channel, not the fleet, not the WAF. The fault is isolated to this one key or the install it was minted on. That specificity is the difference between an email that says “please look at Christine’s password” (which generates a reply asking what’s wrong) and one that says “re-mint it on the .nl install, SiteId 2360, the christineboers profile” (which gets done).

The most likely cause carries its own reusable lesson: on a domain that was re-mapped between TLDs, the old and new installs are SEPARATE WordPress sites. Christine’s site moved from .com (SiteId 2348) to .nl (SiteId 2360); the .com now resolves to a GoDaddy/Fastly parking page and its old WordPress on our IP no longer serves. An Application Password minted on the old install fails on the new one with the exact same rest_not_logged_in as a wrong password — indistinguishable by status code alone. When a key fails, name which install it was probably minted on rather than reporting “bad credential”; the fix (re-mint on the correct install) is invisible until you do.

One trap worth flagging: /wp/v2/users only lists users who have published posts, so the real service account can be invisible to it. Jagoda’s working key belonged to WP user #1 admin, which the public user list never showed. So when a key fails on the two listed users, try likely service usernames (admin / operations / blitzadmin) before concluding anything — the account it was minted under may not be one the API will name for you. Here all 15 still failed, which is itself the finding, not a reason to stop early.

<!– learning:2026-08-01-a-named-blocker-needs-a-recheck-date –> August 1, 2026 (from: skill-pack-propagation — closing out the agent-runtime cloud migration, August 1, 2026)

A plan that says “blocked on <person>” quietly becomes a plan that is blocked on nobody reading their reply. The cloud-migration runbook had said “Phase 1: waiting on Josh or Austin to create the repo and issue a bot token” since July 17, 2026. Daniel created the project and sent BOTH access tokens on July 21, replying to that very thread. Nobody opened it. The runbook, the project memory and every status summary kept reporting a human blocker for eleven more days, while the thing they were waiting for sat in the inbox — and the whole migration, plus the audit-trail benefit that depends on it, stalled for a reason that had already gone away.

The general shape: a blocker recorded as a person is only as fresh as the last time someone checked the channel that would clear it. Writing “waiting on X” creates an obligation to re-read, and nothing in the plan carried that obligation. Every named external dependency needs one of: a re-check date, an owner for the re-check, or — best — an automated probe that tests the actual precondition rather than trusting the note. Here the probe is two seconds of work: git ls-remote against the repo would have returned success on July 21 and every day after.

Test the PRECONDITION, not the memo about the precondition. This is the same error as trusting a “no skills changed” report instead of hashing the files: in both cases a status line was believed because it was written down, when the underlying state was one cheap call away.

Corollary on credentials that arrive by email: treat them as already-exposed. Both GitLab tokens came through in plaintext, so they were compromised the moment they were sent, regardless of what we do next. Store them in the one secret file, use them, and schedule a rotation — do not pretend that careful handling afterwards undoes an insecure delivery.

Fixed August 1, 2026: tokens stored in .credentials.json (read at push time by tools/git_askpass.py, never embedded in a remote URL), 9,023 files pushed live, and the runtime mirror now re-syncs, commits and pushes automatically as step 9/9 of the daily runner — so its freshness no longer depends on anyone remembering.

<!– learning:2026-08-01-a-quiet-queue-may-be-a-disconnected-one –> August 1, 2026 (from: sigrun-website-request-intake)

A daily job can be perfectly correct and still be watching nothing. When a source returns the same answer for weeks, verify that anything upstream is connected to it.

The Sigrun website intake read its Google Form sheet accurately every morning for sixteen consecutive days and truthfully reported “no new requests, 1 row.” The read was never wrong. The form had never been shared with members. Seven people were waiting in queues the job could not see, because every real request arrived by email, by Basecamp, or directly to Dennis. The finding did not come from the data — it came from a client asking, on the project thread, “please share a list of who is waiting.”

Adopt as standing practice:

  1. An unchanging source is a hypothesis, not a fact. Two weeks of identical output means

either nothing is happening or nothing is arriving. Those have different owners and different fixes. After N consecutive no-change runs, spend one call confirming the upstream path is live — that the form is published, the webhook fires, the inbox is monitored, the sheet is the one people actually submit to.

  1. Count the queue, not the source. A pipeline’s real backlog is the union of everything

feeding it. Keep an explicit lane for out-of-band arrivals (here, tracked_non_form) and treat it as first-class, because in a system where the front door is closed it is the whole queue.

  1. Reconcile against sister pipelines before reporting scope. Two agents were tracking

overlapping subsets of the same work — this job knew two members, another knew eight. Neither was wrong; both were partial. One read of the other agent’s outbound email closed the gap. When you know another automation touches the same domain, diff against it.

  1. “No changes” is only a safe report if you checked what would have made it change.

Green on an unconnected input reads exactly like green on a healthy one, and it buys silence for as long as nobody outside asks.

<!– learning:2026-08-01-decode-xml-entities-before-fetching-sitemap-children –> August 1, 2026 (from: wtp-monthly-seo-reaudit run (Western Trading Post, first tracked run))

Decode XML entities before fetching sitemap child URLs — or you will report a healthy sitemap as dead

Checking whether a client’s /sitemap.xml fix had shipped, the index at /xmlsitemap.php returned 200 and listed five child sitemaps. Fetching each child returned 404 with zero bytes, all five. The obvious read was that the sitemap fix was cosmetic — index alive, every child dead, zero URLs discoverable by Google. That was about to be the report’s headline finding, and it was completely wrong.

<loc> values are XML-escaped. The real URL is ?type=pages&page=1; the sitemap contains ?type=pages&amp;page=1. Fetching the raw captured string sends a literal &amp;, the parameters break, and the server 404s. Decoding entities first, all five children return 200 with 4,516 URLs.

Rules:

  1. Always entity-decode <loc> values before fetching them&amp; &lt; &gt; &quot; &apos;.

This bites hardest on sitemaps with query-string pagination, which is the norm on BigCommerce, Shopify and most hosted carts.

  1. A 100% failure rate across every child is a smell, not a finding. Real breakage is

usually partial. When every single item in a set fails identically, suspect the harness before the target — the same instinct that max_crawl_pages taught on the SERP side.

  1. Never report an infrastructure catastrophe from a single method. Confirm with a second

path (browser navigation to one child URL, or Search Console’s sitemap report) before telling a client their sitemap is dead. The credibility cost of a false alarm this size is far higher than the minute it takes to check.

Same run, same discipline, two more times: a robots.txt parser that reported “zero crawlers blocked” was prove-red tested against a synthetic blocking file first (it correctly caught 2/2) before its zero on the live file was trusted, and cross-checked against a raw count of bare Disallow: / lines. And a +46% referring-domain jump — exactly the shape of a mode/measurement artifact — was confirmed as real by pulling refdomains-history and seeing a steady 13-week climb before it was narrated as growth.

General form of all three: when a check returns the answer you were hoping for, or an answer too dramatic to be ordinary, make it prove itself before it reaches the client.

Learned August 1, 2026.

<!– learning:2026-08-01-does-not-resolve-is-not-is-not-registered –> August 1, 2026 (from: sigrun-website-request-intake)

“Does not resolve” and “is not registered” look identical to dig +short and have completely different owners. Ask the TLD’s own authoritative server, with controls.

Four member domains were on a live ops ticket that said “point the A-record at the fleet.” Two of them — anneliesalminen.com and piamuggerud.com — are not registered at all. There is no zone to add a record to. dig +short A returned empty for them, which reads as “DNS not configured yet” and had been reported that way for four weeks. Ops would have gone looking in GoDaddy for zones that do not exist.

Adopt as standing practice:

  1. Query the registry, not a resolver. dig @a.gtld-servers.net <domain> NS and read the

rcode: NXDOMAIN means no registry delegation (unregistered, or registered with no nameservers — either way the fix is at the registrar). NOERROR with an NS referral means registered and delegated, and only then is “add an A record” a real instruction.

  1. Run both controls in the same call or the result is unfalsifiable. A made-up domain as

the negative control (must return NXDOMAIN) and a known-good domain from the same batch as the positive control (must return an NS referral). Without them, an NXDOMAIN could be a network artifact. With them it is a finding you can put in front of a client.

  1. Before repointing any domain at the fleet, fetch its root and read the title. The

email-safety rule catches MX; it does not catch a working homepage. lisasennhauserkelly.com matched every “built but never provisioned” description in the queue while serving the member’s real live business site at HTTP 200. Repointing it would have destroyed live work. Check for a live site and live email, separately.

  1. Correct a wrong public status the same day you find it. Two of eight entries on a

client-visible list were mis-diagnosed as waiting on our DNS. Left alone, the members would have waited on an action nobody could take. Naming the real blocker moved two items from an ops backlog to a one-message ask.

<!– learning:2026-08-01-read-the-channel-before-reporting-a-missing-data-source –> August 1, 2026 (from: wtp-monthly-seo-reaudit run (Western Trading Post) — GSC reported as “not configured” while a teammate posted GSC data weekly in the same thread)

A task parameter that names a missing data source is a claim with an expiry date — check the client’s own channel first

This monthly audit’s parameters said gsc_property: not configured — Ahrefs + direct crawl only. The run believed it, wrote “No Google Search Console property is configured” into the client-facing report as a finding with an owner, and listed “get GSC verified” as an action.

Then the run opened the client’s Basecamp thread to post — and found our own operations teammate posting Search Console data in that thread every single week: ~120K impressions, 3.5% CTR, average position 8.4, top queries with click and impression counts. The property existed. It had existed the whole time.

Two costs, and the second is worse than the first:

  1. We nearly asked a client for access they had already granted — the exact move that burns an ask and makes

the retainer look inattentive.

  1. We did the analysis without the best data we had. Ahrefs estimates rankings; Search Console reports

what actually happened. The GSC query table turned out to contain the single most valuable finding of the engagement — 4,419 monthly impressions on one dead craftsman’s name, landing on a sold lot page. That insight was sitting in a teammate’s weekly report for six weeks and the “authoritative” monthly audit never opened it.

Rules:

  1. Before reporting any data source as missing or unavailable, read the client’s own channel — the

Basecamp thread, the shared drive, the weekly report someone else files. A per-client agent’s parameters are a snapshot of what was true when the task was written; access changes and nobody edits the task.

  1. When you find the parameters wrong, fix the parameters, not just the report. File it as an ask against

yourself in the ledger. A correction that lives only in one month’s write-up gets re-derived — and re-published as a false finding — next month.

  1. Sibling reporting is a data source, not just context. The existing 2026-07-20 learning already says

“check sibling scheduled tasks’ outputs before declaring a metric blocked.” Extend it: check what humans on the account are already reporting, in the channel you are about to post into. Read the channel before you write to it.

  1. Corollary on credit: when you use a teammate’s numbers, say whose they are. The client should see one team,

and the teammate should see their work being built on rather than quietly re-derived.

This is the same family as the 2026-07-31 lesson that “blocked is a claim that needs evidence” — but a rung earlier. There, a real blocker was misdiagnosed. Here, a non-existent blocker was inherited from a config file and published without anyone testing it once.

Learned August 1, 2026.

<!– learning:2026-08-02-same-origin-required-before-trusting-an-empty-search –> August 2, 2026 (from: WTP auction-tracking investigation — five Basecamp searches returned zero because they ran cross-origin from a client site)

An in-page fetch to another origin fails silently — and an empty search result looks exactly like “no history exists”

Asked to mine years of Basecamp history for prior conversations about a client’s auction platform, the run issued five in-page fetch calls to Basecamp’s search endpoint and got zero results for every query. The obvious conclusion was that the team had never discussed it.

The tab was sitting on auction.westerntradingpost.com. Every one of those fetches was cross-origin and was rejected by the browser before it left. The catch block swallowed it. Zero results was never an answer about Basecamp; it was an answer about CORS.

Run properly, the same searches returned 11 hits, and the history contained the single most valuable fact of the whole investigation: the client’s tag stack was already installed on the auction platform, and a 9-month-old access request had dissolved into an unrecorded phone call.

Rules:

  1. Check location.host before trusting any in-page fetch result. If you are not on the origin you are

querying, the result is meaningless. Navigate first, then query.

  1. A search that returns zero needs a positive control before you report “nothing exists.” Run a query you

know has hits through the identical code path. If the control also returns zero, the harness is broken, not the archive. This is the same prove-red discipline used for the robots.txt parser — extend it to every negative finding, because a negative finding is the easiest kind to fake.

  1. A second failure mode stacked on the first here: even same-origin, Basecamp’s search results are

client-rendered, so fetch + DOMParser returned a shell with zero result anchors while the live page showed 53. When a fetch of a modern web app returns structurally empty results, read the rendered DOM after navigation instead. Two different mechanisms, one identical symptom: a confident, wrong “nothing found.”

  1. **”No prior discussion” is a claim about an archive, and archives are exactly where an agent’s memory

advantage lives.** Getting it wrong does not just lose a fact — it wastes the institutional knowledge the client already paid for, and re-asks colleagues questions they answered months ago.

Learned August 2, 2026.


Other skills: ai-search-visibility · boil-the-ocean · 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

A skill is a document. A pack is a folder of documents. Neither one does any work. Work happens when a job runs those skills on a schedule, checks its own output, and keeps its files somewhere it can read them again tomorrow. That is the whole difference between owning skills and having an agent.

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