Skip to content

docs(emitters): design the docs emitter and its shared examples - #1337

Open
OmarAlJarrah wants to merge 7 commits into
docs/emitters-generationsfrom
docs/emitters-docs-artifacts
Open

OmarAlJarrah wants to merge 7 commits into
docs/emitters-generationsfrom
docs/emitters-docs-artifacts

Conversation

@OmarAlJarrah

Copy link
Copy Markdown
Member

Summary

The docs emitter was one bullet: it "reuses the shared plan verbatim (primary response/content, …)" and lowers to a docast. Every docs target hits the questions below on its first build, and so do the samples, tests and mocks that share its values. They are one contract: what the non-SDK artifacts render, and from which shared facts.

  • Every response (docs/emitters: the docs emitter must render every response and both directions of a type #720). The primary response and content are an SDK's ergonomic pick, so in the docs they order the page and decide nothing else. Every Response, ErrorCase and Content is rendered. A type is rendered per direction through its ModelShape, so a write-only property is in no response table and requiredness reads in its own direction.
  • Routes, links, versions (docs/emitters: the docs emitter needs a route plan, ID-keyed links, versions #585), as the issue and its comment asked:
    • a catalog holding no URL comes first, and a route plan derives URLs from it through templates in the docs options. The issue's "injected resolver" is data rather than a function, for the reason docs/emitters: the plan's version hashes a policy whose functions have no stable bytes #1014 gave about functions in a policy;
    • the route plan is total: every OpID and page-worthy type has a route and every PropID an anchor, with a diagnostic for anything left;
    • two entities on one URL are an error diagnostic, checked after the templates apply and across every route kind. That avoids the failure the issue quotes, where tied pages vanish with only a printed warning;
    • links are ID-keyed requests the route plan answers;
    • versions keep identity. The contract rule that an emitter sees one snapshot stays. The engine computes each snapshot's route table, a pure function, and passes them all in EmitterInput.Routes behind Capabilities.CrossVersion, so a switcher links one entity across versions, and an absent entity resolves to a "not in this version" page.
  • Agent documents (docs/emitters: emit agent-facing Markdown and llms.txt from the docs emitter #586). Markdown per operation (aggregate and per target) and llms.txt at site, service, version and target scope go through the same route plan. "Safe" comes from OpPlan.Effects, and "destructive" only from a declared hint or promoted extension, as the issue's comment corrected. Spec text is escaped and raw HTML is never passed through.
  • Samples from the SDK emitters (docs/emitters: docs usage examples need the SDK emitter's symbols, which an emitter never sees #699). Each SDK emitter returns EmitterOutput.Samples per operation, and the engine runs the SDK targets first and hands the docs emitter EmitterInput.Samples, so a call is spelled by the target that generates it. A sample that fails to render is a diagnostic. The templates table now says so.
  • One set of examples (docs/emitters: one set of examples for docs, snippets, tests and mocks #703). Plan.Examples holds, per type, property, parameter and operation, the declared examples, else one synthesized under plan.Policy.Examples: valid against the constraints, bounded in depth and items, Inferred, and seeded from the correlation key. Docs, samples, SDK tests and the mock share it. This departs from the issue's "persisted in the manifest": seeding makes a value reproducible from its inputs, and a value that changes because its schema did had to change, so persistence would add state without adding stability. A value the synthesizer cannot make valid, such as an unsatisfiable pattern, is reported, never emitted invalid.
  • Documentation extensions (docs/emitters: documentation extensions need a docs-policy reader #722). These are read by a mapping in the docs options, on the rules of the embedded reader from docs(emitters): layer runtime policy and read embedded policy #1332, with an empty default mapping, since each key is one tool's spelling. An internal marker is the filter pass's to read, never the docs emitter's, so the docs never describe an SDK other than the one generated. A search index is an optional artifact.
  • A CLI target (docs: plan a CLI emitter target derived from IR facts #587). Settled as a documented extension point beyond the milestones, not a milestone target, since nothing depends on it. milestones.md and architecture.md §2.3 name it. A table maps each CLI behavior to the IR or plan fact it reads. Flag constraints come from union branches by PropID and flag names from ID-keyed collision resolution, which designs out the failure modes the issue reproduced (leaf-name keys, foo_bar against foo.bar, dropped deep fields, unions beside properties).

T-39 pins the docs rules and T-40 the shared examples.

Closes #720
Closes #585
Closes #586
Closes #699
Closes #722
Closes #703
Closes #587

Test plan

  • The new Sample, RouteTable, Example and ExamplePolicy declarations, extracted verbatim, pass go vet.
  • Every docs-emitter fact the text cites (ModelShape, Docs.Refs, ResourceInfo, ir.Example, the filter and version-slice passes) checked against its document.
  • CI: the doc tests over the new anchors and links.

Stacked on #1336 (#1329 ← #1331 ← #1332 ← #1334 ← #1335 ← #1336). Merge in order. After each base lands, "Update branch" is a merge of main that conflicts nowhere.

The docs emitter reused the SDK's primary response and content, had no
design for URLs, links, collisions or versions, spelled usage examples
it could only re-derive, and left documentation extensions and agent
documents unaddressed. Every artifact would also synthesize its own
example values.

The docs now render every response, error case and content, the
primary only ordering them, and each type's request and response shapes
apart. A route plan derives every URL from an ID-keyed catalog through
templates, refuses any two entities on one URL after the templates
apply, resolves links as ID requests, and keeps one entity's identity
across version snapshots through route tables the engine passes in.
Agent Markdown and llms.txt share those routes and escape spec text.
SDK emitters return a usage sample per operation for the docs, and the
plan holds one set of examples, declared first and otherwise seeded
from each entity's correlation key, for docs, samples, tests and mocks.
Documentation extensions are read by mapping, internal markers left to
the filter pass, and a CLI is planned as an extension point built from
IR facts.
The docs emitter now receives every SDK target's usage samples and every
version snapshot's route table, but the shared refiner context carried
neither, so a docs refine step could not read them. Ctx holds both.
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.

1 participant