@gobing-ai/knowledge-kit 0.0.37 → 0.1.1

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.
Files changed (157) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/dist/board/collect/index.css +654 -16
  3. package/dist/board/collect/index.js +191 -64
  4. package/dist/board/daily-voice/index.css +679 -12
  5. package/dist/board/daily-voice/index.js +185 -66
  6. package/dist/board/itc/index.css +679 -13
  7. package/dist/board/itc/index.js +188 -66
  8. package/dist/index.js +2864 -139
  9. package/package.json +3 -2
  10. package/plugins/generations/content-gen/dist/index.js +158 -12
  11. package/plugins/generations/core-facts-gen/dist/index.js +158 -12
  12. package/plugins/generations/daily-article-gen/dist/index.js +158 -12
  13. package/plugins/generations/dailynews-gen/dist/index.js +158 -12
  14. package/plugins/generations/episode-plan-gen/dist/index.js +158 -12
  15. package/plugins/generations/gemini-tts/README.md +4 -3
  16. package/plugins/generations/gemini-tts/dist/index.js +158 -12
  17. package/plugins/generations/image-gen/dist/index.js +158 -12
  18. package/plugins/generations/news-report-gen/dist/index.js +158 -12
  19. package/plugins/generations/omni-voice-gen/README.md +15 -4
  20. package/plugins/generations/omni-voice-gen/profiles.json +1 -2
  21. package/plugins/generations/voice-gen/dist/index.js +158 -12
  22. package/plugins/generations/voxcpm-tts/README.md +21 -1
  23. package/plugins/generations/voxcpm-tts/dist/index.js +173 -13
  24. package/plugins/generations/voxcpm-tts/scripts/audit-speaker-consistency.py +109 -0
  25. package/plugins/generations/voxcpm-tts/src/voxcpm-client.ts +28 -1
  26. package/plugins/ingestions/aihot-ingest/dist/index.js +158 -12
  27. package/plugins/ingestions/horizon-ingest/dist/index.js +158 -12
  28. package/plugins/ingestions/last30days-ingest/dist/index.js +158 -12
  29. package/plugins/ingestions/web-search/dist/index.js +158 -12
  30. package/plugins/kk/commands/workflow-run.md +56 -18
  31. package/plugins/kk/decisions/kk-itc.yaml +46 -0
  32. package/plugins/kk/decisions/kk-seo-keyword.yaml +57 -0
  33. package/plugins/kk/decisions/kk-seo-page.yaml +230 -0
  34. package/plugins/kk/decisions/kk-seo-pair.yaml +21 -0
  35. package/plugins/kk/decisions/kk-seo-site.yaml +90 -0
  36. package/plugins/kk/plugin.json +1 -1
  37. package/plugins/kk/publish-presets/chinese-article.json +14 -0
  38. package/plugins/kk/publish-presets/technical-article.json +14 -0
  39. package/plugins/kk/references/smart-seo-attribution.md +85 -0
  40. package/plugins/kk/scripts/daily-stages.ts +42 -2
  41. package/plugins/kk/scripts/itc-stages.ts +25 -118
  42. package/plugins/kk/scripts/kk-workflow-stages.ts +38 -1
  43. package/plugins/kk/scripts/seo-decisions.ts +491 -0
  44. package/plugins/kk/scripts/seo-enrich.ts +728 -0
  45. package/plugins/kk/scripts/seo-facts.ts +610 -0
  46. package/plugins/kk/scripts/seo-fetch.ts +766 -0
  47. package/plugins/kk/scripts/seo-report.ts +460 -0
  48. package/plugins/kk/scripts/seo-rules.ts +1882 -0
  49. package/plugins/kk/scripts/seo-stages.ts +1044 -0
  50. package/plugins/kk/skills/article-adapt/SKILL.md +2 -2
  51. package/plugins/kk/skills/smart-seo/SKILL.md +183 -0
  52. package/plugins/kk/skills/smart-seo/references/method.md +126 -0
  53. package/plugins/kk/skills/smart-seo/references/narrative.md +125 -0
  54. package/plugins/kk/skills/smart-seo/references/operations.md +209 -0
  55. package/plugins/kk/skills/smart-seo/references/seo-geo-sources.md +90 -0
  56. package/plugins/kk/workflows/feature-verification.yaml +28 -24
  57. package/plugins/kk/workflows/kk-daily-ai-voice2.yaml +85 -17
  58. package/plugins/kk/workflows/kk-itc-author.yaml +406 -0
  59. package/plugins/kk/workflows/kk-itc.yaml +208 -682
  60. package/plugins/kk/workflows/kk-smart-seo.yaml +251 -0
  61. package/plugins/publishings/bilibili-pub/dist/index.js +21782 -0
  62. package/plugins/publishings/bilibili-pub/dist/profile-check.js +20410 -0
  63. package/plugins/publishings/bilibili-pub/package.json +20 -0
  64. package/plugins/publishings/bilibili-pub/plugin.json +12 -0
  65. package/plugins/publishings/bilibili-pub/src/flow.ts +1296 -0
  66. package/plugins/publishings/bilibili-pub/src/index.ts +139 -0
  67. package/plugins/publishings/bilibili-pub/src/profile-check.ts +19 -0
  68. package/plugins/publishings/bilibili-pub/src/selectors.ts +222 -0
  69. package/plugins/publishings/bilibili-pub/tsconfig.json +4 -0
  70. package/plugins/publishings/cnblogs-pub/dist/index.js +21383 -0
  71. package/plugins/publishings/cnblogs-pub/dist/profile-check.js +20412 -0
  72. package/plugins/publishings/cnblogs-pub/package.json +19 -0
  73. package/plugins/publishings/cnblogs-pub/plugin.json +12 -0
  74. package/plugins/publishings/cnblogs-pub/src/flow.ts +708 -0
  75. package/plugins/publishings/cnblogs-pub/src/index.ts +172 -0
  76. package/plugins/publishings/cnblogs-pub/src/profile-check.ts +24 -0
  77. package/plugins/publishings/cnblogs-pub/src/selectors.ts +104 -0
  78. package/plugins/publishings/cnblogs-pub/tsconfig.json +4 -0
  79. package/plugins/publishings/csdn-pub/dist/index.js +21552 -0
  80. package/plugins/publishings/csdn-pub/dist/profile-check.js +20963 -0
  81. package/plugins/publishings/csdn-pub/package.json +19 -0
  82. package/plugins/publishings/csdn-pub/plugin.json +12 -0
  83. package/plugins/publishings/csdn-pub/src/index.ts +1178 -0
  84. package/plugins/publishings/csdn-pub/src/profile-check.ts +23 -0
  85. package/plugins/publishings/csdn-pub/tsconfig.json +4 -0
  86. package/plugins/publishings/emdash-pub/dist/index.js +158 -12
  87. package/plugins/publishings/emdash-pub/plugin.json +22 -1
  88. package/plugins/publishings/infoq-pub/dist/index.js +160 -12
  89. package/plugins/publishings/infoq-pub/dist/profile-check.js +160 -12
  90. package/plugins/publishings/infoq-pub/plugin.json +21 -0
  91. package/plugins/publishings/jike-pub/dist/index.js +21379 -0
  92. package/plugins/publishings/jike-pub/dist/profile-check.js +20951 -0
  93. package/plugins/publishings/jike-pub/package.json +19 -0
  94. package/plugins/publishings/jike-pub/plugin.json +12 -0
  95. package/plugins/publishings/jike-pub/src/index.ts +800 -0
  96. package/plugins/publishings/jike-pub/src/profile-check.ts +21 -0
  97. package/plugins/publishings/jike-pub/tsconfig.json +4 -0
  98. package/plugins/publishings/juejin-pub/dist/index.js +160 -12
  99. package/plugins/publishings/juejin-pub/dist/profile-check.js +160 -12
  100. package/plugins/publishings/juejin-pub/plugin.json +21 -0
  101. package/plugins/publishings/medium-pub/dist/index.js +158 -12
  102. package/plugins/publishings/medium-pub/plugin.json +22 -1
  103. package/plugins/publishings/podcast-pub/dist/index.js +158 -12
  104. package/plugins/publishings/qiita-pub/dist/index.js +158 -12
  105. package/plugins/publishings/qiita-pub/plugin.json +24 -1
  106. package/plugins/publishings/shipinhao-pub/dist/index.js +21715 -0
  107. package/plugins/publishings/shipinhao-pub/dist/profile-check.js +20431 -0
  108. package/plugins/publishings/shipinhao-pub/package.json +19 -0
  109. package/plugins/publishings/shipinhao-pub/plugin.json +12 -0
  110. package/plugins/publishings/shipinhao-pub/src/flow.ts +1323 -0
  111. package/plugins/publishings/shipinhao-pub/src/index.ts +173 -0
  112. package/plugins/publishings/shipinhao-pub/src/profile-check.ts +25 -0
  113. package/plugins/publishings/shipinhao-pub/src/selectors.ts +241 -0
  114. package/plugins/publishings/shipinhao-pub/tsconfig.json +4 -0
  115. package/plugins/publishings/sspai-pub/dist/index.js +21371 -0
  116. package/plugins/publishings/sspai-pub/dist/profile-check.js +20414 -0
  117. package/plugins/publishings/sspai-pub/package.json +19 -0
  118. package/plugins/publishings/sspai-pub/plugin.json +12 -0
  119. package/plugins/publishings/sspai-pub/src/flow.ts +624 -0
  120. package/plugins/publishings/sspai-pub/src/index.ts +167 -0
  121. package/plugins/publishings/sspai-pub/src/profile-check.ts +25 -0
  122. package/plugins/publishings/sspai-pub/src/selectors.ts +122 -0
  123. package/plugins/publishings/sspai-pub/tsconfig.json +4 -0
  124. package/plugins/publishings/substack-pub/dist/index.js +160 -12
  125. package/plugins/publishings/substack-pub/dist/profile-check.js +160 -12
  126. package/plugins/publishings/substack-pub/plugin.json +21 -0
  127. package/plugins/publishings/surfdash-pub/dist/index.js +158 -12
  128. package/plugins/publishings/surfdash-pub/plugin.json +22 -1
  129. package/plugins/publishings/threads-pub/dist/index.js +158 -12
  130. package/plugins/publishings/wechatmp-pub/dist/index.js +1669 -502
  131. package/plugins/publishings/wechatmp-pub/dist/profile-check.js +204 -21
  132. package/plugins/publishings/wechatmp-pub/plugin.json +32 -0
  133. package/plugins/publishings/wechatmp-pub/src/cgi-draft.ts +540 -0
  134. package/plugins/publishings/wechatmp-pub/src/cgi-media.ts +380 -0
  135. package/plugins/publishings/wechatmp-pub/src/cgi.ts +485 -0
  136. package/plugins/publishings/wechatmp-pub/src/flow.ts +104 -0
  137. package/plugins/publishings/weibo-pub/dist/index.js +160 -12
  138. package/plugins/publishings/weibo-pub/dist/profile-check.js +160 -12
  139. package/plugins/publishings/weibo-pub/plugin.json +35 -0
  140. package/plugins/publishings/x-pub/dist/index.js +160 -12
  141. package/plugins/publishings/x-pub/dist/profile-check.js +160 -12
  142. package/plugins/publishings/x-pub/plugin.json +40 -0
  143. package/plugins/publishings/xhs-pub/dist/index.js +160 -12
  144. package/plugins/publishings/xhs-pub/dist/profile-check.js +160 -12
  145. package/plugins/publishings/xhs-pub/plugin.json +37 -0
  146. package/plugins/publishings/xiaoyuzhou-pub/dist/index.js +21491 -0
  147. package/plugins/publishings/xiaoyuzhou-pub/dist/profile-check.js +20417 -0
  148. package/plugins/publishings/xiaoyuzhou-pub/package.json +19 -0
  149. package/plugins/publishings/xiaoyuzhou-pub/plugin.json +12 -0
  150. package/plugins/publishings/xiaoyuzhou-pub/src/flow.ts +826 -0
  151. package/plugins/publishings/xiaoyuzhou-pub/src/index.ts +174 -0
  152. package/plugins/publishings/xiaoyuzhou-pub/src/profile-check.ts +26 -0
  153. package/plugins/publishings/xiaoyuzhou-pub/src/selectors.ts +125 -0
  154. package/plugins/publishings/xiaoyuzhou-pub/tsconfig.json +4 -0
  155. package/plugins/publishings/zenn-pub/dist/index.js +158 -12
  156. package/plugins/publishings/zenn-pub/plugin.json +22 -1
  157. package/plugins/generations/omni-voice-gen/voices/robin-news.wav +0 -0
