Skip to content

docs(planning): draft document-scaffolding — C-0011 capability + D-0017 generation gaps#201

Open
sksizer wants to merge 3 commits into
mainfrom
claude/planning-document-scaffolding-70tvgl
Open

docs(planning): draft document-scaffolding — C-0011 capability + D-0017 generation gaps#201
sksizer wants to merge 3 commits into
mainfrom
claude/planning-document-scaffolding-70tvgl

Conversation

@sksizer

@sksizer sksizer commented Jul 3, 2026

Copy link
Copy Markdown
Owner

Summary

Two draft planning docs exploring contract-driven document generation — turning a contract into an empty-but-valid starting markdown file. This is the architecture/product exploration behind the docs-site reframing in the companion PR (#200); it can land on its own timeline.

It mirrors the existing config-scaffolding pairing (C-0008 capability / D-0009 decision) with the transform reversed:

Config scaffolding (shipped) Document scaffolding (this)
Direction docs → contract (init) contract → document (new)
Capability C-0008 C-0011
Decision D-0009 D-0017

docs/planning/capabilities/C-0011-document-scaffolding.md — product perspective

A markdown-contract new <contract> verb that emits a conforming starting document: required frontmatter keys with starter values, required sections in order, and leaf stubs (table header row, checkbox list, fenced code). Framed as the create door beside validate/read, and the primitive a template engine or a CMS "new document" button would build on — without markdown-contract becoming either.

docs/planning/decisions/D-0017-contract-to-document-generation.md — engineering gap analysis

What it would take to meet the gaps:

  • Deterministic from the section grammar: sections, order, nesting/heading depth, anchors, per-leaf block stubs — a straight walk of the same IR the validator traverses.
  • Frontmatter splits by authoring path: the declarative (YAML) vocabulary inverts into starter values; arbitrary combinator Zod is best-effort (via z.toJSONSchema()), and .refine()/.transform() are non-invertible.
  • The one hard gap — inverting pattern regexes and typed table/list cells — with a proposed optional example:/placeholder: annotation to close it, plus a --fill policy fallback.
  • Round-trip-to-valid property (validate(new(contract)) → zero structural findings), mirroring init's accept-by-construction self-check.

Both are status: open/proposed, need_human_review: true, and each ends with an open-questions section. Drafts for review — not a commitment to build the new verb.

Testing

  • Built packages/core and ran the dogfood validator over docs/planning: No findings.
  • Validated each new doc against contracts/decision.contract.yaml / contracts/capability.contract.yaml directly: 0 findings each (required sections, frontmatter patterns, and the References-cites-a-wikilink rule all pass).

… gaps

Add two draft planning docs exploring contract-driven document generation
— the "basis for a template engine" the Why page now points at — as the
dual of config scaffolding (C-0008 / D-0009: docs → contract; this:
contract → document).

- C-0011 (capability, product view): a `markdown-contract new <contract>`
  verb that emits an empty-but-valid starting document; the create door
  beside validate/read; framing for the template-engine and CMS claims.
- D-0017 (decision, engineering gap analysis): what generates
  deterministically from the section grammar, where frontmatter inverts
  (declarative IR) vs. is best-effort (combinator Zod), the one hard gap
  (regex pattern / typed cells) and the proposed example:/placeholder:
  annotation to close it, the proposed surface, and the round-trip-to-valid
  property.

Both are status open/proposed (draft), need_human_review, and validate
clean against contracts/decision + contracts/capability via the dogfood
config.
@sksizer
sksizer force-pushed the claude/planning-document-scaffolding-70tvgl branch from 3f8bab9 to acf3afe Compare July 3, 2026 21:36
`new` at a contract and it walks the contract's declared shape and emits a fresh
`.md` whose structure passes that contract by construction.

The generated document is a **filled outline**: YAML frontmatter with every

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

I also had an idea that we could emit a typed function over the contract, or via the templates wrapper library that provides a typed data structure of the values to fill in ie:

contract defines frontmatter fields like:

let contract = Contract(
front:matter: {
status: <active|closed|pending>
})

then the template.create(contract).( {frontmatter: {status: "closed"}})

And it automatically puts those values in the right place.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Great idea — captured it in d7afe85. It's the write-dual of typed consumption ([[C-0002-typed-consumption]]): the contract that types doc.frontmatter.status for reading types the values argument for writing.

  • C-0011 — new "Typed builder — the write-dual of read" section with your sketch: template.create(contract)({ frontmatter: { status: "closed" } }), where the input type is derived from the contract and create({}) degenerates to the blank skeleton.
  • D-0017 — decision step 5: the input type comes from z.input<> / the declarative fields (frontmatter) and the grammar + leaves (body — a table reuses the typed-row shape core/model.ts already derives), the factory places values, and — the nice payoff — a bad enum member or missing required field becomes a compile-time type error instead of a post-hoc "fill me in" finding.

One contract, typed both ways, so a builder can't place a value the validator would reject. Both docs still validate clean.


Generated by Claude Code

sksizer added 2 commits July 4, 2026 00:19
Fold in the reviewer's idea: beyond emitting a blank skeleton string, the
scaffolder can expose a typed factory template.create(contract)(values)
whose values argument is typed by the contract — the write-side dual of
typed consumption (C-0002).

- C-0011: new "Typed builder — the write-dual of read" section, plus a
  two-shapes library entry-point bullet.
- D-0017: decision step 5 (input-type derivation, placement, stronger
  compile-time round-trip, scope), an Options-considered bullet, and
  C-0002 added to related.

Both still validate clean against contracts/decision + contracts/capability.
…ding

Add spec-level example code for the C-0011 / D-0017 scaffolding surface under
tests/proposed/, following the harness convention (stubs throw until the plane
lands; types type-check now, behavioural tests stay skipped):

- document-scaffolding.proposed.ts — proposed types/interfaces (ScaffoldOptions,
  ScaffoldInput<C>, TypedBuilder<C>, scaffold(), template.create()) with throwing
  runtime stubs.
- document-scaffolding.todo.test.ts — example usage against a real contract; the
  type layer runs under tsc (an enum-narrowed status field, a @ts-expect-error on
  an out-of-enum value), the behavioural suite is describe.skip behind IMPLEMENTED.
- README.md — what tests/proposed/ is and why it sits under tests/.

Lives under tests/ so it is type-checked (tsconfig includes tests) but never built
into dist (build is src-only) nor counted in src coverage. C-0011 now points at it.

Verified: typecheck 0, lint 0, full suite 683 passed / 5 skipped, 0 proposed files
in dist, both planning docs still validate clean.
@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying markdown-contract-docs with  Cloudflare Pages  Cloudflare Pages

Latest commit: f3b8e14
Status: ✅  Deploy successful!
Preview URL: https://82138dd5.markdown-contract-docs.pages.dev
Branch Preview URL: https://claude-planning-document-sca.markdown-contract-docs.pages.dev

View logs

and writes a document that satisfies it. The same declarative shape drives both
directions, so a scaffolded document and the contract that gates it can never
describe different things.
- Scaffolding emits **shape, never prose.** The generator produces the document's

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

if never emits prose, but given that contracts cn test prose content - doesn't this make it impossible to satisfy the poor statement that new rads a contract and writes a document that satisfies it?

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