Editor passes: SEO/Voice, cycle-until-pass, and grounding against ingested sources #6

Open
opened 2026-08-31 05:33:12 +00:00 by PlasticDigits · 4 comments
PlasticDigits commented 2026-08-31 05:33:12 +00:00 (Migrated from gitlab.com)

Summary

Editor quality: add missing SEO and Voice passes from #1; cycle editor steps until mechanical + grounding checks pass (budget-capped); persist one final document (intermediate pass files only in debug). Replace the incorrect "every H2 maps to a citation or is marked commentary" rule with an internal grounding check: every substantive claim in the post must match ingested data (or be dropped / labeled commentary that does not invent facts).

Gap: gaps/GAP_1788152435.md §2 (editor, outline citation map).


Current codebase

Issue #1 asked for 11 separate creates in order: SEO, grammar, voice, AI-tell, sentence-length, active/passive, clarity, structure, show-don't-tell, conciseness, open+close.

src/pipeline.rs EDITOR_PASSES is: grammar, punctuation, sentence-length, paragraph-rhythm, active-voice, clarity, structure, show-dont-tell, conciseness, open-close, ai-tell.

Missing: SEO, Voice. Extra: punctuation, paragraph-rhythm (keep as additional criteria — "11+").

Implementation does one editor create_once, then writes the same blob to all 11 passes/*.md. Mechanical lint_markdown runs once at emit.

Numeric grounding: unsourced() flags numbers not in NumericClaims (years 20xx and ≤2-digit numbers exempt). There is no check that prose matches ingested repo events, indexer stats, telegram (non-authoritative), tokenlist, or Tavily snippets. Outline H2↔citation is not enforced (and should not be the product rule).

Happy-week replicate.json pads the same paragraph to hit 2000 words — does not prove live editor quality.


Why this is needed

SEO/voice were explicit in #1. Dumping 11 identical files wastes disk and review. Cycling until lint+grounding pass avoids publishing dirty or hallucinated recaps. Posts must not invent shipping, volumes, or prices that were never ingested.


Constraints / guardrails

  1. Create-once per attempt still applies: each cycle iteration is a new create, never a retry of the same create. Prefer one create per cycle with a rubric listing all criteria, then mechanical+grounding fail → new create with the failed report (counts against weekly budget). Do not revive 11 creates per week unless budget is raised explicitly.
  2. Weekly budget remains a hard cap (REPLICATE_WEEKLY_CREATE_BUDGET). Exhaustion → fail the week (or emit short announcement if --short / thin-week), no silent dirty publish.
  3. Debug mode (CL8Y_RESEARCH_DEBUG=1 or --debug): write intermediate cycle artifacts under runs/<week>/passes/. Default: one post.mdx (+ report).
  4. Grounding: claims about volumes, prices, fees, addresses, "we shipped/merged X", dates must appear in ingested sources (with citation). Commentary/opinion allowed without a number but must not fabricate product behavior. Telegram/Tavily are not onchain proof.
  5. Mechanical lint still fail-closed (dashes, curly quotes, banned CTAs, unofficial addresses, fee-tier invention).
  6. Voice: CL8Y-web/blog_gen/SKILL.md. SEO: unique title/description bounds already in invariants.rs; slug; no keyword stuffing; href allowlist including first-party hosts (yieldomega.com if we add it).
  7. Do not pad short weeks with generic DeFi filler (--short / 800 word cap).

Relevant files

Path Role
src/pipeline.rs Editor bundle, emit, word counts
src/lint.rs Mechanical lint
src/numeric.rs Number vs claims
src/mdx.rs Sanitize, frontmatter bounds
src/invariants.rs Title/description bounds, banned phrases
docs/invariants.md Prose rules
CL8Y-web/blog_gen/SKILL.md Voice
tests/issue_plan.rs Lint / injection

  1. Typed rubric list: #1 eleven + punctuation + paragraph-rhythm (document the set in invariants.rs).
  2. ground_post(body, &Collection) -> Vec<Violation>: numbers (existing), plus checks (indexer figures, known repo event strings, listed token names vs unofficial tickers). Fail closed on unsourced quantities and on sentences that assert "merged/shipped" when repo sources are empty.
  3. Loop: editor create → lint + ground → if fail and budget remains, next create with violation list; else emit or error.
  4. Default artifacts: post.mdx, report.md (cycle count, prediction ids, remaining violations if any).

Acceptance criteria

  • SEO and Voice are in the rubric and covered by tests (SEO: title/description bounds, no stuffed keyword blob; Voice: banned promotional/home copy).
  • Default run writes one post file, not 11 duplicate passes/*.md.
  • --debug / env writes intermediates.
  • Dirty lint fails; cycle with fixture predictor that first returns dirty then clean → one published clean body, create_count ≥ 2, under budget.
  • Body asserting volume 99999 when sources only have 12000 fails even if lint-clean.
  • Body asserting "we shipped malware-repo" when only yieldomega events exist fails.
  • Commentary without fake numbers can pass.
  • Budget exhaustion does not publish dirty MDX.

Test plan — functional paths

  1. Clean fixture — one emit, no passes/ unless debug.
  2. Debugpasses/ present.
  3. SEO fail — title 3 chars → fail or cycle to valid title (fixture sequence).
  4. Voice fail — "Buy CL8Y" / "the future of DeFi" → fail.
  5. Cycle dirty→clean — fixture map by create index.
  6. Grounding numbers — existing unsourced test.
  7. Grounding shipping — empty repos + "we shipped the bridge indexer API" → fail.
  8. Short week--short not padded past 800.
  9. Budget — max 1 remaining create, first editor dirty → fail closed, no publish.

Test plan — attack, hack, and abuse

  1. Injection in editor output — script / lookalike href / unofficial address still fail after cycles.
  2. Infinite cycle — budget/max-iterations cap.
  3. Grounding bypass — "as the README describes 99999 volume" still unsourced.
  4. Telegram as proof — "indexer volume is 99999 because chat said so" fails (telegram not numeric source).
  5. Tavily as proof (when that issue lands) — web snippet cannot invent DEX volume.
  6. Secret in cycle artifacts — debug files also run assert_clean_artifact.

Verification criteria

  1. Fixture week: single post.mdx, report lists rubric + cycle count.
  2. Reviewer reads invariants and sees SEO + Voice + grounding rule (not H2-citation fiction).
  3. Injection and unsourced tests still fail closed.
## Summary Editor quality: add missing **SEO** and **Voice** passes from [#1](https://gitlab.com/PlasticDigits/cl8y-research/-/issues/1); **cycle** editor steps until mechanical + grounding checks pass (budget-capped); persist **one** final document (intermediate pass files **only in debug**). Replace the incorrect "every H2 maps to a citation or is marked commentary" rule with an **internal grounding check**: every substantive claim in the post must match **ingested** data (or be dropped / labeled commentary that does not invent facts). Gap: [`gaps/GAP_1788152435.md`](https://gitlab.com/PlasticDigits/cl8y-research/-/blob/main/gaps/GAP_1788152435.md) §2 (editor, outline citation map). --- ## Current codebase Issue #1 asked for **11 separate creates** in order: SEO, grammar, voice, AI-tell, sentence-length, active/passive, clarity, structure, show-don't-tell, conciseness, open+close. `src/pipeline.rs` `EDITOR_PASSES` is: `grammar`, `punctuation`, `sentence-length`, `paragraph-rhythm`, `active-voice`, `clarity`, `structure`, `show-dont-tell`, `conciseness`, `open-close`, `ai-tell`. **Missing:** SEO, Voice. **Extra:** punctuation, paragraph-rhythm (keep as additional criteria — "11+"). Implementation does **one** editor `create_once`, then writes the **same blob** to all 11 `passes/*.md`. Mechanical `lint_markdown` runs once at emit. Numeric grounding: `unsourced()` flags numbers not in `NumericClaim`s (years `20xx` and ≤2-digit numbers exempt). There is **no** check that prose matches ingested repo events, indexer stats, telegram (non-authoritative), tokenlist, or Tavily snippets. Outline H2↔citation is **not** enforced (and should not be the product rule). Happy-week `replicate.json` pads the same paragraph to hit 2000 words — does not prove live editor quality. --- ## Why this is needed SEO/voice were explicit in #1. Dumping 11 identical files wastes disk and review. Cycling until lint+grounding pass avoids publishing dirty or hallucinated recaps. Posts must not invent shipping, volumes, or prices that were never ingested. --- ## Constraints / guardrails 1. **Create-once per attempt** still applies: each cycle iteration is a new create, never a retry of the same create. Prefer **one create per cycle** with a rubric listing all criteria, then mechanical+grounding fail → **new** create with the failed report (counts against weekly budget). Do not revive 11 creates per week unless budget is raised explicitly. 2. Weekly budget remains a hard cap (`REPLICATE_WEEKLY_CREATE_BUDGET`). Exhaustion → fail the week (or emit short announcement if `--short` / thin-week), no silent dirty publish. 3. **Debug mode** (`CL8Y_RESEARCH_DEBUG=1` or `--debug`): write intermediate cycle artifacts under `runs/<week>/passes/`. Default: **one** `post.mdx` (+ report). 4. Grounding: claims about volumes, prices, fees, addresses, "we shipped/merged X", dates must appear in ingested `sources` (with citation). Commentary/opinion allowed without a number but **must not** fabricate product behavior. Telegram/Tavily are not onchain proof. 5. Mechanical lint still fail-closed (dashes, curly quotes, banned CTAs, unofficial addresses, fee-tier invention). 6. Voice: `CL8Y-web/blog_gen/SKILL.md`. SEO: unique title/description bounds already in `invariants.rs`; slug; no keyword stuffing; href allowlist including first-party hosts (`yieldomega.com` if we add it). 7. Do not pad short weeks with generic DeFi filler (`--short` / 800 word cap). --- ## Relevant files | Path | Role | | --- | --- | | `src/pipeline.rs` | Editor bundle, emit, word counts | | `src/lint.rs` | Mechanical lint | | `src/numeric.rs` | Number vs claims | | `src/mdx.rs` | Sanitize, frontmatter bounds | | `src/invariants.rs` | Title/description bounds, banned phrases | | `docs/invariants.md` | Prose rules | | `CL8Y-web/blog_gen/SKILL.md` | Voice | | `tests/issue_plan.rs` | Lint / injection | --- ## Recommended direction 1. Typed rubric list: #1 eleven + punctuation + paragraph-rhythm (document the set in `invariants.rs`). 2. `ground_post(body, &Collection) -> Vec<Violation>`: numbers (existing), plus checks (indexer figures, known repo event strings, listed token names vs unofficial tickers). Fail closed on unsourced quantities and on sentences that assert "merged/shipped" when repo sources are empty. 3. Loop: editor create → lint + ground → if fail and budget remains, next create with violation list; else emit or error. 4. Default artifacts: `post.mdx`, `report.md` (cycle count, prediction ids, remaining violations if any). --- ## Acceptance criteria - [ ] SEO and Voice are in the rubric and covered by tests (SEO: title/description bounds, no stuffed keyword blob; Voice: banned promotional/home copy). - [ ] Default run writes **one** post file, not 11 duplicate `passes/*.md`. - [ ] `--debug` / env writes intermediates. - [ ] Dirty lint fails; cycle with fixture predictor that first returns dirty then clean → one published clean body, create_count ≥ 2, under budget. - [ ] Body asserting volume `99999` when sources only have `12000` fails even if lint-clean. - [ ] Body asserting "we shipped malware-repo" when only yieldomega events exist fails. - [ ] Commentary without fake numbers can pass. - [ ] Budget exhaustion does not publish dirty MDX. --- ## Test plan — functional paths 1. **Clean fixture** — one emit, no `passes/` unless debug. 2. **Debug** — `passes/` present. 3. **SEO fail** — title 3 chars → fail or cycle to valid title (fixture sequence). 4. **Voice fail** — "Buy CL8Y" / "the future of DeFi" → fail. 5. **Cycle dirty→clean** — fixture map by create index. 6. **Grounding numbers** — existing unsourced test. 7. **Grounding shipping** — empty repos + "we shipped the bridge indexer API" → fail. 8. **Short week** — `--short` not padded past 800. 9. **Budget** — max 1 remaining create, first editor dirty → fail closed, no publish. --- ## Test plan — attack, hack, and abuse 1. **Injection in editor output** — script / lookalike href / unofficial address still fail after cycles. 2. **Infinite cycle** — budget/max-iterations cap. 3. **Grounding bypass** — "as the README describes 99999 volume" still unsourced. 4. **Telegram as proof** — "indexer volume is 99999 because chat said so" fails (telegram not numeric source). 5. **Tavily as proof** (when that issue lands) — web snippet cannot invent DEX volume. 6. **Secret in cycle artifacts** — debug files also run `assert_clean_artifact`. --- ## Verification criteria 1. Fixture week: single `post.mdx`, report lists rubric + cycle count. 2. Reviewer reads `invariants` and sees SEO + Voice + grounding rule (not H2-citation fiction). 3. Injection and unsourced tests still fail closed.
PlasticDigits commented 2026-08-31 05:35:05 +00:00 (Migrated from gitlab.com)

marked as related to #5

marked as related to #5
PlasticDigits commented 2026-08-31 05:35:06 +00:00 (Migrated from gitlab.com)

marked as related to #7

marked as related to #7
PlasticDigits commented 2026-08-31 05:35:06 +00:00 (Migrated from gitlab.com)

marked as related to #8

marked as related to #8
PlasticDigits commented 2026-09-02 02:18:49 +00:00 (Migrated from gitlab.com)

mentioned in issue #12

mentioned in issue #12
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
code/cl8y-research#6
No description provided.