Crawler-Factory

Engineering-Specs/Validation/SOP-Writing-Report-V1.md

Writing SOPs Validation Report — Crawler-Factory Engineering Specs

Audit of NARRATIVE TEXT ONLY (not code blocks, not test fixture strings) in the 14 spec files (00-Index plus 01..13). Code blocks, test fixtures, and JSON schemas are excluded by SOP scope (WritingSOPs governs prose; CodingSOPs governs code).

Headline

13 of 14 files FAIL on Hard Rule 1 (No Em-Dashes). Em-dashes appear in every prose-bearing spec, 9 to 77 occurrences per file, 700+ instances total. This is the only systemic violation. On every other axis (AI tells, hedging, vague specificity, false certainty, voice, structure, active voice, sentence length) the specs are clean to PASS / WARN.

The em-dash violation is mechanical, not stylistic — these specs are otherwise model writing-SOP compliance: concrete, named, evidenced, technically dense, no business-speak, no hedge claims.


Methodology

  • Read Standards/WritingSOPs.md fresh (April 2026 version, 238 lines).
  • Read 00-Index.md and 01-foundations.md in full as exemplars.
  • Read 100-line narrative-heavy slices (header, architecture, SOP rules section, acceptance criteria, out-of-scope) of every other spec.
  • Ran literal-character searches across all 14 files for: em-dashes (), en-dashes (), banned discourse markers (Indeed/Furthermore/Moreover/Notably/Importantly), hedging adverbs (essentially/fundamentally/ultimately/arguably/notably/interestingly), conversational openers (let's dive/unpack/here's the thing), stock business phrases (in today's/fast-paced/leverage/utilize/dive into/delve), false-certainty hedges (should work/may work/seems to/appears to), the "not just X, it's Y" construction, and vague-specificity phrases (appropriate/proper/various/several X handling).

The grep audit pierces code blocks too, so any narrative violation surfaces. Code-block matches were filtered or read in context to confirm prose-only attribution.


Per-spec scorecard

Spec Em-dash AI tells Concrete Active Sentence Structure Voice Verdict
00-Index FAIL (9) PASS PASS PASS PASS PASS PASS FAIL
01-foundations FAIL (38) PASS PASS PASS PASS PASS PASS FAIL
02-sitemap-discovery FAIL (59) PASS PASS PASS PASS PASS PASS FAIL
03-waf-sampling FAIL (67) PASS PASS PASS PASS PASS PASS FAIL
04-cms-classification FAIL (69) PASS PASS PASS PASS PASS PASS FAIL
05-extraction-sandbox FAIL (42) PASS PASS PASS PASS PASS PASS FAIL
06-crawler-client-index-manager FAIL (41) PASS PASS PASS PASS PASS PASS FAIL
07-brand-discovery FAIL (44) PASS PASS PASS PASS PASS PASS FAIL
08-backend-api FAIL (59) PASS PASS PASS PASS PASS PASS FAIL
09-frontend-foundations FAIL (50) PASS PASS PASS PASS PASS PASS FAIL
10-frontend-discovery FAIL (63) PASS PASS PASS PASS PASS PASS FAIL
11-frontend-configurator FAIL (60) PASS PASS PASS PASS PASS PASS FAIL
12-frontend-orchestrator FAIL (31) PASS PASS PASS PASS PASS PASS FAIL
13-bundle-smoke-docs FAIL (77) PASS PASS PASS PASS PASS PASS FAIL

Total violations: 14 FAILs (one per file), 1 cross-cutting axis.


Hard Rule 1 — No Em-Dashes (FAIL on all 14 specs)

The SOP is explicit and absolute (lines 40-46 of WritingSOPs.md):

Zero em-dashes () in any user-facing content. Zero. Universal. No exceptions. Em-dashes have become the most recognized AI-writing tell. Even when used correctly by a human, they trigger reader pattern-matching that flags the content as AI-generated. The credibility cost is too high to allow any.

Em-dash counts per file (literal character count):

  9  00-Index.md
 38  01-foundations.md
 59  02-sitemap-discovery.md
 67  03-waf-sampling.md
 69  04-cms-classification.md
 42  05-extraction-sandbox.md
 41  06-crawler-client-index-manager.md
 44  07-brand-discovery.md
 59  08-backend-api.md
 50  09-frontend-foundations.md
 63  10-frontend-discovery.md
 60  11-frontend-configurator.md
 31  12-frontend-orchestrator.md
 77  13-bundle-smoke-docs.md
---
729  total

Representative offenses

  • 00-Index.md:3"covering one cluster of the master plan (../00-Plan.md). Specs are independently executable by a fresh agent (Sonnet 4.6, teams mode) — no agent needs to read the full 2000-line plan; they read their cluster spec." Suggested rewrite: "...teams mode). No agent needs to read the full 2000-line plan; they read their cluster spec."

  • 00-Index.md:35-41 — every list item in the Format-contract block uses em-dashes as bullet separators (**Header** — exact format from writing-plans skill...). Replace with colon: **Header:** exact format from writing-plans skill....

  • 01-foundations.md:5 (Goal line) — "Establish the data foundation for the Crawler Factory — Domain Schema Standard (DSS) registry, zod schemas..., sharded session store, and blueprint store — so every downstream cluster..." Suggested rewrite: "...for the Crawler Factory: a Domain Schema Standard (DSS) registry, zod schemas..., a sharded session store, and a blueprint store. Every downstream cluster..."

  • 01-foundations.md:7 (Architecture line) — "...sharded across 4 record types (session, url_shard, sample, log) in the algoliacentral_factory_sessions index — uncapped URL counts via streaming." Suggested rewrite: "...in the algoliacentral_factory_sessions index. URL counts are uncapped via streaming."

  • 02-sitemap-discovery.md:5"Build the streaming sitemap discovery layer — walkSitemap async iterator..., groupByPathPrefix..., and estimateScope (1–2s pre-flight probe). Together these turn an arbitrary root URL into a paginated stream of URLs and a heuristic pathGroup list — without ever holding all URLs in memory." Two em-dashes in one Goal line. Replace both with periods or colons.

  • 04-cms-classification.md:7"...the aggregator picks the max-confidence candidate, applies an agreement boost (each agreeing layer adds 0.05, capped at 0.95), and only fires the LLM layer when combined layers 1–7 confidence < 0.55. The site-type rubric is an ordered list of {predicate, siteType} rules — data, not code — that maps a probed site to a §16 playbook in the first 30 seconds." Em-dash pair around "data, not code." Replace with parentheses or commas.

  • 13-bundle-smoke-docs.md:5"Final verification cluster — bundle every new factory API endpoint into api/factory/*.mjs..." Suggested: "Final verification cluster. Bundle every new factory API endpoint..."

Pattern of usage

Em-dashes are used in the specs for three structural purposes, all of which the SOP wants replaced:

  1. Apposition / definition pairs in Goal and Architecture lines — replace with a colon or split into two sentences.
  2. Bullet-table separators in key — value or name — description constructs (heavy in File-structure tables and SOP rules section openers like **Logger in every module.** Top of every file: ...). Replace with colon (Key: value) or period.
  3. Aside / parenthetical mid-sentence (recursion happens via a private helper that takes a single \WalkContext` config object`). Replace with a parenthetical or new sentence.

Fix complexity

  • All 729 instances are mechanical replacements (. / : / (...)).
  • Recommend a single sweep per file with a 4-rule replacement table:
  • Word — Word (apposition) → Word: Word or Word. Word.
  • (...) — clause(...). Clause or (... — clause) becomes (...; clause).
  • **Bold —** rest (rule labels) → **Bold:** rest.
  • Numeric ranges (1–7, 2–5, 0.65–0.84) are en-dashes, not em-dashes — not in scope of Rule 1, but see Rule 1b below.

Hard Rule 1b — En-dashes in prose (WARN, not FAIL)

The SOP allows en-dashes in numeric ranges (2024–2025) but says "prefer 'X to Y' in body text." En-dash usage in the specs is almost entirely numeric range (1–7, 0.60–0.75, 5–15s, 9–14), which is acceptable.

  2  00-Index.md
  2  01-foundations.md
  3  02-sitemap-discovery.md
  7  03-waf-sampling.md
 15  04-cms-classification.md
  1  05-extraction-sandbox.md
  1  06-crawler-client-index-manager.md
  5  08-backend-api.md
  6  09-frontend-foundations.md
  3  10-frontend-discovery.md
  1  11-frontend-configurator.md
  1  12-frontend-orchestrator.md
 18  13-bundle-smoke-docs.md

WARN: 04-cms-classification.md (15) and 13-bundle-smoke-docs.md (18) have higher density. A few of these (e.g. 0.65 – 0.84 in a band-table) are inside code-block-style tables and not strictly prose. No FAIL action required, but during the em-dash sweep the writer should convert prose ranges to "X to Y" where natural.


Hard Rule 2 — No Other AI Tells (PASS on all 14 specs)

Literal grep across all 14 files for the SOP's banned constructions returned zero hits in narrative text:

  • \b(essentially|fundamentally|ultimately|arguably|notably|interestingly|furthermore|moreover|indeed|importantly)\b — zero
  • not just|not merely — one match in 03-waf-sampling.md:1590 (playwright is a runtime dependency (not just devDependency)) which is the factual sense ("not just X" describing a dependency category), NOT the banned LLM construction "Not just X, it's Y." PASS.
  • let's dive|let's unpack|here's the thing|buckle up — zero
  • in today's|fast-paced|move the needle|circle back|drill down|leverage |ever-evolving|at the end of the day|the name of the game — zero
  • it's important to note|important to note|worth noting|worth mentioning — zero
  • \b(robust|seamless|seamlessly|utilize|utilizing|dive into|delve|delves|delving)\b — zero

This is excellent. The specs are written in technical-spec voice without any of the canonical AI tells.


Hard Rule 3 — Concrete Over Abstract (PASS on all 14 specs)

Every abstract claim in the audited narrative is grounded with named entities, file paths, line numbers, version pins, or numerical thresholds within two sentences. Examples:

  • 06-crawler-client-index-manager.md:5 claims "Port the Python CrawlerClient" and immediately specifies packages/crawl/src/crawl/manage_seeder.py:34-102.
  • 03-waf-sampling.md:14 claims "WAF/bot-detection reality" and grounds it: "60-site WAF rate findings — ~40% block plain fetch."
  • 04-cms-classification.md:7 claims "CMS+URL is the cascade FOUNDATION" and grounds it: "(~95% combined coverage per Web Almanac 2024 + the 14-site empirical audit). JSON-LD enriches but never leads (only 41% global, ~7% useful @type values)."
  • 02-sitemap-discovery.md:7 claims "Files >5MB use streaming/chunked parse paths to avoid OOM on Vercel functions" and grounds the threshold: "(1024MB ceiling)."
  • 13-bundle-smoke-docs.md:5 claims "smoke against https://www.algolia.com that proves discover → categorize → configure → REAL test crawl → create-crawler → live monitor for 3 distinct content domains" — fully named target, fully numbered scope.

No abstract claim survives a paragraph without a specific anchor. The specs over-perform on Rule 3 — this is among the most concretely-grounded technical writing in the vault.


Hard Rule 4 — Active Voice (PASS on all 14 specs)

Spot-checked architecture and goal sections. Active voice is the default. Passive constructions appear only where the actor is genuinely irrelevant (e.g., "is enforced by ESLint", "are pruned"). No incidence of weak passive ("is identified", "was determined") — every actor is named (walkSitemap yields, Spec 06 wraps, IndexManager ensures, the configurator commits, the user pastes a URL).


Hard Rule 5 — Sentence Length (PASS on all 14 specs)

Sentences are mostly short and declarative. Long sentences earn their length — they are typically architecture lines that stack related modules with semicolons or commas to preserve a single mental model (e.g., 02-sitemap-discovery.md:7 is one long Architecture sentence that legitimately needs to enumerate three modules and their streaming contracts).

No incidence of wandering / unedited long sentences. WARN: a few long sentences could be cut in half for readability without losing meaning, but none violate the rule.


Structural Principles (PASS on all 14 specs)

Every spec follows a strict pyramid:

  1. H1 title = one-sentence what.
  2. Goal (one paragraph) = the through-line.
  3. Architecture (one paragraph) = the shape.
  4. Tech Stack / Depends on / Consumed by / Plan source = anchors.
  5. File structure table = scannable inventory.
  6. SOP rules = numbered list, no prose padding.
  7. Tasks = TDD-cadence steps with embedded code.
  8. Acceptance criteria + Out of scope = bounded close.

Through-line is statable in one sentence for every spec (the Goal line is literally the through-line). Pyramid is followed. Section openers carry weight (no "Now let's look at..."). Endings land — Out-of-scope sections close each spec with a clear handoff to the next.

Named-over-generic discipline is exemplary throughout: real file paths (packages/crawl/src/crawl/manage_seeder.py:34-102), real index names (algoliacentral_factory_sessions), real version pins (fast-xml-parser@^4.5.0, playwright@^1.57.0), real RFC references (RFC 9309), real customer / site references (Microsoft Learn, Apple Autopush, LVMH, Diageo, SKIMS), real plan references (§3a-bis, §16o, §19f principle 7).


Voice and Register (PASS on all 14 specs)

Upper-middle technical formality. Third-person observer voice (the spec describes the system, not the writer). Pronoun usage is consistent — when "we" appears it is the spec speaking for the engineering team (03-waf-sampling.md:31: "Per the user directive, we keep SampleSchema local..."). No mixed registers.

Confident without arrogant. Honest about uncertainty: e.g., 02-sitemap-discovery.md:1574 admits the streaming-SAX path is reserved for later because "v1 of this spec uses the in-memory XMLParser because Node.js fetch buffers the response anyway." That is exactly the SOP's "honest about uncertainty" voice.


Honesty Markers (PASS on all 14 specs)

Zero false-certainty hedges found. Greps for should work | may work | might work | appears to | seems to be | in theory return no narrative hits. Where outcomes are uncertain, the specs name the uncertainty precisely:

  • 06-crawler-client-index-manager.md:7 — "neuralSearch is intentionally NOT enabled on cold-start indices — the Algolia API rejects it without prior Insights events; we set a neuralPending flag for the next-run promotion (Spec 13)."
  • 13-bundle-smoke-docs.md:712-720 — the Lessons-learned template explicitly invites deviation as data, not apology and uses <fill> placeholders the writer is told to replace.

These are honesty markers in the SOP sense (Rule 2 of WritingSOPs §"Tone": "If you are sure, say so. If you are not sure, say that too.").


Specificity (PASS on all 14 specs)

The WritingSOPs treat "appropriate error handling" as a fail (per the skill spec brief). I greped for appropriate|properly|various|several|many|some|multiple paired with error|handling|things|cases|files|edge|features and got one hit, and it is not a violation: 07-brand-discovery.md:40 says "Cheerio parsing is wrapped (cheerio throws on malformed HTML in some edge paths)." The "some edge paths" is a precise, scoped factual statement (not all paths, not none — a known set of edge cases) and immediately followed by the concrete consequence ("The injected sampler call is wrapped"). PASS.

Every other narrative claim has a file:line, a §-reference, a version pin, a named technology, or a numeric threshold attached.


What is genuinely strong about this corpus

  1. Reference density. Every architectural decision is cross-linked to its 00-Plan.md § number AND to the empirical evidence (Web Almanac 2024, the 14-site audit, the 60-site WAF audit). This is the gold standard for engineering specs.
  2. Through-line discipline. Every spec opens with a one-sentence Goal that captures the entire piece. The audit could state each spec back to the writer in one sentence pulled directly from line 5.
  3. Boundary explicitness. Out-of-scope sections at the end of each spec name what is not done and which spec does it. This is the inverse-pyramid SOP done correctly.
  4. No marketing voice. Despite specs that touch UI, configurator drawers, and live monitor panels, there is zero "delight the user" / "seamless experience" / "powerful workflow" diction. The voice stays operational.

Required actions before ship

FAIL — must fix

Em-dash sweep across all 14 files. Mechanical replacement, ~729 occurrences total. Recommend a single PR with the four-rule replacement table from Rule 1 above. Manual review required because some em-dashes carry rhythm that a period would break — those should become colons or parentheses, not periods.

WARN — fix on next pass, not blocking

  1. 04-cms-classification.md and 13-bundle-smoke-docs.md have heavier en-dash density in prose-bordering tables (15 and 18 instances). Convert prose ranges to "X to Y"; leave numeric-table ranges alone. ~10 prose conversions total.
  2. A handful of long Architecture sentences (notably 02-sitemap-discovery.md:7, 04-cms-classification.md:7) could be cut into two without losing rhythm. Optional polish.

PASS — no action required

  • All AI-tell axes (Rule 2)
  • Concrete-grounding (Rule 3)
  • Active voice (Rule 4)
  • Structural pyramid + through-line + named-over-generic
  • Voice / register / pronoun consistency
  • Honesty markers / no false certainty
  • Specificity (no vague "appropriate" / "various" usage)

One-sentence summary for the parent agent

The 14 Crawler-Factory engineering specs are technically excellent on every Writing-SOP axis except em-dashes — every spec violates Hard Rule 1 (collectively 729 em-dashes), so all 14 fail on that mechanical axis and need a sweep replace, but no spec needs a structural rewrite, no spec carries AI tells, and every architectural claim is concretely grounded with named entities, file paths, version pins, or numeric thresholds.