@clize/clize 0.32.0 → 0.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -18
- package/dist/cli.js +487 -374
- package/dist/cli.js.map +1 -1
- package/dist/core/addresses.js +12 -11
- package/dist/core/addresses.js.map +1 -1
- package/dist/core/analytics.js +6 -5
- package/dist/core/analytics.js.map +1 -1
- package/dist/core/authz.js +6 -5
- package/dist/core/authz.js.map +1 -1
- package/dist/core/billing.js +83 -12
- package/dist/core/billing.js.map +1 -1
- package/dist/core/credentials.js +3 -1
- package/dist/core/credentials.js.map +1 -1
- package/dist/core/dns.js +3 -2
- package/dist/core/dns.js.map +1 -1
- package/dist/core/domain-health.js +74 -50
- package/dist/core/domain-health.js.map +1 -1
- package/dist/core/domains.js +74 -44
- package/dist/core/domains.js.map +1 -1
- package/dist/core/email-tx.js +8 -6
- package/dist/core/email-tx.js.map +1 -1
- package/dist/core/email.js +13 -12
- package/dist/core/email.js.map +1 -1
- package/dist/core/env-detect.js +7 -14
- package/dist/core/env-detect.js.map +1 -1
- package/dist/core/handle.js +33 -30
- package/dist/core/handle.js.map +1 -1
- package/dist/core/install.js +132 -59
- package/dist/core/install.js.map +1 -1
- package/dist/core/media.js +37 -36
- package/dist/core/media.js.map +1 -1
- package/dist/core/payments.js +5 -4
- package/dist/core/payments.js.map +1 -1
- package/dist/core/projects.js +38 -26
- package/dist/core/projects.js.map +1 -1
- package/dist/core/redirect.js +4 -3
- package/dist/core/redirect.js.map +1 -1
- package/dist/core/seo-classify.js +41 -1
- package/dist/core/seo-classify.js.map +1 -1
- package/dist/core/seo-pages.js +214 -0
- package/dist/core/seo-pages.js.map +1 -0
- package/dist/core/seo.js +147 -11
- package/dist/core/seo.js.map +1 -1
- package/dist/core/serve.js +2 -1
- package/dist/core/serve.js.map +1 -1
- package/dist/core/setup.js +11 -9
- package/dist/core/setup.js.map +1 -1
- package/dist/core/shop.js +17 -16
- package/dist/core/shop.js.map +1 -1
- package/dist/core/site.js +2 -1
- package/dist/core/site.js.map +1 -1
- package/dist/core/sites.js +41 -40
- package/dist/core/sites.js.map +1 -1
- package/dist/core/triage.js +39 -5
- package/dist/core/triage.js.map +1 -1
- package/dist/core/video.js +45 -43
- package/dist/core/video.js.map +1 -1
- package/dist/github.js +4 -3
- package/dist/github.js.map +1 -1
- package/dist/i18n/t.js +65 -0
- package/dist/i18n/t.js.map +1 -0
- package/dist/i18n/zh.js +1266 -0
- package/dist/i18n/zh.js.map +1 -0
- package/dist/index.js +61 -40
- package/dist/index.js.map +1 -1
- package/dist/lib/cloudflare.js +8 -7
- package/dist/lib/cloudflare.js.map +1 -1
- package/dist/lib/crypto.js +2 -1
- package/dist/lib/crypto.js.map +1 -1
- package/dist/lib/vercel.js +2 -1
- package/dist/lib/vercel.js.map +1 -1
- package/dist/lib/zone.js +2 -1
- package/dist/lib/zone.js.map +1 -1
- package/dist/mcp-profiles.js +5 -1
- package/dist/mcp-profiles.js.map +1 -1
- package/dist/providers/deploy/cloudflare.js +3 -2
- package/dist/providers/deploy/cloudflare.js.map +1 -1
- package/dist/providers/email/cloudflare-inbound.js +4 -3
- package/dist/providers/email/cloudflare-inbound.js.map +1 -1
- package/dist/providers/email/cloudflare-outbound.js +2 -1
- package/dist/providers/email/cloudflare-outbound.js.map +1 -1
- package/dist/providers/email/resend.js +3 -2
- package/dist/providers/email/resend.js.map +1 -1
- package/dist/providers/email/ses.js +3 -2
- package/dist/providers/email/ses.js.map +1 -1
- package/dist/providers/index.js +3 -2
- package/dist/providers/index.js.map +1 -1
- package/dist/providers/media/google.js +10 -9
- package/dist/providers/media/google.js.map +1 -1
- package/dist/providers/media/kie.js +12 -11
- package/dist/providers/media/kie.js.map +1 -1
- package/dist/providers/media/kunavo.js +10 -9
- package/dist/providers/media/kunavo.js.map +1 -1
- package/dist/providers/media/openai.js +5 -4
- package/dist/providers/media/openai.js.map +1 -1
- package/dist/providers/media/suno.js +7 -6
- package/dist/providers/media/suno.js.map +1 -1
- package/dist/providers/registrar/routing.js +2 -1
- package/dist/providers/registrar/routing.js.map +1 -1
- package/dist/providers/registrar/vercel.js +2 -1
- package/dist/providers/registrar/vercel.js.map +1 -1
- package/dist/providers/seo/dataforseo.js +5 -4
- package/dist/providers/seo/dataforseo.js.map +1 -1
- package/dist/providers/seo/gsc.js +31 -4
- package/dist/providers/seo/gsc.js.map +1 -1
- package/dist/remote.js +25 -22
- package/dist/remote.js.map +1 -1
- package/dist/selfcheck.js +23 -22
- package/dist/selfcheck.js.map +1 -1
- package/dist/state/file-store.js +17 -16
- package/dist/state/file-store.js.map +1 -1
- package/package.json +4 -3
- package/skills/clize/SKILL.md +171 -143
- package/skills/clize-seo/SKILL.md +290 -187
- package/skills/clize-site-build/SKILL.md +97 -41
- package/skills/clize-site-debug/SKILL.md +73 -45
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: clize-seo
|
|
3
3
|
description: >-
|
|
4
|
-
Run and read an SEO/GEO analysis with `clize seo`. The commands give facts —
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
"
|
|
15
|
-
tool page", "how do we get backlinks", "outreach for links", "pitch this roundup".
|
|
4
|
+
Run and read an SEO/GEO analysis with `clize seo`. The commands give facts — keyword metrics,
|
|
5
|
+
who occupies a results page, your positions, your traffic sources, what Search Console shows.
|
|
6
|
+
This skill is the judgment layer: where the keywords to test come from, how big and expensive
|
|
7
|
+
one analysis is, what it must deliver, which are worth attacking at your authority level, what
|
|
8
|
+
a results page is really telling you, where to place content so it gets found, how to write so
|
|
9
|
+
AI engines can quote you, and how to read a `seo check` without fooling yourself. Triggers:
|
|
10
|
+
"do keyword research", "find keyword opportunities", "should I go after this keyword", "why
|
|
11
|
+
aren't we ranking", "what does this SERP mean", "read my seo check", "how do I get cited by
|
|
12
|
+
ChatGPT", "write this page for AI search", "where should I publish this", "is our SEO
|
|
13
|
+
working", "build an SEO landing page", "make a page that ranks", "build a free tool page",
|
|
14
|
+
"how do we get backlinks", "outreach for links", "pitch this roundup".
|
|
16
15
|
---
|
|
17
16
|
|
|
18
17
|
# Running and reading `clize seo`
|
|
@@ -26,44 +25,58 @@ Run `clize seo <cmd> --help` for exact flags.
|
|
|
26
25
|
**The weight formula — carry it through everything below.** SEO ≈ content 60% + links/authority
|
|
27
26
|
30% + technical 10%. For agents, amend the 60/30: content production cost has collapsed to ~zero
|
|
28
27
|
for *everyone*, so result pages are flooded with competent content and authority/entity signals
|
|
29
|
-
are what actually separate pages now — placement (§6) weighs more than its nominal 30%.
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
are what actually separate pages now — placement (§6) weighs more than its nominal 30%.
|
|
29
|
+
|
|
30
|
+
**Three failure shapes account for most flat lines, and they need different moves.** Shape A,
|
|
31
|
+
*no placement* (measured on our own tenant zero): nine weeks of work, on-page at 120%, live
|
|
32
|
+
referring domains at zero, every non-brand keyword flat at position 65+ — if your plan is all
|
|
33
|
+
pages and no placement, it is that plan; the fix is §6. Shape B, *lost pages* (measured on
|
|
34
|
+
tabledi, 2026-09): a site that *was* ranking — page-1 positions with no links at all — loses
|
|
35
|
+
most of its impressions to a migration; 79 URLs returning 404 held 38% of three months'
|
|
36
|
+
impressions, and from the keyword side the read looked exactly like Shape A. The fix is
|
|
37
|
+
technical (restore or redirect the URLs, get them re-crawled), and links would not have moved
|
|
38
|
+
it. The 10% is 10% of the *ceiling*; a 404 takes 100% of the *floor*. `gsc.pages` (§9) tells
|
|
39
|
+
the two apart — pages whose impressions collapsed and whose live status is not 200 are Shape B.
|
|
40
|
+
**Do not diagnose Shape A until you have read the pages face.** Shape C, *wrong bets* (measured
|
|
41
|
+
on our own tenant zero, 2026-09): every funded row is a head term — `stretch` / `wall`, or
|
|
42
|
+
`attackable` on a page held by high-authority sites — and not one row is a tool, a long-tail or a
|
|
43
|
+
`no_volume` / `pre_emergence` cell. Eleven weeks, 45 pages, 0 clicks, and the only cluster with
|
|
44
|
+
impressions was one Google picked for us. The read is `keywords.byBand` against the ledger's
|
|
45
|
+
`format` column: if the ledger holds no row your authority can win, placement is not the fix
|
|
46
|
+
either — re-derive the ledger toward tool, long-tail and locale cells (§1.2, §7.2) before
|
|
47
|
+
pitching a roundup. The counter-evidence sits in the same dogfood: tabledi and kunavo earned
|
|
48
|
+
page-1 positions with zero links, on tool and long-tail pages. Settle it per site with one round
|
|
49
|
+
of such rows enrolled in `check`, not by argument.
|
|
33
50
|
|
|
34
51
|
**§10 is the cycle that strings everything together. If a site exists, every session starts
|
|
35
52
|
there — with the free entry read (`clize seo check`), not with buying data. No site yet (or
|
|
36
53
|
nothing to point `--domain` at)? Then the cycle hasn't started: begin at §1, ship the first
|
|
37
54
|
batch, and enter the cycle at your first enrolled check.** §1–§3 are how to run a discovery
|
|
38
55
|
round, §4–§5 read the discovery data, §6–§7 turn rows into placement and pages, §8 is writing
|
|
39
|
-
for AI engines, §9 reads the scoreboard. Either way, the most common
|
|
40
|
-
|
|
56
|
+
for AI engines, §9 reads the scoreboard. Either way, the two most common failures of this
|
|
57
|
+
product are not misreading a number: testing a keyword set that was never enumerated (§1), and
|
|
58
|
+
ending a session with a ledger full of buildable rows and no pages shipped (§3 test 6, §7).
|
|
41
59
|
|
|
42
60
|
---
|
|
43
61
|
|
|
44
62
|
## 1. Where the seeds come from
|
|
45
63
|
|
|
46
64
|
Every command except `competitors` is a **verifier**: you hand it words, it prices them. None
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
**Enumerate the list. Do not recall it.** A list
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
37-keyword matrix built by enumeration, same site, same day. Output included `openrouter
|
|
63
|
-
alternative` at KD 1 with eight named roundups to pitch, a German pricing cluster, a Japanese
|
|
64
|
-
error-message query at 2,900/month that turned out to be a pure-UGC wall (worth knowing — it
|
|
65
|
-
killed a page that was about to be written), and two payment long-tails with no recorded volume
|
|
66
|
-
that Search Console showed sitting at positions 4.5 and 8. **None of those exist in the 19.**
|
|
65
|
+
will hand you a word you did not think of — except `competitors`, and the `newQueries` that come
|
|
66
|
+
back from `check`. The quality ceiling of the whole analysis is set before you spend a cent, by
|
|
67
|
+
how the candidate list was built.
|
|
68
|
+
|
|
69
|
+
**Enumerate the list. Do not recall it.** A list produced by thinking hard about the product is a
|
|
70
|
+
sample of your own memory: small, English-only, biased toward words you already believed in.
|
|
71
|
+
Measured (kunavo, 2026-08-31): 19 recalled keywords gave six themes and zero workable rows; a
|
|
72
|
+
37-cell matrix the same day gave `openrouter alternative` at KD 1 with eight roundups to pitch, a
|
|
73
|
+
German pricing cluster, a Japanese error-string query that turned out to be a UGC wall, and two
|
|
74
|
+
payment long-tails with no recorded volume sitting at positions 4.5 and 8 in Search Console.
|
|
75
|
+
None of those were in the 19.
|
|
76
|
+
|
|
77
|
+
One free source comes before the paid ones: **the entry read's own gsc face** (§10) —
|
|
78
|
+
`gsc.topQueries` / `newQueries` are Google's list of what it already considers you relevant for.
|
|
79
|
+
On a site with history it is the best-fitted word source there is; on a new site it is empty.
|
|
67
80
|
|
|
68
81
|
### 1.1 Competitor teardown — the only command that produces words
|
|
69
82
|
|
|
@@ -71,23 +84,16 @@ that Search Console showed sitting at positions 4.5 and 8. **None of those exist
|
|
|
71
84
|
clize seo competitors rival-one.com rival-two.com --limit 25
|
|
72
85
|
```
|
|
73
86
|
|
|
74
|
-
Two or three competitors × `--limit 25` is
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
their head terms, which you cannot have. Tearing down the site that ranks #4 on the queries you
|
|
80
|
-
care about gives you the long tail that is actually reachable.
|
|
81
|
-
|
|
82
|
-
Run this **first**, before you write down a single keyword of your own. It anchors the list in
|
|
83
|
-
the market instead of in your head.
|
|
87
|
+
Two or three competitors × `--limit 25` is 50–75 candidates for about $0.09 a domain, pre-filtered
|
|
88
|
+
by the only filter that matters: somebody already ranks on them. Read `topKeywords[].keyword`,
|
|
89
|
+
drop their brand terms, keep the rest. Pick rivals *your size or one step up* — a category
|
|
90
|
+
leader's list is head terms you cannot have; the site ranking #4 on your queries hands you the
|
|
91
|
+
reachable tail. Run this **first**, before writing down a keyword of your own.
|
|
84
92
|
|
|
85
93
|
### 1.2 Capability-surface enumeration — cross the axes, don't brainstorm
|
|
86
94
|
|
|
87
|
-
Ask
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
Common axes, and what a row looks like:
|
|
95
|
+
Ask what the product can actually absorb a visitor for, find the axes of that surface, and take
|
|
96
|
+
the cross-product. Mechanical is the point.
|
|
91
97
|
|
|
92
98
|
| pattern | axis A | axis B | rows you get |
|
|
93
99
|
| --- | --- | --- | --- |
|
|
@@ -95,24 +101,22 @@ Common axes, and what a row looks like:
|
|
|
95
101
|
| Payment/checkout tail | payment method (alipay, wechat, sepa, pix…) | your category noun | `alipay checkout api`, `pix payment link` |
|
|
96
102
|
| Competitor displacement | each competitor brand | `alternative` / `vs <you>` / `pricing` / `review` | `openrouter alternative` |
|
|
97
103
|
| New-thing window | model / API / spec names released in the last 90 days | `api`, `pricing`, `how to use` | `seedance api pricing` |
|
|
98
|
-
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
- **Write every cell, then filter.** Filtering while enumerating is
|
|
103
|
-
- **Do the non-English cells.** They
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
- **Cells you can't serve are not candidates.** Ranking for a query you cannot answer is a
|
|
107
|
-
|
|
108
|
-
|
|
104
|
+
| Host × capability | each agent host you have actually tested | each thing the product does for it | `openclaw email`, `codex deploy site` |
|
|
105
|
+
| Tool × locale | each browser-side tool you can honestly ship | each market you can serve | `hreflang generator` in `ja-JP` |
|
|
106
|
+
| Locale × head term | your two or three head terms | each market you can serve | `de-DE` pricing cluster (price it; §7.2 on whether to build it) |
|
|
107
|
+
|
|
108
|
+
- **Write every cell, then filter.** Filtering while enumerating is recalling again.
|
|
109
|
+
- **Do the non-English cells.** They hold the low-competition rows and are the cells a recalled
|
|
110
|
+
list always skips. Priceable markets: `en-US, en-GB, en-CA, en-AU, de-DE, fr-FR, es-ES, ja-JP,
|
|
111
|
+
ko-KR, zh-TW, pt-BR` — anything else is a 400.
|
|
112
|
+
- **Cells you can't serve are not candidates.** Ranking for a query you cannot answer is a bounce.
|
|
113
|
+
- 30–100 cells is a normal matrix; a full site with tools, hosts and locales is 100–150. Twelve
|
|
114
|
+
means you have not found your axes.
|
|
109
115
|
|
|
110
116
|
### 1.3 Search Console feedback — free, and it does not repeat your bias
|
|
111
117
|
|
|
112
|
-
`clize seo check` returns `gsc.newQueries`: queries Google
|
|
113
|
-
|
|
114
|
-
information no amount of enumeration produces. It costs nothing. Fold it back into the matrix
|
|
115
|
-
every round — it is the loop that makes the second analysis better than the first.
|
|
118
|
+
`clize seo check` returns `gsc.newQueries`: queries Google already shows you for that are not on
|
|
119
|
+
your list. No amount of enumeration produces this. Fold it back into the matrix every round.
|
|
116
120
|
|
|
117
121
|
---
|
|
118
122
|
|
|
@@ -123,10 +127,10 @@ A cold start is a funnel, and the funnel has a known size and a known price.
|
|
|
123
127
|
```
|
|
124
128
|
30–100 candidates (§1: competitor teardown + capability matrix)
|
|
125
129
|
→ seo keywords price the whole list in one batched call
|
|
126
|
-
→ drop `wall
|
|
127
|
-
→ 10–20 survivors
|
|
130
|
+
→ drop `wall`; keep `attackable`, `no_volume` and `no_data` (§5: the SERP decides, not the volume table)
|
|
128
131
|
→ seo serp each survivor the SERP check that KD cannot replace (§4)
|
|
129
|
-
→
|
|
132
|
+
→ rows written into the ledger (§3)
|
|
133
|
+
→ every buildable row shipped, family by family (§7: rows are stock, pages are output)
|
|
130
134
|
→ seo check --keywords <the survivors> the baseline you measure against
|
|
131
135
|
```
|
|
132
136
|
|
|
@@ -136,7 +140,7 @@ A cold start is a funnel, and the funnel has a known size and a known price.
|
|
|
136
140
|
| --- | --- | --- |
|
|
137
141
|
| `seo keywords`, 40 words | **~$0.10** | one batched call; per-word cost is negligible next to the fixed cost, so **always send the whole list at once** |
|
|
138
142
|
| `seo competitors`, 3 domains × 25 | **~$0.28** | the cheapest words you will ever buy |
|
|
139
|
-
| `seo serp`, one keyword | **~$0.02** |
|
|
143
|
+
| `seo serp`, one keyword | **~$0.02** | every survivor gets one, `no_data` cells included — 20–100 of these is $0.40–2.00 |
|
|
140
144
|
| `seo check` | **free**, plus ~$0.005/word to price words it has never priced | the metrics are reused for 30 days, so a re-check costs nothing |
|
|
141
145
|
| `seo spend` | **free** | itemized ledger of every charge — **copy it into the deliverable, never hand-tally** |
|
|
142
146
|
|
|
@@ -155,8 +159,9 @@ Three behaviours follow, and all matter:
|
|
|
155
159
|
free and prints every charge itemized with subtotals by command. **Copy those numbers.** A
|
|
156
160
|
hand-tallied total that disagrees with `seo spend` is your bug, and it is the single most
|
|
157
161
|
common defect in these deliverables.
|
|
158
|
-
- **You do not need a budget — you need the prior.** An agent's unit of work is one
|
|
159
|
-
not one month; do not try to ration a
|
|
162
|
+
- **You do not need a budget — you need the prior.** An agent's unit of work is one batch of
|
|
163
|
+
shipped pages — the analysis is what qualifies them — not one month; do not try to ration a
|
|
164
|
+
monthly number. The `cap` block reflects this: normally
|
|
160
165
|
it carries only `usd` (where the fuse sits). If it also carries `spentThisMonth` /
|
|
161
166
|
`remainingUsd`, **that itself is the signal** — you are past 80% of the fuse, and a note in
|
|
162
167
|
`notes` says so. When that happens, or whenever spend is far beyond what your session can
|
|
@@ -195,15 +200,18 @@ will — it cannot measure those. That half of the state exists only here.
|
|
|
195
200
|
|
|
196
201
|
| keyword | locale | sv | kd | serp verdict | intent | target page | page | placement | format | priority |
|
|
197
202
|
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
|
198
|
-
| openrouter alternative | en-US | 1,300 | 1 | listicle_window | commercial | /compare/openrouter/ | shipped:/compare/openrouter
|
|
199
|
-
| email api for ai agents | en-US | 0 (no_volume) | — | listicle_window | commercial | /inbox/ | shipped:/inbox
|
|
203
|
+
| openrouter alternative | en-US | 1,300 | 1 | listicle_window | commercial | /compare/openrouter/ | shipped:/compare/openrouter/, 08-30 | pitched: apidog roundup, 08-31 | comparison + pitch 8 roundups | P0 |
|
|
204
|
+
| email api for ai agents | en-US | 0 (no_volume) | — | listicle_window | commercial | /inbox/ | shipped:/inbox/, 08-27 | none | landing + pitch 4 roundups | P0 |
|
|
200
205
|
| メッセージストリームでエラー | ja-JP | 2,900 | 0 | definition_wall (UGC) | informational | — | none | none | skip: Reddit/Zenn own it | — |
|
|
201
206
|
|
|
202
207
|
- **`target page` is the intent; `page` and `placement` are the facts.** `target page` says which
|
|
203
208
|
page *should* carry this keyword — it is the query→page map, and §9 reads it against `check`'s
|
|
204
|
-
`topPage`. `page` (`none` / `shipped:<url>`) and `placement` (`none` / `pitched:<target,
|
|
205
|
-
/ `listed:<where>`) say what actually got funded. **You maintain those two; no command
|
|
206
|
-
and no command ever will.
|
|
209
|
+
`topPage`. `page` (`none` / `shipped:<url>, <date>`) and `placement` (`none` / `pitched:<target,
|
|
210
|
+
date>` / `listed:<where>`) say what actually got funded. **You maintain those two; no command
|
|
211
|
+
does**, and no command ever will. The date on `shipped:` is load-bearing: `check` measures a
|
|
212
|
+
window that closes three days before you run it, and a bet placed after `rank.window.to` has
|
|
213
|
+
not been measured by that window at all (§9, `pending`). Judging it anyway is how tabledi's
|
|
214
|
+
2026-09-02 round declared a P0 "missed" on a window that closed before the pages shipped.
|
|
207
215
|
- **A row with a page and no placement is half a bet.** Building the page and not placing it is
|
|
208
216
|
manufacturing stock and never shipping it — the failure shape in the formula up top. The
|
|
209
217
|
`placement` column is there so that half is visible at a glance instead of buried in prose.
|
|
@@ -211,14 +219,21 @@ will — it cannot measure those. That half of the state exists only here.
|
|
|
211
219
|
work; "write /compare/openrouter/ as a comparison page and pitch these eight roundups" is.
|
|
212
220
|
- **Rows you decided against stay in the table**, with the reason. That is what stops the next
|
|
213
221
|
session from re-buying the same keyword — and a `skip` row with a reason is a real finding.
|
|
222
|
+
- **Opening the ledger on a site that already has analysis documents** (a plan, an older
|
|
223
|
+
worksheet, a handoff note): import their skip rows and recorded lessons *before* writing a new
|
|
224
|
+
row. A lesson you do not import is one you will re-learn at full price. tabledi, 2026-09-01,
|
|
225
|
+
had recorded in prose that `invoice data extraction` was the site's third-best earning URL
|
|
226
|
+
despite an enterprise-looking SERP; the next day's session opened a fresh ledger, re-derived
|
|
227
|
+
the enterprise read from the SERP, and marked the page for retargeting. The rule that would
|
|
228
|
+
have caught it is in §4 ("Search Console beats the SERP read").
|
|
214
229
|
- **Narrative goes underneath as footnotes.** Two or three lines about what the pattern means.
|
|
215
230
|
Not six paragraphs of themes.
|
|
216
231
|
|
|
217
232
|
### The pass/fail test
|
|
218
233
|
|
|
219
|
-
|
|
234
|
+
Six ways an analysis fails. The first is about *what you found*, the last two about *what you
|
|
220
235
|
funded*; the middle three are about *whether you actually ran the analysis you claim to have
|
|
221
|
-
run* — all
|
|
236
|
+
run* — all six have been observed in real runs that read as finished.
|
|
222
237
|
|
|
223
238
|
1. **Themes instead of rows.** If what you produced is a set of *directions* or *themes*, or
|
|
224
239
|
the table has fewer than ten actionable rows, **the fix is upstream, in §1**. Do not write it
|
|
@@ -244,6 +259,11 @@ run* — all five have been observed in real runs that read as finished.
|
|
|
244
259
|
discussions visible in `items` — so this costs nothing to fix: write them as rows next to
|
|
245
260
|
the pages they support. Observed in the field (2026-08-31 second run): five funded pages,
|
|
246
261
|
placement nearly invisible in the deliverable.
|
|
262
|
+
6. **Rows without pages.** Buildable rows — a serp verdict that is not a wall, an intent you can
|
|
263
|
+
serve — left unshipped at session end are stock, not output. The deliverable counts shipped
|
|
264
|
+
URLs, not qualified rows; a ledger of twenty rows and two pages is a session that spent itself
|
|
265
|
+
on the analysis. Observed on our own tenant zero: eleven weeks of rounds that produced ledgers
|
|
266
|
+
and no pages. If a page costs a session, the first deliverable is the generator (§7).
|
|
247
267
|
|
|
248
268
|
---
|
|
249
269
|
|
|
@@ -287,6 +307,19 @@ invisible to a rule engine because it requires knowing what your product is. Sam
|
|
|
287
307
|
`ai domain registration`, which turns out to mean "buy a .ai domain". **Always read `items`
|
|
288
308
|
before committing to a keyword**, whatever the verdict says.
|
|
289
309
|
|
|
310
|
+
### Search Console beats the SERP read
|
|
311
|
+
|
|
312
|
+
The SERP shows who Google ranks first; Search Console shows which queries Google already sends
|
|
313
|
+
to *your* page. When they disagree about intent, Search Console is the fact about you. So: **a
|
|
314
|
+
keyword your site already earns impressions or clicks on is never an intent-mismatch skip and
|
|
315
|
+
never a retarget**, whatever the top 10 looks like. The move on such a row is additive — a
|
|
316
|
+
section that answers the query, internal links into the page — and it keeps the title and H1
|
|
317
|
+
that earned the impressions. tabledi, 2026-09: `invoice data extraction` read as an enterprise
|
|
318
|
+
AP wall from the SERP (nanonets, klippa, rossum), while the page was the site's third-best
|
|
319
|
+
earner and its "…to excel" long-tails sat at position 11. Retargeting it would have traded a
|
|
320
|
+
measured 262 impressions for a guess. The intent-mismatch rule above is for keywords you have
|
|
321
|
+
not entered yet; once Google has put you on a query, it has already answered the intent question.
|
|
322
|
+
|
|
290
323
|
### `aiOverview: true`
|
|
291
324
|
|
|
292
325
|
An AI Overview sits above the organic results and answers the query in place. Position 3 under
|
|
@@ -316,11 +349,16 @@ For a site with near-zero authority (new domain, few referring domains):
|
|
|
316
349
|
`listicle_window` you can parasitize meanwhile.
|
|
317
350
|
- `wall` is not a plan. Skip it.
|
|
318
351
|
|
|
319
|
-
**Within the same band, intent ranks before volume
|
|
320
|
-
`X vs Y`, `pricing`, `best X for Y`)
|
|
321
|
-
they sit at the bottom of the funnel,
|
|
322
|
-
funding the next batch of pages. Fill
|
|
323
|
-
volume second — a KD 8 commercial row beats
|
|
352
|
+
**Within the same band, intent ranks before volume — until the site's own clicks say
|
|
353
|
+
otherwise.** Commercial rows (`<rival> alternative`, `X vs Y`, `pricing`, `best X for Y`)
|
|
354
|
+
outrank informational rows (`what is X`) for priority: they sit at the bottom of the funnel,
|
|
355
|
+
they convert, and conversion evidence is what justifies funding the next batch of pages. Fill
|
|
356
|
+
the worksheet's `priority` column by intent first, volume second — a KD 8 commercial row beats
|
|
357
|
+
a KD 5 informational row. That is the prior for a site with no history. A site with history
|
|
358
|
+
overrules it: read `gsc.pages.latest` (§9) and rank the intents that already earn clicks first.
|
|
359
|
+
tabledi's history was 105 of 134 clicks from free tool and how-to pages and zero from anything
|
|
360
|
+
commercial — on that site a tool-intent row outranks a `best X` row, whatever the generic rule
|
|
361
|
+
says.
|
|
324
362
|
|
|
325
363
|
### `no_data` and `no_volume` mean opposite things
|
|
326
364
|
|
|
@@ -334,10 +372,21 @@ They used to be one value. They are not one thing:
|
|
|
334
372
|
volume and a `listicle_window` with four roundups on it. On kunavo, two payment long-tails
|
|
335
373
|
came back at volume 0 and Search Console showed them at positions 4.5 and 8 with real
|
|
336
374
|
impressions — **people were already arriving on words the keyword tool scored as empty.**
|
|
337
|
-
That configuration has a name in `check` output: `pre_emergence` (§9).
|
|
375
|
+
That configuration has a name in `check` output: `pre_emergence` (§9) — and since 0.34.0 the
|
|
376
|
+
same label fires when the window's impressions reach twice the priced monthly volume, because
|
|
377
|
+
a small `sv` next to a large impression count is the same fact (the tool has not caught up,
|
|
378
|
+
or it priced one market while Search Console counts all of them). Either way, the impressions
|
|
379
|
+
are the demand; the `sv` is not a reason to downgrade the row.
|
|
338
380
|
|
|
339
381
|
So: a `no_volume` row whose SERP is already full of category roundups is worth an early page.
|
|
340
|
-
A `no_data` row
|
|
382
|
+
A `no_data` row goes to `seo serp` like any other survivor — the SERP does not need a volume
|
|
383
|
+
figure to show who occupies it. Open and servable → buildable, enrolled as `pending`; wall →
|
|
384
|
+
skip row with the reason. What `no_data` never is: a reason to drop the cell. Measured across
|
|
385
|
+
three sites, the rows with the best positions were disproportionately ones the volume table
|
|
386
|
+
could not see (kunavo's payment long-tails at 4.5 and 5.8, clize.ai's German MCP cluster at
|
|
387
|
+
130 impressions), while the priced `attackable` rows sat at 65+ — the table's coverage and the
|
|
388
|
+
incumbents' coverage are the same coverage. On 2026-09-02, 82 of 119 cells left the funnel at
|
|
389
|
+
the pricing step without a single SERP look; keeping them would have cost $1.64 in serp calls.
|
|
341
390
|
|
|
342
391
|
### `--ai` is a different unit
|
|
343
392
|
|
|
@@ -359,7 +408,15 @@ Priority order, most effective first for a low-authority site:
|
|
|
359
408
|
|
|
360
409
|
1. **Third-party trust positions.** Registries and directories that accept submissions, and the
|
|
361
410
|
roundups in `parasiteTargets`. Getting listed in a page that already ranks beats trying to
|
|
362
|
-
outrank it.
|
|
411
|
+
outrank it. Each target carries `owner`: `publisher` (independent media, review or directory
|
|
412
|
+
site — pitch first), `unknown` (go look at who runs it), `vendor` (the host sells in this
|
|
413
|
+
category, or the roundup sits under a product site's `/blog/`-type path — pitch last, and
|
|
414
|
+
only if what you sell is not what they sell). A vendor's roundup of its own category rarely
|
|
415
|
+
adds a rival and never adds a product from another category: tabledi's five `client reporting
|
|
416
|
+
tool` targets were all vendor blogs, with zero fit for a desktop spreadsheet, and were queued
|
|
417
|
+
as P0 placement anyway. **If every target is `vendor`, the `listicle_window` is narrower than
|
|
418
|
+
the verdict says** — treat it as one or two pitches, not a placement plan. Directory/forum
|
|
419
|
+
submissions stay manual — the product produces the target list,
|
|
363
420
|
it does not post anything. One class is manual **once**, not per-post: platforms with a
|
|
364
421
|
publish API (dev.to, Hashnode) need a human to create the account and mint an API key one
|
|
365
422
|
time; after that the agent drafts, sets `canonical_url` back to your own page (skip it and
|
|
@@ -449,6 +506,13 @@ comparison / tool page) plus the on-page and GEO spec sheet — slug, title, met
|
|
|
449
506
|
schema rules. **That pack is the single source of truth for specs; this file only carries the
|
|
450
507
|
judgment calls.** Don't improvise slugs and titles from memory.
|
|
451
508
|
|
|
509
|
+
**If a page costs a session, the first deliverable is the generator, not the page.** A
|
|
510
|
+
hand-written page is one session each; a page family — one stem × N locales, one `tool.js`, one
|
|
511
|
+
skeleton, a strings file per locale (the spec pack's Page families section) — is a strings file
|
|
512
|
+
each after the first. clize.ai went from one page per session to seven in one session the day
|
|
513
|
+
a family generator existed, and tabledi shipped forty in two days from one. Build the generator
|
|
514
|
+
before the second page of any family, and never machine-translate a family page.
|
|
515
|
+
|
|
452
516
|
### 7.1 The pipeline — five steps, each with a pass/fail
|
|
453
517
|
|
|
454
518
|
| step | act | it passed if |
|
|
@@ -457,7 +521,7 @@ judgment calls.** Don't improvise slugs and titles from memory.
|
|
|
457
521
|
| ② produce | skeleton from `build site start`, judgment from here, words and code from you | first 200 words answer the query completely; your definition sentence is verbatim-identical site-wide |
|
|
458
522
|
| ③ self-check | against the spec sheet — there is deliberately no audit command | visible FAQ ↔ JSON-LD strictly 1:1; ≥2 internal links with varied anchors |
|
|
459
523
|
| ④ deploy | ship it | page live, in sitemap |
|
|
460
|
-
| ⑤ enroll | `seo check --keywords <+this page's keyword>` | the keyword shows in check output
|
|
524
|
+
| ⑤ enroll | `seo check --keywords <+this page's keyword>`, and write `shipped:<url>, <date>` in the ledger | the keyword shows in check output — under `pending` until a window that includes the ship date closes, then under `latest` or `unseen` |
|
|
461
525
|
|
|
462
526
|
Page type from row type: variant row → landing; `alternative`/`vs` row → comparison; action-
|
|
463
527
|
intent row → use-case; tool-intent row → tool page.
|
|
@@ -473,9 +537,15 @@ Judgment calls that matter:
|
|
|
473
537
|
entirely client-side (generators, converters, calculators — llms.txt generator, robots.txt
|
|
474
538
|
generator, JSON-LD generator). Tools that must fetch other people's sites (checkers) die on
|
|
475
539
|
CORS — don't attempt them until the platform offers a fetch proxy.
|
|
476
|
-
- **One tool × N locales is legitimate multiplication
|
|
477
|
-
and the non-English cells of your matrix are the low-competition
|
|
478
|
-
real, distinct intent — this is not page-stuffing.
|
|
540
|
+
- **One tool × N locales is legitimate multiplication, and the gate is per cell.** Tool UI
|
|
541
|
+
translates almost for free, and the non-English cells of your matrix are the low-competition
|
|
542
|
+
ones. Each locale page is a real, distinct intent — this is not page-stuffing. Build every
|
|
543
|
+
locale cell whose *own* SERP is not a wall (`seo serp --locale`, $0.02); the English page's
|
|
544
|
+
impressions are not the gate, because locale SERPs are only loosely coupled — a browser CSV
|
|
545
|
+
tool holds #1 in German where the English SERP is a tutorial wall, and a Korean payment
|
|
546
|
+
long-tail ranked where its English sibling never did. What does not multiply: marketing and
|
|
547
|
+
landing pages. Locale copies of those earned zero on every site measured (72 on clize.ai, 199
|
|
548
|
+
near-identical on kunavo) and are the composition drag gate 4 exists to prevent.
|
|
479
549
|
- **Pick tools with a shelf life.** Protocol- and format-level tools (llms.txt, schema, robots)
|
|
480
550
|
keep earning links for years; trend-chasing tools die with the trend. Ask: will anyone search
|
|
481
551
|
this in a year?
|
|
@@ -492,11 +562,30 @@ do the protecting, not experience:
|
|
|
492
562
|
3. **Promise only what exists.** Features described must actually work; for a tool page, run
|
|
493
563
|
the tool yourself before shipping. Pages that oversell poison the domain's trust and your
|
|
494
564
|
own E-E-A-T.
|
|
495
|
-
4. **
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
565
|
+
4. **Throttle by the site, not the session.** Google's scaled-content rule has three terms —
|
|
566
|
+
many pages, built to manipulate ranking, not helping users — counted per site, with no term
|
|
567
|
+
for how many pages one session shipped and none for how they were produced. So there is no
|
|
568
|
+
page count here, per batch or per session: several sessions build on one site at once, and a
|
|
569
|
+
per-session cap neither sees the site nor protects it. A batch is gated by the site's
|
|
570
|
+
composition, three checks:
|
|
571
|
+
- **each page has its own row and its own SERP** (gate 1) — the query exists and is
|
|
572
|
+
enterable;
|
|
573
|
+
- **the family is not a template with the noun swapped** — before deploy, diff the
|
|
574
|
+
non-skeleton text of each new page against its siblings and the site. Pages that are mostly
|
|
575
|
+
identical are doorway pages whatever their keywords say, and one thin family drags the
|
|
576
|
+
whole site's classifier score, tool pages included. Distinct real content per page: a
|
|
577
|
+
locale page written in-locale, a host page whose install and config really differ;
|
|
578
|
+
- **the last batch is being indexed** — `gsc.pages.unseen` (§9) shows the previous batch's
|
|
579
|
+
`index`. Mostly `indexed` → ship the next family. Mostly `Crawled - currently not indexed`
|
|
580
|
+
→ that is Google's own verdict on those pages; the round is indexing and pruning, not
|
|
581
|
+
shipping.
|
|
582
|
+
Impressions are not the gate for the next batch: at zero authority they arrive five to six
|
|
583
|
+
weeks after shipping and clicks may not arrive in a quarter, so a rule that waits for them
|
|
584
|
+
ships nothing for months — that was the mechanism behind five pages a month. Waiting is for
|
|
585
|
+
*deciding a new page type* (§10), never for the siblings of a type already decided. The
|
|
586
|
+
retire rule is the other half of shipping fast: a page indexed for two windows with no
|
|
587
|
+
impressions, or refused by the index for two, is noindexed, merged or redirected. The
|
|
588
|
+
composition stays clean only if dead pages leave.
|
|
500
589
|
|
|
501
590
|
### 7.4 E-E-A-T floor and locales
|
|
502
591
|
|
|
@@ -552,108 +641,114 @@ absolute share.
|
|
|
552
641
|
|
|
553
642
|
### `rank`
|
|
554
643
|
|
|
555
|
-
`rank.source` is always `"gsc"`: positions come from Search Console
|
|
556
|
-
upstream calls
|
|
557
|
-
|
|
558
|
-
- `position` is the **impression-weighted average over `rank.window`**, not
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
impressions
|
|
565
|
-
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
- `rank: null` means
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
a head-to-head on one keyword is `seo serp <keyword>`, where the whole page comes back at once.
|
|
644
|
+
`rank.source` is always `"gsc"`: positions come from Search Console, and measurement makes no
|
|
645
|
+
upstream calls — the only thing `check` may pay for is pricing words it has never priced.
|
|
646
|
+
|
|
647
|
+
- `position` is the **impression-weighted average over `rank.window`**, not a rank at a moment;
|
|
648
|
+
one good day barely moves it, and it is not comparable to a scraped SERP position. `delta` is
|
|
649
|
+
positive when you moved up, against `rank.prevWindow`; `series` is weekly, newest first, with
|
|
650
|
+
empty weeks absent — read the dates.
|
|
651
|
+
- **`movers` runs on two axes**: position shifted by 3+, *or* impressions halved / doubled with
|
|
652
|
+
the larger side at 20+ (`prevImpressions` / `prevClicks` sit on every row). Traffic is
|
|
653
|
+
impressions × CTR, and position alone is blind to the event that matters most: tabledi's
|
|
654
|
+
duplicate-finder page fell from 3,088 impressions to 50 while its `delta` read −0.5. A word
|
|
655
|
+
that had impressions last window and none now appears with `position: null`. **A collapse
|
|
656
|
+
with a flat position is a page problem — go to `gsc.pages` before you touch the ledger.**
|
|
657
|
+
- `pending` lists words **enrolled after `rank.window.to`**: no reading yet, not in `unseen`, no
|
|
658
|
+
signal form. A zero on a pending word says nothing. One exception you apply yourself: a word
|
|
659
|
+
enrolled for a page older than the window reads as `unseen`.
|
|
660
|
+
- Every row carries **`sv` and `kd` next to the Search Console numbers** — demand and delivery
|
|
661
|
+
side by side. `sv: 27100` at `position: 74` and `sv: 0` at `position: 4.5` are different worlds.
|
|
662
|
+
- `unseen` lists words **Search Console did not see this window**. Not "not ranking" — GSC has a
|
|
663
|
+
row only once you get impressions. A real position for one of them is a deliberate
|
|
664
|
+
`clize seo serp <keyword>` (~$0.02); never for all of them.
|
|
665
|
+
- `rank: null` means the round had nothing to synthesise from (no Search Console access, or no
|
|
666
|
+
list) — read `notes`. **Competitor positions are not in `check`**; `--competitors` is remembered
|
|
667
|
+
configuration, and a head-to-head is `seo serp <keyword>`.
|
|
580
668
|
|
|
581
669
|
### Intent vs reality: your `target page` against `topPage`
|
|
582
670
|
|
|
583
|
-
Every `rank.latest` row
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
pages is actually carrying this query". The judgment is the *comparison*, and the other half
|
|
587
|
-
comes from the ledger (§3): the row's `target page`, the page you meant to carry it.
|
|
671
|
+
Every `rank.latest` row carries **`topPage`** — the page of yours that took the most impressions
|
|
672
|
+
on that query — and **`pages`**, how many of yours got any. Pure aggregate; the judgment is the
|
|
673
|
+
comparison with the ledger's `target page` (§3):
|
|
588
674
|
|
|
589
675
|
| configuration | what it means | what to do |
|
|
590
676
|
| --- | --- | --- |
|
|
591
|
-
| **Accidental carrier** — ledger `page` is `none`,
|
|
592
|
-
| **Mismatch** — `target page` is A, `topPage` is B |
|
|
593
|
-
| **Self-cannibalisation** — `pages` > 1 |
|
|
677
|
+
| **Accidental carrier** — ledger `page` is `none`, `topPage` has a value | Google picked a carrier out of pages you built for something else | Go with its pick: internal links and a section that answers the query on *that* page. Cheapest double-down there is |
|
|
678
|
+
| **Mismatch** — `target page` is A, `topPage` is B | A is not recognised as the answer | Structure, not content: links and anchors into A, title/H1 alignment, is A indexed. More words on A is the reflex to resist |
|
|
679
|
+
| **Self-cannibalisation** — `pages` > 1 | Your pages are splitting one query | Merge, or canonical one to the other — after looking: `pages` is a count, not a split, and `2` with one page taking almost everything is ordinary |
|
|
594
680
|
|
|
595
|
-
|
|
596
|
-
almost everything is ordinary, and the row does not tell you which case you are in. Treat >1 as
|
|
597
|
-
a reason to go look, not a verdict — the query→page detail lives in Search Console.
|
|
598
|
-
|
|
599
|
-
### `rank.signals` — the cross-source read, done for you
|
|
681
|
+
### `gsc.pages` — the page face
|
|
600
682
|
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
numbers the label was derived from (`sv`, `kd`, `position`, `impressions`, `clicks`), so you can
|
|
604
|
-
overrule it. Three forms:
|
|
683
|
+
The question the product exists for is "how much traffic does the final page get", answered by
|
|
684
|
+
page since 0.34.0 — three facts side by side, no judgment:
|
|
605
685
|
|
|
606
|
-
|
|
|
686
|
+
| list | what is in it | the facts per row |
|
|
607
687
|
| --- | --- | --- |
|
|
608
|
-
| `
|
|
609
|
-
| `
|
|
610
|
-
| `
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
688
|
+
| `latest` | the 20 pages with the most impressions this window | `impressions` / `clicks` and their `prev*` twins, `position`, live `status` (final HTTP status; `finalUrl` when it landed elsewhere) |
|
|
689
|
+
| `movers` | pages whose impressions halved or doubled (larger side 20+), including pages that went to zero | the same, plus `index` for the collapsed ones |
|
|
690
|
+
| `unseen` | sitemap URLs with no impressions this window, newest `lastmod` first | `status`, `index` — Google's own words: `Submitted and indexed`, `Crawled - currently not indexed`, `Discovered - currently not indexed`, `URL is unknown to Google` |
|
|
691
|
+
|
|
692
|
+
Read it as three shapes, in this order:
|
|
693
|
+
|
|
694
|
+
- **Lost page** — in `movers`, impressions collapsed, `status` ≠ 200 or a `finalUrl` elsewhere.
|
|
695
|
+
Shape B: restore the URL or 301 it to the page with the same intent (a mismatched redirect is
|
|
696
|
+
a soft 404), request indexing, leave the ledger alone — nothing can be read off a window in
|
|
697
|
+
which the page was dead.
|
|
698
|
+
- **Not on the shelf** — in `unseen` with `index` anything but indexed. Not in the race yet;
|
|
699
|
+
discovery and content cannot help it. Internal links, sitemap `lastmod`, an indexing request,
|
|
700
|
+
then wait a window. Thirty-five `unseen` words on a site that just shipped sixty pages are
|
|
701
|
+
this shape until proven otherwise. This list is also the batch gate (§7.3): the last batch
|
|
702
|
+
mostly `indexed` → ship the next family; mostly `Crawled - currently not indexed` → Google's
|
|
703
|
+
own verdict on those pages, and this round prunes instead of shipping.
|
|
704
|
+
- **Indexed and ignored** — in `unseen` with `index` indexed, for a window after its ship date.
|
|
705
|
+
Only now is it a keyword question (§4, §5); after a second such window it is a retire
|
|
706
|
+
question (§7.3).
|
|
707
|
+
|
|
708
|
+
Index status costs a quota call per URL, so `check` inspects at most 30 per run — collapsed
|
|
709
|
+
movers first, then the newest unseen sitemap URLs, then top pages — and `notes` says how many.
|
|
710
|
+
`index: null` means *not inspected this round*, never "not indexed". `unseen` order follows the
|
|
711
|
+
sitemap's `lastmod`; without it the budget goes to whatever sorts first — ship `lastmod`. If
|
|
712
|
+
`notes` says inspection is unavailable, the service account is `Restricted`; `Full` unlocks it.
|
|
713
|
+
No sitemap means no `unseen` list — publish one.
|
|
628
714
|
|
|
629
|
-
|
|
630
|
-
are excluded and the gap is stated in `notes` — an empty bucket is an honest empty, not a
|
|
631
|
-
fabricated `no_volume`. `no_data` inside `byBand` means "we asked and the upstream has no row
|
|
632
|
-
for it", which is a genuine finding about a very long-tail word.
|
|
633
|
-
|
|
634
|
-
### `traffic`
|
|
635
|
-
|
|
636
|
-
- `ai` is always present, **including when it is all zeros — zero is the finding.** First
|
|
637
|
-
non-zero AI referral is a real milestone; note the date.
|
|
638
|
-
- The numbers are **adaptively sampled and rounded by magnitude**. Compare them over time.
|
|
639
|
-
Never quote them as exact counts, to yourself or to anyone else.
|
|
640
|
-
- Same-host referrers are internal navigation and already excluded.
|
|
715
|
+
### `rank.signals` — the cross-source read, done for you
|
|
641
716
|
|
|
642
|
-
|
|
717
|
+
`signals` puts the three sources side by side and labels the configuration. **Fact compression,
|
|
718
|
+
not advice** — like `serp`'s `verdict` — and every row carries the numbers it was derived from,
|
|
719
|
+
so you can overrule it:
|
|
643
720
|
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
721
|
+
| form | the configuration | how to read it |
|
|
722
|
+
| --- | --- | --- |
|
|
723
|
+
| `pre_emergence` | priced volume is **0**, or the window's impressions are **at least twice** the priced monthly volume — and Search Console shows real impressions (10+) | Demand is ahead of the keyword tools; on a low-authority site this is the cheapest entry there is. Write the page. A small `sv` next to a large impression count is the same fact — never let the `sv` downgrade the row |
|
|
724
|
+
| `authority_limited` | many impressions, position 50+, **zero clicks** | Google offers the keyword surface and withholds the position. An authority bottleneck **on that theme** — another page on it will not move it; go do §6.1. The label is per keyword: production on other themes continues |
|
|
725
|
+
| `demand_no_surface` | real search volume, **no impressions at all** this window | Google is not putting you on the shelf. A discovery question (what occupies it? → `seo serp`), not a measurement one |
|
|
726
|
+
|
|
727
|
+
`demand_no_surface` is capped at the five most attackable words (lowest KD, then volume;
|
|
728
|
+
`pending` words never take this form); the full list is `rank.unseen`. On a cold-start site
|
|
729
|
+
nearly every word is in this shape — normal state, not a finding. An empty `signals` is not a
|
|
730
|
+
failure. There is deliberately no `data_conflict` form: `check` has one position source; when
|
|
731
|
+
a scrape disagrees with Search Console, believe Search Console — a 28-day average of what
|
|
732
|
+
Google served against one personalised fetch of a rotating page.
|
|
733
|
+
|
|
734
|
+
### `keywords.byBand`, `traffic`, `gsc`
|
|
735
|
+
|
|
736
|
+
- `byBand` counts **only priced keywords**; the unpriced gap is stated in `notes`. `no_data`
|
|
737
|
+
inside it means "we asked and the upstream has no row" — a finding about a long-tail word,
|
|
738
|
+
not about demand (§5).
|
|
739
|
+
- `traffic.ai` is always present, **including when it is all zeros — zero is the finding**, and
|
|
740
|
+
the first non-zero referral is the milestone. The numbers are adaptively sampled and rounded;
|
|
741
|
+
compare over time, never quote as exact counts. Same-host referrers are already excluded.
|
|
742
|
+
- `gsc.newQueries` is the feedback loop closing (§1.3) — the most valuable output of the whole
|
|
743
|
+
command; fold them into the matrix. `gsc: null` is not an error: read `notes`, usually the
|
|
744
|
+
service account is not on the property yet.
|
|
649
745
|
|
|
650
746
|
### The one rule that ties it together
|
|
651
747
|
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
the roundups still come from a deliberate `seo serp`.
|
|
748
|
+
`sv` in the thousands, `position` around 70, hundreds of impressions, almost no clicks, and
|
|
749
|
+
`serp` says `listicle_window`: **the demand exists, the position is authority-limited, and the
|
|
750
|
+
way in is the roundups, not another page on that theme.** `check` labels the middle of that
|
|
751
|
+
sentence (`authority_limited`); the roundups still come from a deliberate `seo serp`.
|
|
657
752
|
|
|
658
753
|
---
|
|
659
754
|
|
|
@@ -694,10 +789,13 @@ analysis), and on a site with any history it hands you free seeds (`gsc.topQueri
|
|
|
694
789
|
|
|
695
790
|
| the read says | this round is |
|
|
696
791
|
| --- | --- |
|
|
697
|
-
| nothing yet (round one), or `demand_no_surface` across the board | **discovery + first bets**: `seo competitors <2–3 rivals>` → capability matrix (§1.2, seeded with anything the entry read gave you) → `seo keywords <the whole list>` in one call → drop `wall` → `seo serp` each survivor → ledger rows (§3) → first batch
|
|
792
|
+
| nothing yet (round one), or `demand_no_surface` across the board | **discovery + first bets**: `seo competitors <2–3 rivals>` → capability matrix (§1.2, seeded with anything the entry read gave you) → `seo keywords <the whole list>` in one call → drop `wall` → `seo serp` each survivor → ledger rows (§3) → first batch: **at least three page types, every buildable row in each**, through the §7 pipeline (a generator first for any family, specs from `build site start`, prose per §8) → enroll: `seo check --keywords <survivors> --brand <you>` → start placement (§6) immediately — do not wait for rankings to "settle"; that wait is how sites end up at 120% on-page and zero authority. Total ≈ $1 |
|
|
698
793
|
| `movers` rising, a `rank.latest` row's clicks first going non-zero, or `traffic.ai.total` first non-zero | **double down** — the branch the whole flywheel exists for: enumerate that keyword's sibling cells and price only the new ones (old ones are cached), add internal links into the page that is actually carrying it (`topPage` on that row is Google's answer, the ledger's `target page` is yours — when the two disagree, §9), add placement positions on that theme. Returns compound where the signal already is |
|
|
699
794
|
| `pre_emergence` | write that page this round — demand is ahead of the tools |
|
|
700
|
-
| `
|
|
795
|
+
| a `rank.movers` row whose impressions collapsed with a flat position, or a `gsc.pages.movers` row with `status` ≠ 200 | **technical round, no keyword work**: restore or redirect the URL, request indexing, re-read next window. Shape B (top of this file) — links and new pages do not fix a dead URL |
|
|
796
|
+
| `gsc.pages.unseen` rows whose `index` is not indexed | **indexing round**: internal links into them, sitemap `lastmod`, indexing requests; do not judge their keywords until a window after they are indexed. This row is also the batch gate (§7.3 gate 4): last batch mostly indexed → ship the next family; mostly `Crawled - currently not indexed` → no new pages this round, and retire what Google has refused twice |
|
|
797
|
+
| `rank.pending` non-empty | those words are not measured yet — no branch applies to them this round; do not read their zero as "missed" |
|
|
798
|
+
| `authority_limited` | placement only **on that theme** — another page on it will not move it (§6). The label is per keyword, not per site: production continues on every other theme. If every funded theme reads this way, the site is Shape C (top of this file): re-derive the ledger toward tool, long-tail and locale rows before pitching |
|
|
701
799
|
| `gsc.newQueries` non-empty | mini-discovery: fold them into the matrix, price the new cells, extend the tracked list if they survive the SERP check |
|
|
702
800
|
| no movers two rounds running *and* the placement queue is empty | the plan is stale, not slow — back to §1 and re-derive; more waiting will not convert a flat line |
|
|
703
801
|
|
|
@@ -720,7 +818,11 @@ your side of the bet, deliberately: it cannot measure whether a pitch was sent,
|
|
|
720
818
|
cannot measure is just a form for you to fill in. That state lives in the ledger or nowhere.
|
|
721
819
|
|
|
722
820
|
**Cadence**: SEO is a weeks-to-months system and the window `check` measures is 4 weeks wide —
|
|
723
|
-
checking daily just re-reads the same window. Every two weeks is plenty.
|
|
821
|
+
checking daily just re-reads the same window. Every two weeks is plenty. And a round that
|
|
822
|
+
re-reads an unchanged window must not re-decide: if `check` shows nothing new since the last
|
|
823
|
+
round (same window, no movers, no new queries), the round is execution of the existing ledger,
|
|
824
|
+
not a new analysis. tabledi 2026-09-02 re-ran discovery on the same data as 09-01 and flipped
|
|
825
|
+
its P0 — a recommendation that flips on identical data is not a reading, it is noise.
|
|
724
826
|
|
|
725
827
|
---
|
|
726
828
|
|
|
@@ -744,4 +846,5 @@ Generate the content (that's your job — clize is the hands, you're the brain),
|
|
|
744
846
|
directories or post to forums for you (manual, on purpose — though §6.2's outreach email loop
|
|
745
847
|
is fully in scope, because every send passes a human approval gate), or promise rankings. And
|
|
746
848
|
there is no automatic monitoring: measurement only pays off when someone can act on it, so a
|
|
747
|
-
recheck happens when you show up to run one.
|
|
849
|
+
recheck happens when you show up to run one. What it does count is pages: a round's deliverable
|
|
850
|
+
is shipped URLs and their index status, not a ledger (§3 test 6).
|