@@ -9,8 +9,8 @@ description: This skill should be used when a kk-itc publish plan contains varia
9
9
 
10
10
 
11
11
  This skill adapts one published article into one Content file per `publish-plan.json` variant with
12
- `adapt: true`. It is deliberately **thin**: the plan (built from `TARGET_PROFILES` in
13
- `plugins/kk/scripts/itc-stages.ts`) carries the per-variant locale, format and limits — this skill
12
+ `adapt: true`. It is deliberately **thin**: the plan (built by `buildItcPublishPlan` in
13
+ `apps/cli/src/itc-plan.ts` from each publish manifest's declared profiles) carries the per-variant locale, format and limits — this skill
14
14
  holds **no platform table** and never decides which variants exist.
15
15
 
16
16
  ## Inputs
@@ -0,0 +1,183 @@
1
+ ---
2
+ name: smart-seo
3
+ description: >-
4
+ Run, review and interpret a read-only Smart SEO/GEO audit and turn its evidence into a prioritized
5
+ action plan. Use when the user says "audit this site for SEO", "run a smart SEO audit", "review
6
+ this saved audit", "explain these SEO findings", or "check GEO readiness". Read-only results: no
7
+ site mutation, no ranking and no citation promises.
8
+ version: 0.1.0
9
+ ---
10
+
11
+ # smart-seo — evidence-grounded SEO/GEO audits
12
+
13
+ One entry point for the whole audit lifecycle: intake → run → review evidence → interpret →
14
+ narrative → offline rescoring. There is no second command, subagent or plugin kind; this skill plus
15
+ `plugins/kk/workflows/kk-smart-seo.yaml` and the `kk stage run seo-*` stages are the entire surface.
16
+
17
+ ## 0. The contract — state it before you report
18
+
19
+ - **Read-only.** The audit fetches public pages and writes artifacts under `.works/`; it never edits
20
+ the site, submits URLs, changes accounts, publishes or schedules anything. Remediation is an
21
+ operator action you may recommend, never perform.
22
+ - **No ranking, citation, traffic or revenue promise.** The audit cannot observe indexing, ranking,
23
+ visits, revenue, bot behavior or answer-engine citations. Say "not measured" where that is true.
24
+ - **Label the evidence class** in every statement: observed **measurement**, **heuristic**,
25
+ **unknown**, **model judgment / advisory**, or **consultation**. `references/method.md` §1.
26
+ - **Crawler purposes stay separate:** search-indexing, model-training and user-triggered fetchers
27
+ have different policies and different consequences. `references/seo-geo-sources.md` §2.
28
+ - **`vendors/` is reference-only:** excluded from runtime and install resolution; never read or copy
29
+ it into a shipped file, and never wrap the vendor Python.
30
+ - **Specialist advice is consultation.** Legal, hosting, server, analytics or i18n work outside the
31
+ collected evidence is flagged as consultation with the missing evidence named — never presented
32
+ as an audit finding.
33
+ - **No phantom metrics.** Every number in your answer comes from an artifact field, and you name it.
34
+
35
+ ## 1. Route the request
36
+
37
+ | Input | Path |
38
+ | --- | --- |
39
+ | A public URL / domain | Full workflow run (§3→§4) with a written options file. |
40
+ | A saved audit directory (`audit.json` present) | Skip to review/interpret (§5→§6); rescore with §8 when the rubric or inputs changed. |
41
+ | A question about an existing report | Read `report.json` + `audit.json` + `checks.json`, answer from the fields (§5). |
42
+ | "Make the site rank / get cited" | Explain what the evidence does and does not support (§0, §6) and produce the action plan. |
43
+
44
+ Never invent a run id, work directory or artifact path. Resolve them from the run-bound file
45
+ `.spur/run/<runId>-seo-work-dir.txt`, and read the package root from the running executable
46
+ (`references/operations.md` §1) — never from a hard-coded checkout path.
47
+
48
+ ## 2. Intake and the options file
49
+
50
+ Confirm with the operator: the exact URL, the intended audience locale, any business context worth
51
+ recording (≤ 2,000 characters — operator context, not website evidence), whether measured
52
+ performance evidence is desired (PSI/CrUX keys present), and whether an imported visibility
53
+ snapshot exists. Then write a strict JSON options file — fields, defaults, bounds and the imported
54
+ snapshot contract are in `references/operations.md` §3, §5. Pass the file **path** as data; never
55
+ template a user value into shell source, and refuse NUL input.
56
+
57
+ ## 3. Register the decision catalogs (integrated runs)
58
+
59
+ The workflow's preflight passes `require_decisions=true`: the four SEO catalogs (22 decisions) must
60
+ be served, or preflight fails with the registration pointer. The supported setup is an **append** of
61
+ `<package-root>/plugins/kk/decisions` to the consumer project's `decisions.paths`, preserving every
62
+ existing entry and every maker setting. Verify with `spur decision list|show|status --json` — that
63
+ is configuration evidence, never model health. Exact steps and failure text:
64
+ `references/operations.md` §2. Collection-only stage use and the offline fixture mode need no
65
+ catalogs.
66
+
67
+ ## 4. Run it, bounded
68
+
69
+ ```bash
70
+ spur workflow run <package-root>/plugins/kk/workflows/kk-smart-seo.yaml \
71
+ --vars '{"options_file":"/abs/path/options.json"}' --quiet
72
+ ```
73
+
74
+ `preflight → collect → check → enrich → judge → score → narrative → report`, terminal
75
+ `done | partial | failed`. Leave `work_dir` empty so preflight allocates it and records the
76
+ run-bound path. Show progress from the workflow trace and the run record, not by guessing a
77
+ directory; report `partial` as successful delivery with named missing evidence, and `failed` as the
78
+ only failure state. Full command/artifact/progress detail: `references/operations.md` §6–§8.
79
+ For a run with no network access (fixture mode), the same workflow executes offline — say so
80
+ explicitly and never present fixture-derived numbers as a live site audit.
81
+
82
+ ## 5. Review the evidence before reading any judgment
83
+
84
+ 1. `report.json` — `status`, `narrativeStatus`, `scores`, `coverage`, `actions` (already ordered).
85
+ 2. Start with `P1`, then `P2` actions. For each: severity, category, affected pages, `effort` band,
86
+ and `heuristic` (true = rule-of-thumb over observed facts, false = directly observed).
87
+ 3. Confirm each action against `checks.json`: `status`, `reason`, `evidenceRefs`, `sourceUrl`,
88
+ `sourceCheckedAt`. A failing row with a `heuristic: true` flag is a hypothesis to verify, not a
89
+ defect.
90
+ 4. Read `audit.json` `/limits/*` and `coverage` (`assessed`, `unknown`, `ratio`, `hitLimit`). A
91
+ limit or a large `unknown` count bounds every conclusion; orphan/depth/duplicate observations are
92
+ "among sampled pages".
93
+ 5. Treat policy-looking rows carefully: robots directives, intentional `noindex`, canonical-to-other
94
+ and missing optional files (`robots_missing`, `sitemap_missing`, `not_in_sitemap`,
95
+ `llms_txt_missing`) are **signals requiring operator intent**, not automatic defects.
96
+ 6. When a row's `sourceCheckedAt` is old relative to the question, refresh the primary source first
97
+ (`references/seo-geo-sources.md` §3) instead of asserting behavior from memory.
98
+
99
+ ## 6. Interpret for the operator — per area
100
+
101
+ Explain each area from the recorded evidence and stop where the evidence stops. Rubric, scoring and
102
+ the full "cannot conclude" list live in `references/method.md` §3 and §6.
103
+
104
+ | Area | What the evidence supports | What it must not become |
105
+ | --- | --- | --- |
106
+ | **Technical** | Observed HTTP status/redirect chains, canonical and hreflang tags, indexability directives, headers, HTTPS/host variants, response size and timings. | Proof of indexing, rendering or crawl-budget impact. `js_dependent` is raw-HTML shell suspicion only — no browser rendering. |
107
+ | **On-page** | Title/meta/H1–H3/outline/link/anchor/image facts, structured-data syntax and type inventory, Open Graph presence. | A ranking explanation. Length thresholds are heuristics; structured data is data, and its presence is not an AI or rich-result eligibility requirement. |
108
+ | **Content / editorial** | Visible word counts (an editorial measure, language-limited), duplicate/thin-content observations, sampled text. | A quality verdict or an audience judgment. Model judgments (`source: model`) are advisory with a maker and a confidence. |
109
+ | **Local** | Declared locale/origin facts, contact and business-entity signals, `kk-seo-local-intent`. | Local-pack or maps presence claims — not measured. |
110
+ | **Ecommerce** | Product/category page-type and action classifications, duplicated titles/meta, canonical and redirect observations on product URLs. | Price, feed or merchant-center diagnostics — out of scope; name that as consultation. |
111
+ | **International** | hreflang observations, locale declarations, duplicate-content observations across host/path variants. | A statement that a market does or does not receive traffic. Machine-translation quality is consultation. |
112
+ | **GEO (generative engines)** | Editorial diagnostics — intent satisfaction, first-hand specificity, verifiable provenance, entity consistency, answer-first structure — plus imported citation/visibility snapshots when supplied. | Any citation, mention or inclusion promise. Blocking a training crawler is a policy choice, not a search defect. |
113
+
114
+ ### Crawler policies, kept separate
115
+
116
+ Explain a robots observation by the crawler's **purpose**, never as one verdict, and report the
117
+ observed directive plus the path it applies to:
118
+
119
+ - **Search indexing** (`Googlebot`, `Bingbot`, `OAI-SearchBot`) — affects eligibility for search and
120
+ search-grounded answer surfaces. A deliberate restriction is an operator decision, not a defect.
121
+ - **Model training** (`GPTBot`, `Google-Extended`, `ClaudeBot`, `CCBot`) — a data-policy choice.
122
+ Blocking it is not a search defect; allowing it promises nothing.
123
+ - **User-triggered fetch** (`ChatGPT-User` and similar) — serves a user's explicit request; a block
124
+ limits that session, not search eligibility.
125
+
126
+ A router robots group (the audit's own user agent) is likewise an operator policy. Never claim what
127
+ any crawler actually did: the audit observes the site's declared policy and its own requests only.
128
+
129
+ ### When evidence is missing
130
+
131
+ Say which of the three it is — **not collected** (outside the bounded crawl), **collected but
132
+ unknown** (`status: unknown`), or **not applicable** (`not-applicable`) — and name the input that
133
+ would settle it (a larger budget, a supplied visibility snapshot, an operator answer about intent).
134
+ Consultation is the right label for work this method cannot evidence at all.
135
+
136
+ ## 7. Write the narrative
137
+
138
+ Two different artifacts — keep them apart:
139
+
140
+ - **Validated narrative** (`narrative.json`): the §9 schema — allow-listed headings, fixed
141
+ `fact | action | limit` templates and `evidenceRef` pointers into the materialized `audit.json`,
142
+ bound to its sha256 and run id. Selecting and ordering recorded references is allowed; adding a
143
+ measurement, diagnosis or prediction is not, and any invalid claim rejects the whole narrative
144
+ (which then degrades to the labeled deterministic summary). Schema, worked example and rejection
145
+ cases: `references/narrative.md`.
146
+ - **Advisory prose** (your answer): the explanation, prioritization and operator-facing advice.
147
+ Label evidence classes, cite artifact fields, mark consultation, and describe what the report
148
+ deliberately cannot say. The report accepts no free prose — `references/narrative.md` §4.
149
+
150
+ ## 8. Offline rescoring
151
+
152
+ To re-score a saved audit without touching the network, the source run or its artifacts:
153
+
154
+ ```bash
155
+ kk stage run seo-score <saved-work_dir> <new-revision_dir> # revision dir must be absent/empty
156
+ kk stage run seo-report <new-revision_dir>
157
+ ```
158
+
159
+ The revision copies the validated inputs beside a fresh `audit.json`, keeps the original `runId`
160
+ and adds a `revisionId`, so source artifacts stay immutable and two revisions are comparable as
161
+ durable directories. Never compare scores across runs with different coverage or `methodVersion`,
162
+ and never patch a saved artifact in place. Details: `references/operations.md` §9.
163
+
164
+ ## 9. Failures and limits — report, never paper over
165
+
166
+ Unserved catalogs, invalid options, a populated `work_dir`, a refused network target
167
+ (local/reserved address), budget exhaustion, unavailable optional PSI/CrUX evidence, maker
168
+ fallback/low confidence, malformed predecessors, a rejected narrative and cancellation each have a
169
+ defined handling in `references/operations.md` §8. Three habits matter most: never present an
170
+ exhausted crawl as complete, never turn an unknown or a low-confidence judgment into a defect, and
171
+ never convert "not measured" into "passed".
172
+
173
+ ## 10. References
174
+
175
+ | Reference | Owns |
176
+ | --- | --- |
177
+ | `references/operations.md` | Package-root resolution, catalog registration and failure text, options fields, imported snapshot, run/progress commands, per-artifact map, failure handling, offline rescoring. |
178
+ | `references/method.md` | Evidence classes, the 52-rule checklist structure, score/coverage formulas, action ordering and priorities, how to read a check back to its evidence, and the method's explicit limits. |
179
+ | `references/seo-geo-sources.md` | Primary source registry with checked-at dates and per-source limits, crawler-purpose separation, the source refresh procedure, GEO limits, and the `vendors/` reference-only rule. |
180
+ | `references/narrative.md` | The validated narrative schema, worked example, rejection matrix, and the boundary with advisory prose. |
181
+
182
+ Attribution for the adapted rubric, rule identities and upstream notice:
183
+ `plugins/kk/references/smart-seo-attribution.md`.
@@ -0,0 +1,126 @@
1
+ # Smart SEO — method, rubric and score interpretation
2
+
3
+ The rubric is a **versioned internal checklist**, not a vendor grade and not a search-engine
4
+ verdict. Every claim below is what the artifacts record; nothing here measures indexing, ranking,
5
+ traffic, revenue, bot behavior or citation visibility.
6
+
7
+ ## 1. Evidence classes — label them every time you report
8
+
9
+ | Class | Meaning | Recorded where |
10
+ | --- | --- | --- |
11
+ | **Measurement / observed fact** | A value the audit actually observed within its stated budget (HTTP status, title text, robots directive, response bytes, timings). | `crawl.json` page facts, `checks.json` row `evidenceRefs`, `audit.json` `/facts/*` |
12
+ | **Heuristic** | A bounded rule-of-thumb over observed facts. `heuristic: true` on the check row and on the derived action. Examples: page depth, thin content (visible word count), title length, heading skips, orphan/depth observations "among sampled pages". | `checks.json` / `audit.json` `heuristic` |
13
+ | **Unknown** | Not assessed or not assessable: malformed, unavailable, budget-exhausted, blocked, or outside the collected scope. Never rendered as a pass. | `checks.json` status `unknown`, `coverage.unknown`, `audit.json` `/limits/*` |
14
+ | **Model judgment** | An editorial output from a typed decision (`source: model`, with maker and confidence). Advisory, never a measurement. | `judgments.json`, `audit.json` `/judgments` |
15
+ | **Imported evidence** | A supplied visibility snapshot or PSI/CrUX measurement, with source, collection window and `measurementKind`. | `performance.json`, `visibility.json` |
16
+ | **Consultation** | Advice outside the collected evidence (specialist legal/hosting/i18n/analytics work). Say so explicitly and name the missing evidence. | nowhere — it must not enter the report as a finding |
17
+
18
+ Rule of thumb: a value is a **measurement** only if some artifact records it with a source; a
19
+ judgment, an inference or a threshold over content is a **heuristic** or **advisory** statement.
20
+
21
+ ## 2. Checklist structure
22
+
23
+ 52 deterministic rules across five categories — `technical` (22), `content` (16), `crawl` (8),
24
+ `security` (4), `geo` (2). Each rule keeps a stable identity, a severity, an effort band, a
25
+ `heuristic` flag, a primary `sourceUrl` and a `sourceCheckedAt` date, and one disposition from the
26
+ vendor review (`native`, `adapted`, `informational`, `deferred`).
27
+
28
+ Every eligible rule/subject pair yields exactly **one** row:
29
+
30
+ ```json
31
+ {
32
+ "ruleId": "title_missing",
33
+ "subjectId": "<hashed subject identity>",
34
+ "subjectUrl": "https://example.com/a",
35
+ "status": "fail",
36
+ "category": "content",
37
+ "severity": "high",
38
+ "evidenceRefs": ["https://example.com/a"],
39
+ "sourceUrl": "https://developers.google.com/search/docs/appearing-in-search/...",
40
+ "sourceCheckedAt": "2026-10-14",
41
+ "heuristic": false,
42
+ "reason": "Human-readable reason for this status"
43
+ }
44
+ ```
45
+
46
+ `status` is `pass | fail | unknown | not-applicable`. **The absence of a row is an error, never a
47
+ pass.** `not-applicable` means the rule cannot apply to that subject (for example a site-level rule
48
+ against a page-level subject); it is excluded from both the score and the coverage denominator.
49
+ A `deferred`-disposition rule applies but is not assessed in this method version, so it emits
50
+ `unknown` and counts in `coverage.unknown`.
51
+
52
+ Intent-sensitive observations are not defects by themselves: a robots directive, an intentional
53
+ `noindex`, a canonical pointing elsewhere, a missing optional file or missing optional metadata all
54
+ require operator intent before they become a recommendation. Missing SEO files never turn into
55
+ indexing requirements in the narrative.
56
+
57
+ ## 3. Scores
58
+
59
+ Category score = `round(100 * pass / (pass + fail))` over the **applicable assessed** rows of that
60
+ category, and `null` when that category has no assessed row:
61
+
62
+ | Field | Meaning |
63
+ | --- | --- |
64
+ | `scores.technical` / `content` / `crawl` / `security` | The checklist pass ratio in that category. `null` = undefined denominator, never 0. |
65
+ | `coverage.totalRules` | 52 (the checklist size). |
66
+ | `coverage.applicableRules` | Rows whose status is not `not-applicable`. |
67
+ | `coverage.assessed` | Rows with `pass` or `fail`. |
68
+ | `coverage.unknown` | Applicable rows left `unknown`. |
69
+ | `coverage.ratio` | `assessed / (assessed + unknown)` — how much of the applicable checklist was actually assessed. |
70
+
71
+ Interpretation rules:
72
+
73
+ - There is **no blended overall SEO/GEO grade** in this method version. Report the four category
74
+ ratios with their coverage side by side; a ratio published without its coverage is misleading.
75
+ - `geo` rules are assessed and prioritized like any other row but produce no category score.
76
+ - `informational` rules (vendor-review disposition) are reported but never enter a category score:
77
+ they neither deduct on `fail` nor pad the ratio on `pass`.
78
+ - Unknown and review values never count as successes, and a disabled or unavailable optional area
79
+ is reported as such rather than folded into a pass.
80
+ - Never compare scores across two runs with different coverage, different `methodVersion`, or
81
+ different options — record the denominators with the number.
82
+ - A high score means "few sampled rules failed", not "the site is healthy". A low score does not
83
+ prove a traffic or ranking problem.
84
+
85
+ ## 4. Action ordering and priorities
86
+
87
+ One action per failing rule, carrying the full affected-subject list (aggregation must not hide
88
+ unknown subjects). Stable ID `SEO-<ruleId>-<first12(sha256(subject identity))>` — sorting never
89
+ renumbers an ID, so IDs stay comparable across revisions.
90
+
91
+ Order: severity (`critical`, `high`, `medium`, `low`, `info`) → affected-page count descending →
92
+ effort band ascending → stable ID.
93
+
94
+ | Priority | Meaning |
95
+ | --- | --- |
96
+ | `P1` | Confirmed `critical` or `high` severity failures. |
97
+ | `P2` | Confirmed `medium` severity failures. |
98
+ | `P3` | Confirmed low-severity failures. |
99
+ | `review` | Uncertain items — kept out of fix-first lists until the operator confirms intent or supplies missing evidence. |
100
+
101
+ `effort` is a declared planning band (1–4), not a quote or an estimate in hours. Severity expresses
102
+ the rule's consequence, not its certainty; read it together with `heuristic` and with `coverage`.
103
+ Editorial (content/GEO) scores can justify a review suggestion, never confirm a technical fact.
104
+
105
+ ## 5. Reading a check back to its evidence
106
+
107
+ 1. Take the action's `ruleId`, `affectedSubjects` and stable ID.
108
+ 2. Open `checks.json` and select the rows with that `ruleId`; read `status`, `reason`,
109
+ `evidenceRefs`, `sourceUrl` and `sourceCheckedAt`.
110
+ 3. Read the underlying page facts in `crawl.json` (or the referenced `evidence/pages/*.json`).
111
+ 4. Read `audit.json` `/limits/*` and `coverage` before drawing any conclusion: a limit or a high
112
+ `unknown` count bounds what the finding can mean.
113
+ 5. If the primary source page has changed since `sourceCheckedAt`, refresh the source before
114
+ relying on the rule's stated rationale (see `seo-geo-sources.md`).
115
+
116
+ ## 6. What this method cannot conclude
117
+
118
+ - Whether a URL is indexed, ranking, receiving traffic or being cited by any AI answer engine.
119
+ - What a crawler actually did: the audit observes the site's **declared** policy
120
+ (robots.txt, meta robots, sitemap) and its own requests — never another crawler's behavior.
121
+ - How a browser renders a JavaScript-dependent page: rendering is deferred, so a raw-HTML shell is
122
+ suspicion only.
123
+ - Whether an editorial judgment is correct: adequacy of a value proposition, content depth or
124
+ citability is an opinion with a maker and a confidence, not a fact.
125
+ - Performance causes or fixes: PSI lab metrics and CrUX field data, when supplied, describe
126
+ observed timing, not the work needed to change it.
@@ -0,0 +1,125 @@
1
+ # Smart SEO — the validated narrative contract
2
+
3
+ The narrative is **optional** and **fully validated**. It selects and orders references into the
4
+ materialized `audit.json`; it cannot introduce a measurement, unit, diagnosis or prediction. When
5
+ it is absent or rejected the report is still delivered with `narrativeStatus: omitted|rejected`
6
+ plus a deterministic summary — an optional agent failure is never an audit failure.
7
+
8
+ ## 1. Schema (schema version 1)
9
+
10
+ ```json
11
+ {
12
+ "schemaVersion": 1,
13
+ "runId": "<audit.runId>",
14
+ "auditDigest": "<sha256 of the exact audit.json bytes>",
15
+ "sections": [
16
+ {
17
+ "heading": "summary | priorities | limits",
18
+ "claims": [
19
+ { "template": "fact | action | limit", "evidenceRef": { "artifact": "audit.json", "jsonPointer": "/facts/<key> | /actions/<id> | /limits/<key>" } }
20
+ ]
21
+ }
22
+ ]
23
+ }
24
+ ```
25
+
26
+ Hard limits and exact shapes — every key below is the *only* accepted key at its level:
27
+
28
+ | Rule | Value |
29
+ | --- | --- |
30
+ | Root keys | exactly `schemaVersion`, `runId`, `auditDigest`, `sections` |
31
+ | `schemaVersion` | `1` |
32
+ | `runId` | must equal the audit's `runId` |
33
+ | `auditDigest` | must equal the sha256 of the materialized `audit.json` (stale or unknown digest rejects) |
34
+ | `sections[]` keys | exactly `heading`, `claims`; at least one section; `claims` non-empty |
35
+ | `heading` | one of `summary`, `priorities`, `limits` |
36
+ | `claims[]` keys | exactly `template`, `evidenceRef`; unknown keys anywhere (including a free-prose `text`) reject the whole narrative |
37
+ | `template` | one of `fact`, `action`, `limit` |
38
+ | `evidenceRef` keys | exactly `artifact`, `jsonPointer`; `artifact` must be `audit.json` |
39
+ | `jsonPointer` | exactly two segments: the template's own collection plus one key — `/facts/<key>` for `fact`, `/actions/<actionId>` for `action`, `/limits/<key>` for `limit` |
40
+ | Pointer targets | `fact` must address a recorded scalar observed fact (`/facts/pages_visited`, `/facts/requests_made`, `/facts/total_bytes`, …); `action` must address an existing action **by stable id** (never an array index); `limit` must address a recorded limit (`pagesVisited`, `requestsMade`, `totalBytes`, `elapsedSeconds`, `hitLimit`) |
41
+ | Bounds | ≤ 32 KiB raw and ≤ 30 claims |
42
+
43
+ Prototype keys (`__proto__`, `constructor`, `prototype`) and out-of-collection pointers are refused.
44
+ Rendering is method-owned: each claim becomes an `**Observed fact**`, `**Planned action**` or
45
+ `**Recorded limit**` line with the audit's own value, unit and source, and Markdown-escaped.
46
+
47
+ ## 2. Worked example
48
+
49
+ Given an audit whose `runId` is `8f2c…`, whose `auditDigest` is the real sha256 of `audit.json`,
50
+ and whose actions include `SEO-title_missing-0f115db062b7`:
51
+
52
+ ```json
53
+ {
54
+ "schemaVersion": 1,
55
+ "runId": "8f2c1d0e-…",
56
+ "auditDigest": "<sha256 of audit.json>",
57
+ "sections": [
58
+ {
59
+ "heading": "summary",
60
+ "claims": [
61
+ { "template": "fact", "evidenceRef": { "artifact": "audit.json", "jsonPointer": "/facts/pages_visited" } },
62
+ { "template": "fact", "evidenceRef": { "artifact": "audit.json", "jsonPointer": "/facts/requests_made" } }
63
+ ]
64
+ },
65
+ { "heading": "priorities", "claims": [{ "template": "action", "evidenceRef": { "artifact": "audit.json", "jsonPointer": "/actions/SEO-title_missing-0f115db062b7" } }] },
66
+ { "heading": "limits", "claims": [{ "template": "limit", "evidenceRef": { "artifact": "audit.json", "jsonPointer": "/limits/hitLimit" } }] }
67
+ ]
68
+ }
69
+ ```
70
+
71
+ Renders as (in `report.md`, section 3):
72
+
73
+ ```markdown
74
+ ### Summary
75
+
76
+ - **Observed fact** `pages_visited` = `12` count — source: `crawl.json`
77
+ - **Observed fact** `requests_made` = `34` count — source: `crawl.json`
78
+
79
+ ### Priorities
80
+
81
+ - **Planned action** `SEO-title_missing-0f115db062b7` (high, P1) — Fix title missing — 3 affected page(s), effort band 1
82
+
83
+ ### Limits
84
+
85
+ - **Recorded limit** `hitLimit` = `none`
86
+ ```
87
+
88
+ To produce this in a run: read `<work_dir>/audit.json`, compute its sha256 over the exact file
89
+ bytes, keep the same `runId`, then write the object to `<work_dir>/narrative.json` **before**
90
+ `seo-report` runs (the workflow's `agent.run` step does exactly that, with
91
+ `answerFile: <work_dir>/narrative.json`). Replying `{}` is the documented way to decline: the
92
+ report then falls back to its deterministic summary.
93
+
94
+ ## 3. Rejection cases (each rejects the entire narrative)
95
+
96
+ | Case | Diagnostic shape |
97
+ | --- | --- |
98
+ | Free prose added to a claim (`text: "…"`) or any unknown key | `sections[0].claims[1]: unknown key "text"` |
99
+ | Heading outside the allow list (`"Overview"`) | `sections[0]: heading "Overview" is not an allow-listed heading` |
100
+ | Template mismatch with the pointer (`fact` + `/limits/…`) | `template "fact" requires a pointer into /facts, got "/limits"` |
101
+ | Invented fact or limit key | `unknown fact id "domain_authority"` / `unknown limit id "ranking_position"` |
102
+ | Invented or misspelled action id, or an array index (`/actions/0`) | `unknown action id "0"` |
103
+ | Pointer deeper or shallower than two segments (`/facts/a/b`) | `jsonPointer "…" must address exactly one record (collection/key)` |
104
+ | Stale digest (audit re-scored after the narrative was written) | `narrative auditDigest does not match the materialized audit.json digest (stale or unknown digest)` |
105
+ | Wrong run id | `narrative runId "…" does not match the audit runId` |
106
+ | Non-object root, non-JSON bytes, empty sections, > 32 KiB, > 30 claims | `narrative root must be a JSON object` / `narrative is not valid JSON: …` / `narrative sections must not be empty` |
107
+
108
+ On rejection the stage writes `narrative-diagnostics.json` beside the audit, sets
109
+ `report.json.narrativeStatus: 'rejected'`, and renders the deterministic summary labeled
110
+ `**Narrative rejected**`. Never edit the saved `narrative.json` to force acceptance, and never move
111
+ a rejected claim into the deterministic summary by hand: the summary is method-owned.
112
+
113
+ ## 4. Advisory prose lives outside the report
114
+
115
+ The validated narrative deliberately cannot carry commentary. If the operator asks for
116
+ explanation, interpretation or an action plan in prose, that is the **skill's** output: keep it in
117
+ conversation (or a separate operator-authored document) and:
118
+
119
+ - label every statement as measurement, heuristic, model judgment or unknown (see `method.md`);
120
+ - cite the artifact plus the field (`report.json` action id, `checks.json` row, `audit.json`
121
+ `/facts/*`) for every number;
122
+ - mark specialist advice outside the collected evidence as **consultation**, naming the missing
123
+ evidence and the source that would be needed;
124
+ - make no promise about rankings, citations, traffic, revenue or crawler behavior, and propose no
125
+ change that mutates the site — the audit is read-only, and any remediation is an operator action.