Skip to content

T-ZGO4-rebase-parse-operations-table-on-markdown-contract

Status: closed/done · Impact: low · Complexity: small

plugin/lib/model/entity.ts#parseOperationsTable hand-scans an entity definition.md for its ## Operations section and splits the table rows with bespoke string code, even though every entity contract already parses bodies through markdown-contract. Re-basing it on markdown-contract’s parse() + typed table read removes a hand-rolled ## + | scanner and follows the precedent already set by parse-touchpoints.ts.

LocationRole today
plugin/lib/model/entity.ts#parseOperationsTableReads a definition.md, finds ## Operations by string scan, collects |-delimited rows, and splits them into Operation[] by hand — preserving the raw header→cell map in Operation.columns, with legacy 3-col aliasing
plugin/lib/model/entity.tsCalls parseOperationsTable when projecting an entity definition
plugin/lib/model/entities/task/ops/parse-touchpoints.tsThe precedent: already reads ## Today / ## Files to touch tables via markdown-contract parse() + SectionNode

parseOperationsTable finds the Operations section and reads its table via markdown-contract’s parse() projection — the same typed table access parse-touchpoints.ts uses — and returns the identical Operation[]: the columns raw header→cell map preserved, a legacy 3-col header still aliased to the D-0004 5-col shape. No bespoke ## Operations string scan or \|-split remains in the function.

  1. Read parse-touchpoints.ts as the model for selecting a section and reading its table from the parse() projection in this codebase.
  2. Reimplement parseOperationsTable to: parse() the definition, select the Operations section, read its header + rows from the projection, and build Operation[] with the same columns mapping and the same legacy-3-col aliasing the current code produces.
  3. Keep a minimal raw fallback only if a real gfm edge case needs it (as parse-touchpoints.ts does); otherwise none.
  4. Run the model tests and project-check-entities; confirm column-drift detection still fires on a legacy 3-col header.
LocationKindChange
plugin/lib/model/entity.ts#parseOperationsTablemodifyReimplement over markdown-contract parse() + typed table read; drop the ## Operations string scan and |-split
plugin/lib/model/tests/modifyAdd/confirm a test pinning Operation[] output (columns map + legacy aliasing) over the new backing
  • AC-1: parseOperationsTable contains no bespoke ## Operations heading scan or manual |-row split — it reads the table via markdown-contract parse().
  • AC-2: For a D-0004 5-col Operations table, parseOperationsTable returns the same Operation[] (including columns) as before — pinned by a test.
  • AC-3: For a legacy 3-col header, the aliasing (Signature ← CLI, Description ← What it does, surface / pointer null) is preserved, and project-check-entities still flags the column drift (the T-E9C9 behavior).
  • AC-4: bun test passes for plugin/lib/model/.
  • check_entities.ts’s own readOperationsHeaderCells mirror — re-pointing that at the shared capability is [T-4WR3-rebase-check-entities-markdown-reads-on-contract](/planning/tasks/rebase-check-entities-markdown-reads-on-contract/).
  • The other bespoke parsers in entity.ts (e.g. the template-strip FRONTMATTER_RE) — [T-Z51X-consolidate-frontmatter-regex-onto-shared-helper](/planning/tasks/consolidate-frontmatter-regex-onto-shared-helper/).
  • None. [T-4WR3-rebase-check-entities-markdown-reads-on-contract](/planning/tasks/rebase-check-entities-markdown-reads-on-contract/) consumes this function; either order works.

Captured by /sdlc:task-work on 2026-06-30. PR: pending.

  • AC-1: auto — rewritten source reads the table via markdown-contract parse().root.sections (selecting the Operations H2) + section.blocks.find(b => b.kind === "table"); the ## Operations string scan and the |-line/separator collection are gone. Verified by reading the new code and the passing model suite.
  • AC-2: auto — new test pins the exact Operation[] (including the columns map) for a D-0004 5-col table; additionally a golden snapshot of all 11 real entities’ output diffed byte-identical old-vs-new.
  • AC-3: auto — new test pins legacy 3-col aliasing (Signature ← CLI, Description ← What it does, surface/pointer null); sdlc entities audit / project-check-entities still emit the “legacy 3-column header” drift warning for decision/milestone/principle, so T-E9C9 drift detection still fires (the out-of-scope check_entities.ts mirror was untouched).
  • AC-4: auto — bun test plugin/lib/model/ → 326 pass / 0 fail.
  • markdown-contract’s parse() projection plus the parse-touchpoints.ts precedent mapped directly onto the Operations table — the rewrite was a clean port with no new abstraction needed.
  • Re-splitting each row’s source line (located via the projection’s pos/rowPos) preserved the verbatim backticks the columns map pins, sidestepping the projection’s gfm-flattened cells; a golden diff over all 11 real entities confirmed byte-identical output with zero hand-authored fixtures.
  • Step 7’s baseline-gated quality run reported one false-positive new-drift: line — a dashboard test prints a non-deterministic PID (/private<TMPDIR> <pid> http://127.0.0.1:<PORT>/); the baseline normalizer scrubs TMPDIR and PORT but not the PID, so the line differs run-to-run and masquerades as branch-introduced drift on a branch that touches only model/entity.ts. The quality baseline normalizer should scrub process-ephemeral integers (PIDs) the way it already scrubs temp paths and ports. → T-BQRU-quality-normalize-ports-pids-timings
  • Step 3b’s preflight_permissions.ts reported Write/Edit as hard gaps for the worktree path because this harness grants file-mutation tools at the top level rather than via path-scoped settings.json entries; the settings-file-only probe can’t see harness-level grants, so a real-but-spurious hard gap had to be judged a false positive before proceeding. The probe could treat a blanket/top-level file-mutation grant (or an acceptEdits-equivalent harness mode) as covering the worktree path. → T-0AM0-preflight-probe-honors-runtime-edit-grant

← Back to Tasks