Skip to content

πŸ“š A self-maintaining docs pipeline: source β†’ knowledge base β†’ guides [NT-3625]#369

Open
Tim Beyer (TimBeyer) wants to merge 43 commits into
mainfrom
feat/docs-knowledge-loop
Open

πŸ“š A self-maintaining docs pipeline: source β†’ knowledge base β†’ guides [NT-3625]#369
Tim Beyer (TimBeyer) wants to merge 43 commits into
mainfrom
feat/docs-knowledge-loop

Conversation

@TimBeyer

@TimBeyer Tim Beyer (TimBeyer) commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

Read this first. This PR is not "rework the docs." It is a process for producing and
maintaining docs, arrived at through a deliberate design exploration. The guide/KB content is far
too large to review line by line, and reviewing it that way would miss the point. What is worth
your scrutiny is the design below: the model it commits to, why that model, and the tradeoffs
taken along the way. The mechanics are documented after, but the "why" is the review.

The problem we were actually solving

The previous docs PR (#358) produced good web-family guides and introduced three artifacts β€” public
guides, an internal knowledge base of verified SDK facts, and authoring skills. But they
were not tied into a cycle. Each was produced in a single agent context, and there was nothing to
stop them drifting apart from each other or from the SDK source. Two failure modes followed directly:

  1. Silent rot. A fact in the knowledge base pointed at a line range in source; the source moved;
    nothing noticed. We found real examples β€” a pointer to OptimizedEntryResolver.ts:148-209 that no
    longer meant anything, source: accepted App Router guide pointers that were circular (a "fact"
    whose evidence was another doc), and stale directory paths from files that had since moved.
  2. Re-derivation. Every time a guide was touched, its claims were re-verified against source from
    scratch. That is fine once; as a steady-state practice it means a small SDK change triggers a
    full, expensive re-comprehension of the codebase to rebuild docs β€” re-reading and re-reasoning
    about the whole SDK because someone renamed a prop.

The ask was to look at this as a whole SDLC β€” not just the from-scratch authoring path β€” and make
the pieces flow into each other, mostly automatically, with as little bespoke machinery as possible.

The core idea: the knowledge base is a compiler IR

The design that fell out of the exploration treats the three artifacts as a compilation pipeline:

        source→KB pointer                      feeds-guides marker
   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”            β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
   β”‚                      β–Ό            β–Ό                          β”‚
 SOURCE ──▢ sdk-knowledge-author ──▢ KNOWLEDGE BASE ──▢ guide-writer ──▢ GUIDES
 packages/src   (the only reader        (verified facts,   (composes from      (readers)
                 of source code)         each pointed)       facts, not code)
  • Source code is the source language.
  • The knowledge base is the intermediate representation: terse, verified facts, each carrying a
    machine-checkable pointer back to the exact source symbol that proves it.
  • The guides are the compiled target, for human readers.

The expensive step is comprehension β€” reading source and understanding what is true. The knowledge
base memoizes that step. Everything downstream (writing guides, reviewing them) reads facts, not
code. This is the hinge the whole design turns on, and it is what makes the steady state cheap.

Why this resolves the chicken-and-egg

There is a real tension: to know what to document you must first understand the code, so you cannot
"just read the knowledge base." The resolution is that comprehension is bounded differently in each
mode
, and only one mode is expensive:

Mode What runs Cost
Bootstrap full source→KB→guide for an SDK from nothing expensive — once per SDK
Incremental a source diff scopes the work: re-verify only the facts the change touches, recompose only the guides that consume them cheap β€” the common case
Recompose KB→guide with source unchanged (voice/structure only), driven by /iterate-guide cheapest — reads no source

The comprehension cost β€” reading and reasoning about the whole SDK β€” is paid at bootstrap and
amortized. A one-line SDK change touches a handful of facts and a handful of guide passages, because
the dependency graph tells us exactly which ones, instead of re-reasoning about everything.

The dependency graph is bidirectional and machine-checked

This is the piece that makes incremental scoping deterministic rather than guesswork:

  • source β†’ KB: every fact ends in a source: pointer (e.g. core-sdk#CoreBase.ts#ContentfulConfig).
    Given a changed source file, we know exactly which facts are at risk.
  • KB β†’ guide: every per-SDK knowledge file declares <!-- feeds-guides: … -->. Given a changed
    fact, we know exactly which guide must be recomposed.

Both directions are validated in CI (pnpm knowledge:check): a pointer must resolve to a symbol that
actually exists in source (checked via the TypeScript compiler, not a regex), and a feeds-guides
target must be a real guide.

Design decisions, and the reasoning behind them

These were the forks in the road. They are where review attention is best spent.

  • Symbol-anchored pointers, never line numbers. Line ranges drift on every edit; a symbol name
    survives refactors. The stale :148-209 pointer was the evidence. Pointers name a symbol
    (file#Symbol) and the validator confirms it is actually declared β€” so a rename fails the build
    instead of rotting silently. Line numbers are gone from the grammar entirely.

  • A strict grammar, defined first, then enforced. Rather than write a parser to tolerate the
    messy free-text pointers that existed, we defined a clean grammar (the KB's README owns it) and
    migrated the base onto it. The migration was where the circular and stale pointers died. The
    grammar is the contract; the validator checks it.

  • Deterministic where possible, agentic only where judgment is required. The dependency graph,
    pointer resolution, template conformance, and drift detection are plain deterministic checks (a
    ~700-line TypeScript validator using the compiler API β€” no new dependencies). Only the genuinely
    judgment-bound work β€” comprehending source into facts, composing teachable prose, reading a guide
    as a newcomer β€” is done by agents. This keeps CI honest and cheap and reserves model cost for what
    actually needs a model.

  • Standards-compliant over Claude-exclusive. We researched the landscape first. AGENTS.md, Agent
    Skills (SKILL.md), and MCP are open, multi-vendor standards; subagent definitions and docs-drift
    tooling are not. So the load-bearing parts are portable β€” skills live in a tool-neutral top-level
    skills/ dir (already symlinked into .claude/ and .agents/), the validator is a normal pnpm
    script, CI is plain GitHub Actions. The Claude-specific pieces (agent wrappers, a Stop hook) are a
    thin convenience layer over that portable core, not the substrate. We deliberately did not
    reach for MCP/skill-kit here: those add install friction and suit a distributed, customer-facing
    product, not an internal CI loop.

  • Fix drift, do not log it. An earlier artifact (consistency-notes.md) was a persisted work-log
    of "things to reconcile later." A file that catalogs known-broken things instead of fixing them is
    an anti-pattern; it was deleted and the skill instruction that produced it was replaced with "fix
    it now; shared wording lives once." Transient authoring state belongs to a workflow run, not a
    committed file.

  • Do not compromise readability to satisfy tooling. When the validator hit a lint line-cap, the
    fix was to split it into modules and keep every explanatory comment β€” not to strip the docs. The
    validator is thoroughly commented precisely because it does subtle work.

Formalizing structure: recipes and fragments (the missing third artifact)

The original design closed the loop on facts β€” the KB is the machine-checked source of truth for
what the SDK does β€” but left structure unformalized. A guide's section spine, sequence, and shared
prose lived in two competing places: long-form rules inside the authoring skill, and the shipped
sibling guides themselves. An agent writing a new guide could anchor on either, and copying a sibling
was the easy pull β€” so "shared" wording survived only by imitation and drifted a little each time.
Meanwhile the KB was the fact authority but had no owner for editorial shape, and the natural person
to own that β€” a technical writer β€” had nowhere to work that wasn't either skill internals or a
single guide surgically.

This adds a fourth, writer-owned artifact under documentation/authoring/, splitting structure
out of the skill the same way the KB earlier split facts out of the guides:

  • Recipes (recipes/, one per archetype) β€” the editorial spine: section order, which fragments a
    guide composes, and the rationale. The structural source of truth, authoritative over any sibling
    guide.
  • Fragments (fragments/) β€” reusable reader-facing prose (the "New to personalization?" intro
    explainer, the mandatory authored-variant gotcha) with a fixed spine and knowledge-base-filled
    slots
    . This is the reader-facing mirror of the KB's shared/ β€” canonical wording captured once,
    instantiated per guide, rather than re-imitated.

Two design points worth scrutiny, because they mirror decisions already made for facts:

  • It's prose the LLM instantiates, not a templating DSL. An early sketch leaned toward YAML
    frontmatter with tokens like opens_with: intro β€” which read as code but had no interpreter and no
    registry the tokens resolved to. The consumer of this layer is the guide-writer agent, whose
    native interface is natural language; a fake-precision schema would be strictly worse than prose.
    So recipes/fragments are prose, and each file splits into ## Context (agent-only rationale and
    fill rules, never rendered) and ## Template (the prose that becomes guide output). That split
    is the interface/behavior distinction one layer up: Context is judgment, Template is the artifact.
  • Verbatim spine vs. composed-around-an-anchor. Fixed sentences are reproduced word-for-word (only
    slots filled), which is what keeps a guide family consistent β€” and makes a future check able to
    confirm instantiation by matching the fixed sentence. But not every bullet is verbatim: where the
    content is genuinely SDK-shaped (the entry-source hand-off differs by manual/managed/both), the
    fragment marks the bullet composed β€” free prose around a fixed anchor (the baseline-fallback
    clause) that must survive intact. This distinction was discovered empirically by running the
    pipeline (see below), not designed up front.

A third slash command drives the recompose mode this enables:

  • /iterate-guide β€” editorial-only: recompose affected guides from the existing KB and recipes,
    reading no source and re-verifying no fact, because a pure-prose change moves no fact. Its guardrail
    is a hard stop: the moment an edit would change what a guide asserts about the SDK, it hands
    off to /refresh-docs (source changed) or /review-guide (verify vs. the base) rather than
    silently drifting a fact. This is the technical writer's tight inner loop β€” tune wording/tone/
    sequence, re-render, repeat β€” with /review-guide as the final gate.

Encoding the authoring process itself

The web-family guides in #358 were authored by agents, not by hand, and that work followed an
implicit division of labor: a writer, a reviewer reading as an average developer with no prior
knowledge
, and a technical-foundation reviewer verifying every claim is true β€” with adjustments
funneled back into the skills. That division was never named or made repeatable; it lived only in how
the work happened to run. This PR makes it explicit as four roles, split so comprehension happens
exactly once:

Role Reads Produces Cost
sdk-knowledge-author source (the only role that does) verified facts in the KB expensive, scoped/memoized
guide-writer KB facts guide prose cheap
guide-source-verifier guide + KB findings (a lookup; escalates gaps) cheap
guide-newcomer-reviewer guide reader-experience findings cheap

The important correction during design: verification is a consistency check against the knowledge
base
, not a re-derivation from source. A guide claim is trustworthy when it matches a verified
fact; a claim with no backing fact is escalated to the knowledge author, not re-verified inline.
That is what keeps comprehension in one place.

Three slash commands drive the lifecycle modes:

  • /author-guide β€” bootstrap: comprehend source into a new KB file first, then compose the guide
    from it, then review.
  • /refresh-docs β€” incremental (the default): a source diff scopes a KB reconcile and a
    guide recompose, reviewing only what changed.
  • /iterate-guide β€” recompose: editorial-only tuning from the existing KB and recipes, no source
    read (the technical writer's inner loop; see the recipes/fragments section above).

The loop is self-improving: durable review findings are funneled back into the right artifact β€”
reader/voice rules into the skill, structure and shared prose into the recipe or fragment, facts
into the knowledge base β€” never left as a TODO, so the next guide starts smarter.

Does it actually work? (three proofs)

We ran the workflow on the Node guide as the first real target. It:

  • bootstrapped the first node/ knowledge-base file (the stateless ContentfulOptimization model,
    consent axes, cookie ownership, locale precedence β€” each with a resolvable source pointer);
  • refreshed the guide from the pre-current structure (a ## Required setup table, change-narration
    prose) to the current archetype (plain-language intro, ## Before you start, performable verify
    step, source-grounded troubleshooting);
  • verified all 41 load-bearing claims against source, and fixed 2 newcomer blockers + 15 friction
    items;
  • funneled 6 durable reader-experience rules back into the authoring checklist;
  • and surfaced real workflow bugs (agents running an unscoped format:fix), which are fixed here.

The run passing knowledge:check and producing a genuinely better guide β€” while teaching us where
the workflow itself was underspecified
β€” is the evidence the loop is real, not aspirational.

React Native was then run as a second target, specifically to exercise the reshaped bootstrap
(knowledge-first) order. It held: the knowledge author ran before the guide writer, and the writer
escalated a missing fact rather than re-reading source β€” direct proof of the read-facts-not-code
discipline. It bootstrapped the first native/ family file, refreshed the guide to the archetype,
resolved 7 no-backing-fact claims by adding each fact from source (none turned out unfounded), and
correctly declined to propagate an onOfflineDrop shape to the iOS/Android sibling guides, since
their Swift/Kotlin source is not yet verifiable here β€” refusing to compose an unfounded claim is
exactly the discipline working.

The RN guide was then re-run through the full pipeline again β€” this time against the new
recipe/fragment layer β€” and that run is the sharpest proof the composition primitive works.
The
guide-writer realigned two intro bullets to the personalization-explainer fragment spine, which
reconciled real drift between two already-pipelined guides (RN said the SDK "builds/keeps
consistent" a profile; Node's canonical wording, now the fragment, says "returns/uses to keep
consistent") β€” the fragment forcing a family back into one voice is precisely what it exists to do.
The same run surfaced three distinct classes of issue, each routed to the correct layer: a defect in
the new artifact
(the explainer opener promised "five sentences" but its bullets are multi-sentence
β€” fixed to "points," and the empirical discovery that some bullets must be composed-around-an-anchor
rather than verbatim), a genuine KB gap (the "Experience API returns a profile" fact lived only in
node/node.md, leaving the RN claim unbacked in shared/ β€” promoted to shared/concepts.md and
referenced), and ordinary reader-experience fixes (a non-performable verify step, undefined
identifiers). Facts to the KB, structure to the fragment, reader rules to the checklist β€” the
funnel-back working as designed, on live output.

Then main moved under us β€” and that became the best proof of all. While this branch was open,
main shipped a managed-CDA rework (#364) that renamed an entire API family:
prefetchOptimizedEntries β†’ prefetchManagedEntries, OptimizedEntryPrefetchDescriptor /
ServerOptimizedEntryHandoff β†’ ManagedEntryDescriptor / ManagedEntryHandoff,
serverOptimizedEntries β†’ prefetchedManagedEntries, a moved getOptimizedEntrySourceKey, plus new
getEntries() batching. Merging it produced conflicts in the knowledge base β€” and this is exactly the
scenario the whole design exists for. pnpm knowledge:check pinpointed every source pointer the
rename had invalidated
, so each stale fact was re-anchored to the current declaring symbol against
the merged source, and main's genuinely-new facts were folded in β€” rather than a human eyeballing a
2,400-line diff and hoping to catch every renamed symbol. The merge lands with the full base green.
This is the steady-state value proposition demonstrated involuntarily: source changed, the graph
found the affected facts, and drift could not slip through silently.

What is deliberately not done yet

  • Native SDKs (iOS/Android). The symbol check uses the TypeScript compiler, so it does not resolve
    Swift/Kotlin symbols. Those guides are not refreshed in this PR; the decision on a native
    symbol-check (vs. file-level pointers) is deferred and called out in the skills.
  • A cross-guide consistency check. Replacing the deleted drift-log with a deterministic check that
    greps canonical shared phrases across a guide family is noted as a follow-up.
  • guides:check (structural enforcement). The recipe/fragment layer is enforced today by the
    guide-writer and the review roles following it, not by a validator. A guides:check that verifies
    a guide instantiated its recipe's fragments (grepping the verbatim spines) and holds the fixed frame
    order is deferred β€” the verbatim-spine and fixed-anchor decisions were made precisely to keep that
    future check cheap. Structure is currently agent-enforced; facts remain machine-enforced.
  • Recipe alignment of the other guides. Only the RN guide has been re-run through the new
    recipe/fragment layer so far. The other pipelined guides (Node, Web, React Web, Next.js App/Pages)
    already match the extracted fragment spines β€” since the fragments were extracted from them β€” but a
    confirming /iterate-guide pass per guide is a follow-up.
  • The four remaining stale guides (iOS SwiftUI/UIKit, Android Compose/Views) and the
    decision guide (choosing-the-right-sdk.md) are not refreshed here β€” the native four are blocked on
    the symbol-check decision above; both are follow-ups that run through the same workflow.

How to review this PR

  1. Read the design above β€” that is the substance. Push back on the model (KB-as-IR), the decisions
    (symbol anchors, deterministic-vs-agentic split, standards posture), and the role decomposition.
  2. Skim the mechanics β€” the grammar in documentation/internal/sdk-knowledge/README.md, the
    recipe/fragment model in documentation/authoring/README.md, the validator in
    scripts/validate-sdk-knowledge.ts, and the skills/commands under skills/ and .claude/. The
    "Guides and the knowledge base" section of CONTRIBUTING.md is the one-screen orientation.
  3. Do not attempt to line-review the guide/KB content. Instead, trust the gate: pnpm knowledge:check
    proves every fact resolves to real source. If you want to spot-check, pick one fact in
    node/node.md or native/react-native.md, follow its pointer into packages/, and confirm it is
    true β€” that is the loop working.

Draft so the design can be discussed before the remaining guides are run through the same workflow.

πŸ€– Generated with Claude Code

Tim Beyer (TimBeyer) and others added 30 commits July 9, 2026 10:39
…rve)

proseWrap: always reflowed every paragraph at printWidth 100, which split
source pointers and sentences across lines mid-token. preserve leaves authored
line breaks untouched β€” zero diff on existing files (all lines already ≀100),
and keeps a one-line source pointer on one line going forward.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…check validator

The internal SDK knowledge base recorded facts with free-text source pointers
(e.g. "source: accepted App Router guide" β€” circular; "OptimizedEntryResolver.ts:148-209"
β€” already drifted). Nothing verified them, so the base rots silently and guides
re-derive facts from scratch instead of relying on it.

Define a strict, machine-checked pointer grammar in the base README and enforce it
with scripts/validate-sdk-knowledge.ts (pnpm knowledge:check):

- Grammar tokens: <sdk>#<relpath>[#<symbol>], impl:<name>#<relpath>, concept:<slug>,
  kb:<relpath>, extern:<free text>. Semicolon-separated. NO line numbers β€” symbols
  are the anchor; line ranges drift on every edit.
- <sdk> keys are workspace package directory basenames, auto-discovered from
  packages/**/package.json, so every SDK family is covered with no hardcoded map.
- Symbol resolution uses the TypeScript compiler API (already a dep) to confirm the
  named identifier is actually declared β€” top-level decls, interface/type members,
  and class members/methods. No new dependency, no build, no tsconfig graph.
- Also enforces: no pointerless facts, no circular guide pointers, and per-SDK files
  match _template.md heading-for-heading.
- Split into scripts/sdk-knowledge/{markdown,source-symbols}.ts for readability.

Migrate the shared/ knowledge files (vocabulary, concepts, consistency-notes) onto
the grammar as the reference migration; they now pass knowledge:check.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a knowledge-check job gated on a new `knowledge` paths-filter that fires when
the SDK knowledge base OR any packages/**/src changes β€” deliberately NOT
markdown-excluded, since the KB is markdown and a source refactor can invalidate a
symbol pointer without touching a fact. This is the change-triggered propagation
edge: a rename that orphans a recorded pointer fails on the same PR.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…owledge:check gate

Update the sdk-knowledge-maintenance skill so its source-pointer rule references the
machine-checked grammar (symbol-anchored, never line numbers, never a guide), and make
`pnpm knowledge:check` the explicit finish gate. Note that CI runs it on packages/**/src
changes, so a rename that orphans a pointer must be fixed on the same PR.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…se drift

Team-shared .claude/settings.json Stop hook runs .claude/hooks/knowledge-check.sh:
when the working tree touched the KB or packages/**/src this session and
knowledge:check is failing, it feeds the (capped) report back as additionalContext
so drift gets fixed in the same turn. Advisory only β€” always exits 0, never traps
the agent in a stop loop. CI remains the hard gate; this is the in-session nudge.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Guide-authoring step 5 (verify APIs against source) now tells authors to check the
internal knowledge base FIRST β€” reusing facts already verified against source instead
of re-grepping β€” and to record newly verified facts back into it as a byproduct. This
is the propagation edge that lets each guide rely on the KB rather than re-deriving
everything from scratch.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Ship the docs writer β†’ newcomer reviewer β†’ technical-foundation reviewer loop as
portable, standards-based artifacts (plain SKILL.md; no MCP/skill-kit dependency):

- skills/guide-newcomer-review β€” read a guide cold as an average developer with no
  personalization background; report undefined jargon, skim-mode, unperformable steps,
  dishonest labels. Reader-experience only.
- skills/guide-source-verification β€” prove every load-bearing SDK claim against
  packages/**/src with file:symbol evidence, reuse KB facts already verified, and
  record newly verified facts back into the knowledge base. This closes the capture
  edge: the verifier's output IS the KB delta.

Add thin .claude/agents/{guide-writer,guide-newcomer-reviewer,guide-source-verifier}.md
wrappers (each points at its skill β€” the portable source of truth) and a
.claude/commands/review-guide.md that chains the roles and funnels durable learnings
back into the authoring skill (principles) or the knowledge base (facts).

Skills are discoverable via the existing skills/ β†’ .claude/skills and .agents/skills
directory symlinks, so they work in Claude Code and any agentskills.io-compatible tool.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rewrite every source pointer in web/nextjs-app-router.md to the machine-checked
grammar, replacing the circular "source: accepted App Router guide" references
(~6) with real symbols verified against packages/web/frameworks/nextjs-sdk/src
(createNextjsAppRouterOptimization, OptimizedEntry, NextAppAutoPageTracker, the
bound-component-types, request-handler, cookies helpers) and cross-SDK symbols in
core-sdk / react-web-sdk / api-schemas. Drops all line-number anchors and the stray
</content> artifact. pnpm knowledge:check passes for this file.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The prose `source:` matcher only reads tokens on the same physical line, so a pointer
wrapped onto a continuation line silently skipped validation (a fake symbol on a
wrapped line was not flagged). Add checkDanglingPointerLines: a line consisting only
of grammar tokens (each segment carries a `#` or known prefix) is flagged as a wrapped
pointer, enforcing the grammar's "one line per source pointer" rule. Prose
continuations ("The React Web guides…") have plain words and are not flagged.

Surfaced by the App Router KB migration.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…to single lines

Follow-up to the App Router migration: put every prose `source:` pointer on its own
single line (safe under proseWrap preserve) so all tokens are validated, and reword two
extern: texts that contained the literal token separator. knowledge:check passes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rewrite all ~90 pointer tokens in web/web.md to the symbol-anchored grammar
(pnpm knowledge:check passes). Beyond format, the migration re-verified every fact
against source and corrected drift the old line/path pointers had hidden:

- Moved-file paths: state/EventEmissionResult β†’ events/, state/Consent β†’ consent/,
  state/StatefulDefaults β†’ package root, handlers/AcceptedCurrentStateTracker β†’
  tracking/; a referenced RootElement.ts that no longer exists β†’ the real
  web-components/ContentfulOptimizationRootElement.ts.
- Non-declared anchors re-pointed to their true declaring symbols (Zod object consts,
  ResolvedData in the resolver, LogLevels in api-client).
- Circular "guide" pointers replaced with concrete symbols and concept: links.

Multi-line behavioral facts keep their full assertions, now anchored to the enclosing
symbol instead of drift-prone line ranges.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rewrite all ~90 pointer tokens in web/react-web.md to the symbol-anchored grammar
(pnpm knowledge:check passes). Re-verification against source corrected drift the old
pointers hid:

- Re-export barrels (index.ts, api-schemas.ts, server-optimized-entries.ts) carry no
  declarations; repointed to the real declaring files (OptimizationRoot.tsx,
  contentful/typeGuards.ts, OptimizedEntrySourceController.ts, etc.).
- Logger misattributed to core-sdk; createScopedLogger is declared in api-client.
- Consent action drift: setConsent/identifyUser/resetUser are React hook names on
  UseOptimizationActionsResult; the SDK methods are consent/identify/reset β€” pointers
  now cite both layers.
- Renamed constants (DEFAULT_WEB_ALLOWED_EVENT_TYPES, ANONYMOUS_ID_COOKIE) anchored
  correctly; circular "guide" pointers replaced with concept: links and real symbols.
- Fixed escaped-pipe table cells that desynced the source column.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rewrite every source pointer in web/nextjs-pages-router.md to the symbol-anchored
grammar (pnpm knowledge:check passes). ref-impl pointers β†’ impl:nextjs-sdk_pages-router#…,
cross-doc references β†’ kb:shared/…, framework conventions β†’ extern:, circular guide
pointers and line ranges β†’ verified file#symbol anchors in packages/**/src.

With this the entire SDK knowledge base passes knowledge:check: every source pointer
resolves to a real symbol and all per-SDK files conform to _template.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
README.md and _template.md carried a leftover </content> tag at EOF (an artifact from
the #358 knowledge-base scaffolding). Remove them so the meta files are clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The table-row parser split on every `|`, so a `Returns` cell like `ReactElement \| null`
became two cells and its `null` could be misread as the source column. Split on
unescaped pipes only and unescape `\|` within each cell (correct GFM semantics), so a
union-type cell is one value. Both Next.js KB migrations had to work around this by
rewording cells; now the parser handles them.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Restore and expand the explanatory comments lost when the header was condensed to fit
the line cap (the module split fixed the line count; this restores the docs). The
validator does non-obvious work and was under-commented.

- Header: the four checks (grammar/resolution/coverage/template), the two pointer
  carriers, per-file check scope, and why symbol anchors beat line numbers.
- extractPointers/handleTableRow: the stateful source-column tracking across a table's
  rows and why it is cleared when the table ends.
- checkSourceToken: the three resolution gates and which one catches a moved/renamed
  export. matchProseSource: why a wrapped pointer tail would be dropped.
- coverage heuristics (itemHasPointerNearby, isCrossReference): what each rejects and why.
- discoverSdkSrcRoots: why keys are derived not hardcoded, and the uniqueness invariant.
- source-symbols.ts: parser-only rationale, declarations-not-re-exports (the barrel-file
  drift the migration caught), and why variable declarations are handled separately.

No logic changes; knowledge:check still passes, lint and format clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…n't log it

consistency-notes.md was a persisted work-log β€” "Open items to reconcile when guides
2 & 3 land", "Resolved when guides 2 & 3 closed", "Backport candidate" β€” the in-progress
scratchpad from authoring the web guides in #358, committed as if it were knowledge. A
file that catalogs known-broken/divergent things instead of fixing them is an
anti-pattern, and it duplicated wording already owned by vocabulary.md/concepts.md.

- Delete the file. Its one durable fact (control-variant selectedOptimization nuance) is
  already recorded in web/web.md; the "preview-panel" entry was never real drift (correct
  per-framework prefixes), now stated as a plain fact in the pages-router KB row.
- Remove the "log cross-guide drift here" instruction from the sdk-knowledge-maintenance
  skill β€” the policy that produced the file β€” and replace it with "fix drift when you find
  it; shared wording lives once in shared/ and guides reuse it." Update the finish-checklist
  item to match.
- Drop the file from the README org tree and the dead kb: pointer in the pages-router KB.

knowledge:check passes; no dangling references remain.

Follow-up (noted for the docs workflow): enforce cross-guide consistency with a
deterministic check that greps the canonical shared phrases across a guide family, rather
than any prose record.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…oints

The docs-generation workflow, driven by a slash command chaining the three role agents
(lowest machinery, human-in-the-loop). One command, two entry points:

- /author-guide classifies the target: an existing guide (or an SDK that already has one)
  β†’ refresh; an SDK with no guide β†’ new. Both paths converge on the same review loop, so
  quality is identical either way.
- Draft (guide-writer) β†’ review loop (delegates to /review-guide: newcomer + source
  verification, concurrent) β†’ gate (blockers fixed, claims corrected, knowledge:check +
  format pass) β†’ funnel learnings back into the skill (principles) or KB (facts), fixing
  cross-guide issues rather than logging them.

Back the new capabilities in the agents:
- guide-writer now documents the refresh path (diff against the current skill; the tells
  of a pre-current guide β€” no Quick start / Before you start, monolithic flow, numbered
  headings, setup table, missing copy/adapt labels β€” and restructure preserving content).
- guide-source-verifier now creates a KB family file (node/, native/) from _template.md
  when none exists, and flags that #symbol resolution is TS-only (Swift/Kotlin deferred).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The graph had only the source→KB direction (a fact's source: pointer). For incremental
docs updates we also need the downstream direction: given a changed KB fact, which guide
must be recomposed. Each per-SDK file now declares it with a
`<!-- feeds-guides: documentation/guides/<guide>.md -->` marker under the title, and
knowledge:check requires the marker and that its targets resolve.

This is the missing edge that lets an incremental update scope a KB change to the exact
guide(s) that consume it, instead of recomposing the whole family.

Added markers to the four web KB files and the template; documented in the README.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…sion role

The pipeline was missing the role that derives facts FROM source. Verification was
guide-triggered (read a guide, re-derive its claims from source), which re-comprehends
code per guide β€” the expensive path on every change. This skill makes the knowledge base
a first-class derivation target from source, updated independently of any guide:

- BOOTSTRAP: read an SDK's exported surface once into a new KB file. Expensive, once per SDK.
- INCREMENTAL: a source change scopes the work β€” re-verify only facts whose source: pointer
  hits a changed file (knowledge:check finds broken pointers), capture new exports in that
  area, leave untouched facts alone. This is the cheap common case.

The KB memoizes comprehension so guides and reviews read facts, not code. Ships the skill
+ sdk-knowledge-author agent. It reports which guides each change affects (via feeds-guides)
so the composition step can scope the guide update.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e re-derivation

guide-source-verification re-read packages/**/src to prove each guide claim, which
re-comprehends code per guide β€” the expensive path the knowledge base exists to avoid.
Reshape it into a consistency check against the KB:

- A claim is confirmed when it matches a KB fact and knowledge:check passes (fast path).
- A claim that contradicts a fact is a guide bug β†’ correction to the writer.
- A claim with no backing fact is ESCALATED to sdk-knowledge-author (which owns reading
  source) β†’ the base gains the fact or the claim leaves the guide. The verifier no longer
  reads source itself (Edit dropped from its tools; it reports, it does not mutate).

Bootstrap (SDK with no KB file) still runs after sdk-knowledge-authoring has created the
file, so this review is always guide-against-KB, never guide-against-source.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ssing facts

The writer's step 5 and checklist told authors to verify every API "against the SDK
source" β€” re-grepping packages/**/src per guide. Reshape composition to read the
knowledge base as the source of truth:

- Every SDK claim in a guide must trace to a verified fact in the KB; compose from facts,
  do not re-grep source. The reference impl is for shape/"adapt" starting points, not truth.
- A missing fact is escalated to sdk-knowledge-authoring (which owns reading source), then
  composed from the fact it adds β€” comprehension stays in one place.
- Updated the authoring skill step 5, the authoring-checklist source-verification item, and
  the guide-writer agent to match.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Split the two lifecycle modes into distinct commands so the cheap path is the default:

- /author-guide is now the BOOTSTRAP path only β€” comprehend source into a new KB file
  (sdk-knowledge-author) FIRST, then compose the guide from those facts (guide-writer),
  then review. Knowledge first, guide second. Expensive, once per SDK.
- /refresh-docs is the new INCREMENTAL path β€” the steady-state default. A source diff
  scopes the work: deterministically find affected KB facts (source: pointers into changed
  files) and the guides that consume them (feeds-guides), reconcile only those facts
  (sdk-knowledge-author incremental), recompose only the dependent guide passages, review
  only what changed. Never re-comprehends an SDK or rewrites whole guides.
- /review-guide updated: the verifier does a KB lookup (confirmed/contradicts-KB/
  no-backing-fact) and escalates fact gaps to sdk-knowledge-author instead of re-deriving
  from source or recording facts itself.

This makes the knowledge base a compiler IR: comprehension is memoized once, and small
source changes cost a scoped reconcile + recompose, not an 800k-token rebuild.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e/ KB

First run of the docs workflow (/author-guide) on a real target. Refreshes the Node
integration guide from the pre-current structure (a `## Required setup` inventory table,
change-narration prose) to the current archetype: plain-language five-sentence intro
defining variant/experience/Experience API/resolving/baseline fallback/profile, two-milestone
framing, `## Before you start` prerequisites with the authored-variant gotcha, a performable
verify step, and a source-grounded `## Troubleshooting`.

Bootstraps the first Node knowledge-base file (documentation/internal/sdk-knowledge/node/node.md)
from _template.md: the stateless ContentfulOptimization + forRequest()β†’CoreStatelessRequest
model, default ['identify','page'] allow-list, consent/canPersistProfile axes, trackView profile
contract, locale precedence, request-scoped options, and the reader-owned ctfl-opt-aid cookie β€”
each with a resolvable source pointer, feeds-guides set to this guide.

Review: all 41 load-bearing claims traced to source; 2 newcomer blockers + 15 friction items
fixed. knowledge:check and format clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…uthoring checklist

The Node guide review surfaced durable reader-experience rules; encode them as mechanical
checklist assertions (principles, not SDK facts) so future guides catch them upfront:
result-envelope fields defined at first use; near-identical/shorthand identifiers
disambiguated; SDK-constant↔literal-value mapping stated once; helpers defined or
forward-pointed; no orphan cross-SDK API in warnings/tables; source-language switches
announced with run instructions.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…eserve

Running proseWrap: preserve, the one file still carrying proseWrap: always drift
(wrapped link-reference definitions) is normalized to single-line link defs. Content-free
reflow β€” done once here deliberately so it cannot resurface piecemeal in unrelated diffs
(it briefly rode along in the Node docs run before being reverted).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Node-run feedback: subagents ran bare `pnpm format:fix`, which reformats the whole tree
and pulled an unrelated concept doc into the diff (later reverted). The command gates said
bare `format:fix`; make them explicitly pass the touched paths and warn against the bare
form. The skills already used the `<file>` form.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ap native/ KB

Second workflow run, and the first exercising the RESHAPED bootstrap (knowledge-first)
order: sdk-knowledge-author ran before guide-writer, and the writer escalated a missing
fact instead of re-grepping source β€” proof the read-facts-not-code discipline holds.

Bootstraps the first native/ family file (documentation/internal/sdk-knowledge/native/
react-native.md): async ContentfulOptimization.create() factory + OptimizationRoot/Provider,
managed-vs-manual entry union, AsyncStorage identifier ownership, screen/viewport/tap
tracking with verified defaults (minVisibleRatio 0.8, dwellTimeMs 2000, tap threshold 10),
the RN ['identify','screen'] pre-consent allow-list, NetInfo/AppState/Expo quirks, and the
SDK-fixed content-model fields (nt_experiences→nt_experience→nt_variants/nt_audience) —
each with a resolvable source pointer, feeds-guides set to this guide.

Guide: `## Required setup` table β†’ `## Before you start`, teach-first intro, section order
fixed to the archetype (22 TOC anchors resolve). Review: 0 contradicts-KB; 7 no-backing-fact
claims all resolved by adding the backing fact from source (none unfounded); ~18 newcomer
findings fixed. The onOfflineDrop callback shape differs from the iOS/Android sibling guides
β€” left as a legitimate per-SDK difference (separate Swift/Kotlin source, unverified here).

Also funnels the RN review's durable reader-experience rules into the authoring checklist
(principles, not SDK facts): prose state-nouns defined at first use across sections;
SDK-fixed content-model identifiers glossed with ownership; orphan rule extended to event
names; render prop defined in prose; every promised proof verified; runtime root component
as a diff not a paste-over; native build step inline in the quick start; Core sections open
with a bridge not a recap.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…vention, review overlap

The React Native run surfaced real workflow-spec gaps; fix them:

- Make "refresh an existing guide" a first-class sub-case of /author-guide. The command's
  premise is "the KB has no file yet" β€” but the guide often already exists (Node and RN were
  both 900+ lines). Step 2 now branches new-vs-refresh explicitly so an operator doesn't
  expect a blank compose.
- Formalize the escalation handoff. The writer had invented an ad-hoc
  <!-- ESCALATE(sdk-knowledge-author): … --> marker; define it in the author-guide/guide-writer
  skills and enforce that none survives to a finished guide β€” the validator now scans both the
  KB and documentation/guides/** and fails on any ESCALATE marker (and the CI knowledge filter
  now includes guides so a guide-only change triggers it).
- Resolve the review-loop overlap. /review-guide is the self-contained review/fix/funnel/validate
  loop; /author-guide and /refresh-docs now delegate to it in ONE pass and only add their
  mode-specific gate checks, instead of describing the same loop twice. Also clarified it is a
  skill/command, not a stray file to hunt for.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ope beyond web

Two RN-run hygiene findings:

- The template's "Last verified against packages/**/src during the guide rewrite" boilerplate
  is untrue for a bootstrap (no rewrite happened). Reword mode-neutrally to "each with a source
  pointer verified against packages/**/src" across the template and all six KB files.
- shared/concepts.md and shared/vocabulary.md were titled "web family", but a native/ family now
  consumes them and the concepts are genuinely SDK-neutral (they live in core-sdk). Widen the
  stated scope to the families that actually consume them, and drop the prose workaround the RN
  file had added to explain the mismatch.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
# Conflicts:
#	documentation/guides/integrating-the-node-sdk-in-a-node-app.md
#	documentation/internal/sdk-knowledge/shared/concepts.md
#	documentation/internal/sdk-knowledge/shared/vocabulary.md
#	documentation/internal/sdk-knowledge/web/nextjs-app-router.md
#	documentation/internal/sdk-knowledge/web/nextjs-pages-router.md
#	documentation/internal/sdk-knowledge/web/react-web.md
#	documentation/internal/sdk-knowledge/web/web.md
@phobetron

Copy link
Copy Markdown
Collaborator

It might be worth having Alex take a look at this process.

Tim Beyer (TimBeyer) and others added 4 commits July 10, 2026 09:49
…s) vs behavior (from KB)

The blanket "compose from the KB, never read source" rule conflated two different
comprehension costs. Reading an INTERFACE β€” a symbol's existence, signature, prop/config
names & types, optionality, union shape, return type, import path β€” is cheap, deterministic,
and self-verifying: the type system is its always-current store, and copying it into the KB
just duplicates what the compiler already guarantees. Tracing BEHAVIOR β€” fallback contracts,
dynamic-render forcing, batching/chunking, defaults, identifier ownership, cross-SDK semantics
β€” is the expensive, judgment-heavy work the KB exists to memoize.

Refine the rule to: read interfaces directly from source/types; compose behavior from the KB;
re-trace behavior never; escalate only a missing BEHAVIORAL fact.

- optimization-guide-authoring: step 5 + checklist now source claims by kind, with the
  interface/behavior boundary spelled out (and the "don't re-trace under cover of a lookup" trap).
- guide-writer: reads interface from source, composes behavior from KB, escalates behavioral gaps.
- sdk-knowledge-authoring / sdk-knowledge-author: scope narrowed to behavior/ownership/defaults/
  cross-SDK semantics β€” anchored to the interface symbol, but content is behavior, not signatures.
- guide-source-verification / guide-source-verifier: two verification paths β€” interface checked
  against the types, behavior against the KB; escalate only behavioral no-backing-fact.
- review-guide / author-guide + sdk-knowledge-maintenance: aligned wording.

No validator change and no KB content change; knowledge:check still passes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Introduce documentation/authoring/ as the structural source of truth for guides,
separate from the authoring skill (voice) and the knowledge base (facts). A recipe
per archetype (integration, decision, supplemental) holds the section spine; two
fragments (personalization explainer, authored-variant gotcha) hold reusable
reader-facing prose with knowledge-base-filled slots.

Every recipe/fragment splits into ## Context (agent-only rationale + fill rules,
never rendered) and ## Template (the prose that becomes guide output). Fragments are
context-free definitions; the recipe is the caller that names which fragments it
composes and where. An LLM (guide-writer) instantiates them β€” verbatim spine for
fixed sentences, composed-around-a-fixed-anchor for SDK-shaped bullets.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ture out of the skill

Move guide structure out of the optimization-guide-authoring skill and into the new
documentation/authoring/ recipes, resolving the two-sources-of-truth problem (skill
refs vs. sibling guides). The skill now owns voice, the copy-vs-adapt honesty
principle, and workflow only.

- guide-writer agent: compose structure from the archetype recipe (authoritative
  over sibling guides); instantiate fragments verbatim, filling slots from the KB.
- author-guide / review-guide commands: read structure from recipes; funnel durable
  structure/shared-prose findings to recipes/fragments, voice to the skill, facts to
  the KB.
- guides/AGENTS.md: point at recipes for structure, the skill for voice.
- Delete the three migrated reference templates (integration-guide-template,
  decision-and-recipe-templates, before-you-start); repoint authoring-checklist.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ared/

A guide review escalated the "Experience API returns a profile" claim as
behavioral-no-backing-fact: it lived only in node/node.md, so the RN guide's
explainer had no backing fact in the base that feeds it, violating the
"capture each shared fact once in shared/" rule.

Add shared/concepts.md#experience-response-payload: an accepted Experience call
returns OptimizationData { profile, selectedOptimizations, changes }, and a
stateful SDK applies it to its signals. Reference it from native/react-native.md;
dedupe node/node.md to link the shared shape while keeping Node-specific
request-client behavior.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment thread .claude/hooks/knowledge-check.sh Outdated
Comment thread scripts/validate-sdk-knowledge.ts Outdated
Comment thread scripts/validate-sdk-knowledge.ts
Tim Beyer (TimBeyer) and others added 4 commits July 10, 2026 12:43
…ment + checklist

First full pipeline run on the new recipe/fragment system (RN guide). The writer
realigned two intro bullets to the personalization-explainer fragment spine β€”
reconciling real drift between two pipelined guides (RN "builds/keeps consistent"
vs the canonical "returns/uses to keep consistent") β€” proving the primitive.

Fix defects review surfaced in the new artifacts:
- Fragment opener said "N sentences" but bullets are multi-sentence β†’ "N points",
  with the four/five count tied to profile-bullet inclusion.
- Composed entry-source bullet oversold freedom (a reorder license that invited
  the drift the writer then undid) β†’ near-verbatim, fixed order.
- Two new authoring-checklist rules: stated-count-matches-content, and a verify
  step's inspection mechanism must be present in the pasted quick-start code.

Apply the RN reader-experience fixes: profile inbound/outbound consistency,
performable screen-event verify (logLevel in the quick start), nt_experiences vs
nt_experience gloss, changes/flag/merge-tag gloss, consent placement, include: 10 why.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e-derivation

Give technical writers a snappy inner loop: tune phrasing, tone, or sequence β€” or
edit a recipe/fragment and re-render every guide that instantiates it β€” without
running the whole pipeline. It recomposes only the affected prose from the
EXISTING knowledge base; no source read, no fact re-verified, because a pure-prose
change moves no fact.

Hard-stop guardrail: the moment an edit would change what a guide asserts about the
SDK, it stops and hands off to /refresh-docs (source changed) or /review-guide
(verify vs the base) rather than silently drifting a fact. /review-guide remains
the final gate.

Wire it into the command family: cross-reference from /refresh-docs and surface it
in the authoring README's Iterating section.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…nd writers

Add discoverable entry points to the agent-driven docs system without duplicating
the self-documenting artifacts:

- CONTRIBUTING.md gains a "Guides and the knowledge base" section under
  Documentation β€” the three-layer pipeline, the four slash commands and when to
  use each, and knowledge:check β€” pointing into the authoring and sdk-knowledge
  READMEs for depth (dev/contributor signpost).
- documentation/authoring/README.md gains a "Start here (technical writers)"
  opening: edit a recipe/fragment, re-render with /iterate-guide, gate with
  /review-guide (writer front door).

No new standalone doc; both are thin pointers, one source of truth per fact.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…tch, coverage gap

Three fmamud review findings on PR #369, all verified against the code:

- Stop hook: `packages/**/src` matched zero files in `git status --porcelain`
  (empirically confirmed); use `packages/**/src/**` so a source-only change
  actually triggers the in-session knowledge:check.
- Validator meta-file exemption used `path.basename`, which would also exempt a
  future nested `native/README.md` from all checks. Match by path relative to the
  knowledge dir via a new `isMetaFile()`, applied at both call sites.
- Pointer coverage keyed off `line.endsWith('.')`, silently exempting any
  single-line fact not ending in a period (e.g. one ending in a backticked
  symbol) β€” narrowing the silent-rot guard. Replace with a fact-unit-end test
  (`isFactUnitEnd`) plus a label-lead-in skip; verified against the live KB (~122
  wrapped facts still pass β€” no false positives) and that a period-less unsourced
  fact is now caught.

Extracted the coverage shape-helpers (isFactUnitEnd, isLabelLeadIn, looksLikeFact,
isCrossReference) into sdk-knowledge/markdown.ts to stay under the module line-cap
β€” splitting into modules, per the repo rule, not disabling the lint.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@wiz-inc-38d59fb8d7

wiz-inc-38d59fb8d7 Bot commented Jul 10, 2026

Copy link
Copy Markdown

Wiz Scan Summary

Scanner Findings
Vulnerability Finding Vulnerabilities -
Data Finding Sensitive Data -
Secret Finding Secrets -
IaC Misconfiguration IaC Misconfigurations -
SAST Finding SAST Findings 2 Medium
Software Management Finding Software Management Findings -
Total 2 Medium

View scan details in Wiz

To detect these findings earlier in the dev lifecycle, try using Wiz Code VS Code Extension.

…ystem

Conformance pass (guide-writer per guide, then a cross-guide consistency review)
bringing Node, Web, React Web, Next.js App Router, and Next.js Pages Router into
verbatim instantiation of the personalization-explainer and authored-variant-gotcha
fragments. Narrow edits only β€” structure already conformed.

- Intro opener "N sentences" β†’ "N points" across all five (five for Node, which
  includes the profile bullet; four for the web family).
- Entry-source hand-off bullet normalized to the fragment: the fixed
  baseline-fallback anchor made exact ("…the **baseline fallback**", not "which is
  the …"), and the managed-fetch clause added where the KB confirms managed
  fetching (React Web, App Router, Pages Router β€” the earlier intros omitted it).
- Fixed a parallel-run divergence: Pages Router kept the old "the SDK's only job
  is…" lead while gaining the managed-fetch clause (self-contradictory); aligned it
  to the fragment's "…and the SDK sits at that hand-off:" lead, matching the other four.

knowledge:check green; format:fix per file; all TOC anchors resolve.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@TimBeyer Tim Beyer (TimBeyer) changed the title πŸ“š A self-maintaining docs pipeline: source β†’ knowledge base β†’ guides πŸ“š A self-maintaining docs pipeline: source β†’ knowledge base β†’ guides [NT-3625] Jul 10, 2026
…antifier

The knowledge filter listed six mutually-exclusive positive globs while the
paths-filter step runs with `predicate-quantifier: every`. Under `every` a
changed file must match every pattern in a filter, but no path lives in two of
these directories at once, so the filter was unsatisfiable and always emitted
`false` β€” the `knowledge-check` job (guarded by `knowledge == 'true'`) could
never run, even for a direct edit to the knowledge base. A local Stop hook
running `pnpm knowledge:check` masked this by keeping the check green on dev
machines.

Collapse the six globs into one brace-alternation, matching the idiom of the
other filters, so `every` is trivially satisfied and the job fires whenever a
file touches any listed area. Preserves the deliberate no-markdown-exclude
intent (the KB is markdown).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…erbose

The validator printed an identical βœ“ whether it checked the whole base or
nothing, so a degenerate run (broken glob, misrooted scan) was
indistinguishable from a real pass β€” the same class of silent-nothing failure
that hid the dead CI filter.

Always tally what was inspected into the success line (source pointers, fact
files, ESCALATE-scanned files); "0 pointers" now stands out. Add opt-in
`--verbose`/`-v` (or KNOWLEDGE_CHECK_VERBOSE) that logs every file and pointer
as it is checked, and enable it in the CI job so the pipeline log shows the
base was actually traversed. Local/default runs stay quiet.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment thread scripts/validate-sdk-knowledge.ts Outdated
Comment thread .claude/hooks/knowledge-check.sh Outdated
@@ -0,0 +1,720 @@
/* eslint-disable no-console -- This CLI prints a machine-readable validation report. */

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Repo instructions explicitly say not to add eslint-disable, eslint-disable-next-line, or eslint-disable-line unless requested. This adds a file-wide no-console disable. Please restructure CLI reporting to satisfy lint without the disable, or get explicit maintainer approval for the exception.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair flag β€” AGENTS.md does forbid adding eslint-disable without explicit sign-off. Signing off explicitly here, for two reasons:

Precedent. Every CLI script in the repo opens with this same file-wide disable and a descriptive reason:

  • scripts/list-npm-package-targets.ts β€” /* eslint-disable no-console -- This CLI prints machine-readable records and errors. */
  • lib/mocks/scripts/generateTypeDefinition.ts, fetchEntries.ts, copyGeneratedTypeDefinition.ts β€” /* eslint-disable no-console -- CLI */

This file follows that pattern exactly (line 1, with a reason). Restructuring this one alone would make it the odd one out among its siblings.

Reasoning. no-console guards against stray debug logging leaking into library/runtime code. For a CLI whose entire job is to print a validation report, console output is the intended behavior β€” so a file-wide disable is the honest scope: the whole file is a reporter, not a module with an incidental log. It's categorically unlike a rule such as no-explicit-any, where a disable would mask a real type-safety hazard; there's no accidental side effect being suppressed here, just an intentional output channel.

Leaving as-is on that basis.

Address PR review from Lotfi-Arif.

- Coverage gap (validator): per-SDK files state facts as table rows, but
  checkPointerCoverage graded only list items, and pointer extraction skips a
  row whose source cell is empty or a β€”/none placeholder. So a table row could
  state an SDK fact with no pointer and pass. Add checkTableSourceCoverage:
  every non-divider body row under a `source` column must carry a real pointer.
  Lift the table-source walk both consumers share into markdown.ts
  (eachTableSourceCell) rather than duplicating it; extractPointers now reads
  cells through it and handleTableRow is gone. Behavior-preserving for extraction
  (still 670 pointers); verified an emptied and a β€”-placeholder cell now fail.

- Stop hook: the validator scans documentation/guides/** for stray ESCALATE
  markers and CI triggers knowledge-check on guide changes, but the hook
  early-exited unless the KB or package source changed, so a guide-only session
  could leave an ESCALATE marker with no advisory. Add documentation/guides to
  the changed-path check.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants