@clize/clize 0.33.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.
@@ -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
- keyword metrics, who occupies a results page, your positions, where your traffic comes
6
- from, what Search Console is showing you. This skill is the judgment layer: where the
7
- keywords to test come from in the first place, how big and how expensive one analysis is,
8
- what it has to deliver, which keywords are worth attacking at your authority level, what a
9
- results page is actually telling you, where to place content so it gets found, how to write
10
- so AI engines can quote you, and how to read a `seo check` without fooling yourself.
11
- Triggers: "do keyword research", "find keyword opportunities", "should I go after this
12
- keyword", "why aren't we ranking", "what does this SERP mean", "read my seo check", "how do
13
- I get cited by ChatGPT", "write this page for AI search", "where should I publish this",
14
- "is our SEO working", "build an SEO landing page", "build a free
15
- tool page", "how do we get backlinks", "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%. The
30
- single most common failure shape, measured on our own tenant zero: nine weeks of work, on-page
31
- at 120%, live referring domains at zero, every non-brand keyword flat at position 65+. If your
32
- plan is all pages and no placement, it is that plan.
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 failure of this product
40
- is not misreading a number, it is testing a keyword set that was never enumerated (§1).
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
- of them will hand you a word you did not think of — except `competitors`, and the `newQueries`
48
- that come back from `check`. So the quality ceiling of the whole analysis is set before you
49
- spend a cent, by how the candidate list was built.
50
-
51
- **Enumerate the list. Do not recall it.** A list you produced by thinking hard about the
52
- product is a sample of your own memory, and it will be small, English-only, and biased toward
53
- words you already believed in.
54
-
55
- One free source comes before all of the paid ones: **the entry read's own gsc face** (§10) —
56
- `gsc.topQueries` / `newQueries` are Google's list of what it already considers you relevant
57
- for. On a site with any history it is the cheapest, best-fitted word source there is; on a
58
- brand-new site it is empty, and reading it cost nothing.
59
-
60
- Measured case (kunavo.com, 2026-08-31). Round one: 19 keywords written down off the top of the
61
- head. Output: six thematic directions, zero rows anyone could start work on. Round two: a
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 **50–75 candidates for about $0.09 a domain**, and
75
- they are pre-filtered by the only filter that matters: somebody is already ranking on them.
76
- Read `topKeywords[].keyword`, ignore their brand terms, keep everything else as a candidate.
77
-
78
- Pick competitors that are *your size or one step up*. Tearing down a category leader gives you
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: *what can this product actually absorb a visitor for?* Then find the axes of that surface
88
- and take the cross-product. This is mechanical, and mechanical is the point.
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
- | Locale × head term | your two or three head terms | each market you can serve | `de-DE` pricing cluster |
99
-
100
- Rules for the matrix:
101
-
102
- - **Write every cell, then filter.** Filtering while enumerating is just recalling again.
103
- - **Do the non-English cells.** They are where the low-competition rows are, and they are the
104
- cells a memory-driven list always skips. Supported markets: `en-US, en-GB, en-CA, en-AU,
105
- de-DE, fr-FR, es-ES, ja-JP` — anything else is a 400, so do not build rows you cannot price.
106
- - **Cells you can't serve are not candidates.** Ranking for a query you cannot answer is a
107
- bounce, not a win.
108
- - 30–100 cells is a normal matrix. If you have twelve, you have not found your axes yet.
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 is already showing you for that are
113
- not on your list. This is Google telling you what it thinks you are relevant for, which is
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`, keep `attackable` + interesting `no_volume` (§5)
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
- 10–20 rows written into the ledger (§3)
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** | 1020 of these is $0.200.40 |
143
+ | `seo serp`, one keyword | **~$0.02** | every survivor gets one, `no_data` cells included — 20100 of these is $0.402.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 analysis,
159
- not one month; do not try to ration a monthly number. The `cap` block reflects this: normally
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/ | pitched: apidog roundup, 08-31 | comparison + pitch 8 roundups | P0 |
199
- | email api for ai agents | en-US | 0 (no_volume) | — | listicle_window | commercial | /inbox/ | shipped:/inbox/ | none | landing + pitch 4 roundups | P0 |
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, date>`
205
- / `listed:<where>`) say what actually got funded. **You maintain those two; no command does**,
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
- Five ways an analysis fails. The first is about *what you found*, the last about *what you
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 five have been observed in real runs that read as finished.
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.** Commercial rows (`<rival> alternative`,
320
- `X vs Y`, `pricing`, `best X for Y`) outrank informational rows (`what is X`) for priority:
321
- they sit at the bottom of the funnel, they convert, and conversion evidence is what justifies
322
- funding the next batch of pages. Fill the worksheet's `priority` column by intent first,
323
- volume second — a KD 8 commercial row beats a KD 5 informational row.
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 is worth *pricing*, not deciding on.
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. Directory/forum submissions stay manual the product produces the target list,
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, even if only under `unseen` |
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.** Tool UI translates almost for free,
477
- and the non-English cells of your matrix are the low-competition ones. Each locale page is a
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. **First batch ≤5 pages.** Enroll them (step ⑤), wait for a check that shows impressions,
496
- *then* fund the next batch. Never carpet-bomb 50 pages: it wastes money on unverified SERPs
497
- and it is exactly the shape Google's scaled-content-abuse enforcement targets. The line
498
- between a variant matrix and a content farm is: distinct real intent per page, and batch
499
- size throttled by measurement.
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. Measurement makes no
556
- upstream calls; the only thing `check` may pay for is pricing keywords it has never priced.
557
-
558
- - `position` is the **impression-weighted average over `rank.window`**, not your rank at a moment.
559
- A 62.4 means "across the window, the average slot Google gave you was ~62". Two consequences:
560
- a single good day barely moves it, and it is **not comparable** to a scraped SERP position.
561
- - `delta` is **positive when you moved up** (the number got smaller), measured against
562
- `rank.prevWindow` the equal-length window right before this one. `movers` shifted by 3+;
563
- anything smaller is averaging noise. `series` is weekly, newest first; weeks with no
564
- impressions are simply absent, so read the dates, not the spacing.
565
- - Each row also carries **`sv` and `kd` next to the Search Console numbers**. That is the whole
566
- point of the row: demand and delivery side by side. A row with `sv: 27100` and `position: 74`
567
- is a completely different situation from `sv: 0` and `position: 4.5`, and you should never
568
- have to go find the second number somewhere else to see it.
569
- - `unseen` lists the keywords on your list that **Search Console did not see this window**.
570
- This is not "not ranking" — GSC only has a row once you get impressions. It is the honest
571
- statement "no data yet", and it costs nothing to say. **If you need one of these words' real
572
- position, that is a separate, deliberate act: `clize seo serp <keyword>`** (~$0.02, and its
573
- `items` carry both your position and your competitors'). Do not ask for all of them: for a
574
- word with no demand signal yet, the answer is a slow, expensive way to keep reading "still
575
- not there".
576
- - `rank: null` means **this round had nothing to synthesise from** (no Search Console access, or
577
- no keyword list) — read `notes`. It is not "you rank nowhere".
578
- - **Competitor positions are not in `check`.** `--competitors` is remembered configuration only;
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 also carries **`topPage`** — the page of yours that took the most
584
- impressions on that query this window — and **`pages`**, how many distinct pages of yours got
585
- any impressions on it. Pure aggregate, no judgment: it is Google's answer to "which of your
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`, and `topPage` has a value anyway | Google picked a carrier for you, out of pages you built for something else | Go with its pick. Strengthen that page for the query — internal links, a section that answers it instead of building the page you had planned and splitting the signal in two. Cheapest double-down there is: the page already exists. |
592
- | **Mismatch** — `target page` is A, `topPage` is B | Page A is not being recognised as the answer | Structure problem, not a content problem: internal links and anchors into A, title/H1 alignment, whether A is indexed at all. Writing more words onto A is the reflex to resist — it is what you would do if you had never seen this field. |
593
- | **Self-cannibalisation** — `pages` > 1 | Several of your pages are splitting one query's impressions | Merge, or canonical one to the other. Two pages of yours are competing for one slot. |
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
- One caution on the third: `pages` is a count, not a split. `pages: 2` where one page takes
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
- `signals` is where the three sources are put side by side and labelled. It is **fact
602
- compression, not advice**the same kind of thing as `serp`'s `verdict`. Every row carries the
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.0three facts side by side, no judgment:
605
685
 
606
- | form | the configuration | how to read it |
686
+ | list | what is in it | the facts per row |
607
687
  | --- | --- | --- |
608
- | `pre_emergence` | priced volume is **0**, and Search Console shows real impressions anyway | The demand exists and the keyword tools have not caught up. On a low-authority site this is the cheapest entry you will find: nobody is optimising for a keyword the tools score as empty. Write the page. |
609
- | `authority_limited` | many impressions, position 50+, **zero clicks** | Google is offering you the keyword surface and withholding the position. This is an authority bottleneck, not a content problem — **another page on the same theme will not move it.** Go do §6.1. |
610
- | `demand_no_surface` | real search volume, and Search Console gave you **no impressions at all** this window | The mirror image. Google is not even putting you on the shelf. That is a **discovery** question (is this reachable at your authority? what occupies it? `seo serp`), not a measurement one. Running the measurement again will not answer it. |
611
-
612
- An empty `signals` array is not a failure; it means no keyword on your list is currently in one
613
- of these three shapes.
614
-
615
- **`demand_no_surface` is capped at the five highest-volume words**, and `notes` says how many
616
- more there are; the full list is `rank.unseen`. On a cold-start site nearly every tracked word
617
- is in this shape — that is the normal state, not a finding, and letting it fill `signals` would
618
- bury the two rare forms. If your `signals` is *all* `demand_no_surface`, the honest reading is
619
- "this site has no search presence yet", and the next move is discovery (§1), not measurement.
620
-
621
- There is deliberately **no** `data_conflict` form. That would need two independent position
622
- sources to disagree, and `check` has only one (Search Console). If you want the scraped-position
623
- comparison, that is an explicit `seo serp <keyword>` and when the two disagree, believe
624
- Search Console: it is a 28-day average of what Google actually served, while a scrape is one
625
- personalised fetch of a rotating page.
626
-
627
- ### `keywords.byBand`
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
- Counts **only keywords that have been priced**. Words on your list that have never been priced
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
- ### `gsc`
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
- - `newQueries` is the feedback loop closing (§1.3): Google telling you which queries it
645
- *considers* you relevant for. Queries you never targeted showing real impressions are the most
646
- valuable output of the whole command. Fold them into the matrix.
647
- - `gsc: null` is not an error. Read `notes`usually it means the service account has not been
648
- added to that property yet.
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
- When a row shows `sv` in the thousands, `position` around 70, hundreds of impressions and almost
653
- no clicks, and `serp` on that keyword says `listicle_window` that is a complete, actionable
654
- picture: **the demand exists, the position is authority-limited, and the way in is the roundups,
655
- not another page.** `check` now labels the middle of that sentence for you (`authority_limited`);
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 ≤5 pages through the §7 pipeline, 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 |
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
- | `authority_limited` | placement only; skip discovery *and* production another page on the theme will not move it (§6) |
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).