Skip to content

docs(plan): handover for the parser contract#81

Merged
teezeit merged 1 commit intomainfrom
parser-contract-plan
May 1, 2026
Merged

docs(plan): handover for the parser contract#81
teezeit merged 1 commit intomainfrom
parser-contract-plan

Conversation

@teezeit
Copy link
Copy Markdown
Owner

@teezeit teezeit commented May 1, 2026

Summary

Self-contained brief for the next architectural beat after the renderer split. No code change — just the planning doc at .github/dev-docs/parser-contract-plan.md.

The renderer monolith is split (#77 #78 #79 #80). The next high-leverage move is not splitting parser/transformer.ts per file — it's fixing the contract: explicit TransformContext with peekNext/consumeNext, plus isolated per-node parse tests that don't go through the full markdown→AST→render pipeline.

The doc explains why (the file split is cosmetic; the leverage is the contract), what to build (_context.ts + test helpers + per-node tests), and what NOT to do (no file split in this PR, no syntax change, no bug fix). It also captures the user's pending syntax direction (((pill)) over |pill|, :::columns over :::grid, etc.) so the next session has the destination clear before starting work.

Two syntax ideas got pushed back during discussion and are flagged for design calls before any code:

  • Free-floating {.right}/{.center}/{.left} "applies until resolved" — stateful-parsing trap; counter-proposed "applies to nearest element only."
  • Navbar [[ a | b | c ]] | separators — collides with table syntax, new escape rules; counter-proposed alignment classes on items.

Test plan

  • No code touched.
  • Skim the doc for accuracy.

🤖 Generated with Claude Code

Captures the next architectural beat after the renderer split:

- The leverage isn't in splitting transformer.ts per file (it's
  already organized as 17 named functions internally). The leverage is
  in fixing the contract: explicit TransformContext with
  peekNext/consumeNext, plus isolated per-node parse tests that don't
  go through the full markdown→AST→render pipeline.
- Land both as one PR. Pure refactor — zero snapshot drift, 17
  KNOWN_FAILURES still .fails. No file split, no syntax change, no
  bug fix in the same PR.
- Then knock out KNOWN_FAILURES one cluster at a time with the new
  tooling. Then attack syntax changes one at a time.

Captures the user's pending syntax direction (((pill)) over |pill|,
:::columns over :::grid, consistent {.classname}, disallow row +
heading-alignment). Two ideas got pushed back during discussion:
free-floating alignment classes (stateful parsing trap — counter:
"applies to nearest element only") and navbar | separators (collides
with tables — counter: alignment classes on items). Both need design
calls before code.

The doc is the in-repo source of truth; the user-memory pointer at
parser-contract-handover.md cross-references it so a fresh agent
loads MEMORY.md, sees the entry, and reads the full plan.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@teezeit teezeit merged commit 7fae209 into main May 1, 2026
8 checks passed
teezeit added a commit that referenced this pull request May 1, 2026
## Summary

Pure refactor: replaces the implicit `nextNode` parameter and post-hoc
`i++` patterns in `processNodeList` with a first-class
`TransformContext` that every transform receives. Cross-sibling
peek/consume is now explicit, auditable, and unit-testable in isolation.

This is the parser-contract step from
`.github/dev-docs/parser-contract-plan.md` (PR #81). It unblocks the
bug-fix and syntax-change PRs that follow.

## What changed

**New: `packages/core/src/parser/_context.ts`**
- `TransformContext` interface — `peekNext()`, `consumeNext()`,
`transformChild()`, `transformChildren()`, plus shared helpers
(`parseAttributes`, `extractTextContent`, `isHtmlCommentNode`).
- `makeContext(siblings, startIndex, options, deps)` — sibling-aware
factory used by `processNodeList`.
- `makeIsolatedContext(options, deps)` — for transforms that walk a
single child without cross-sibling lookahead.

**Modified: `packages/core/src/parser/transformer.ts`**
- Every transform function migrated from `(node, options [, nextNode])`
to `(node, ctx)`.
- `processNodeList` builds a `TransformContext` per iteration and
advances by reading `handle.getCursor()`.
- The post-hoc shape rule (`if (transformed.type === 'select' &&
nextNode...) i++;`) is gone — consumption now lives next to the peek
inside `transformParagraph`, where the read actually happens.
- The dropdown lookahead is the only cross-sibling peek in the parser;
both single-line and multi-line dropdown branches now call
`ctx.consumeNext()` themselves.
- New `__transformerInternals` export (test-only) so isolated parse
tests can dispatch through `transformNode` directly.

**New: `packages/core/tests/lib/transform-test-helpers.ts`**
- `mdastFor(markdown)` — runs the same MDAST stage that `parse()` runs,
stops before the wiremd transform.
- `makeTestContext(siblings, options)` — sibling-aware test context.
- `runTransform(mdast, siblings, options)` — fires `transformNode` and
returns `{ node, cursor }` so tests can assert on lookahead consumption.

**New: `packages/core/tests/parser/dropdown-lookahead.test.ts`**
- First per-node parse test. 4 cases pinning the dropdown contract:
  - Dropdown + following list → consumes the list.
  - Dropdown alone → no consume.
  - Dropdown + non-list sibling → no consume.
  - Non-dropdown paragraph + list → no peek.

## What this PR is NOT

- Not a file split. `transformer.ts` stays in one file. Each function
gets the new `ctx` parameter; that's it.
- Not a bug fix. All 17 `KNOWN_FAILURES` continue to fail.
- Not a syntax change. The user's pending syntax direction (`((pill))`,
`:::columns`, etc.) lands in separate PRs.

## Test plan

- [x] `pnpm typecheck` — clean
- [x] `pnpm test` — **1,184 passed** (1,180 baseline + 4 new isolated
parse tests)
- [x] `pnpm review:refresh` — **"Nothing matched — log unchanged"**
(zero snapshot drift)
- [x] All 17 `.expected-fail.invariants.ts` tests continue failing as
expected

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.7 (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.

1 participant