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.
| Location | Role today |
|---|---|
plugin/lib/model/entity.ts#parseOperationsTable | Reads 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.ts | Calls parseOperationsTable when projecting an entity definition |
plugin/lib/model/entities/task/ops/parse-touchpoints.ts | The precedent: already reads ## Today / ## Files to touch tables via markdown-contract parse() + SectionNode |
Proposed
Section titled “Proposed”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.
Approach
Section titled “Approach”- Read
parse-touchpoints.tsas the model for selecting a section and reading its table from theparse()projection in this codebase. - Reimplement
parseOperationsTableto:parse()the definition, select theOperationssection, read its header + rows from the projection, and buildOperation[]with the samecolumnsmapping and the same legacy-3-col aliasing the current code produces. - Keep a minimal raw fallback only if a real gfm edge case needs it (as
parse-touchpoints.tsdoes); otherwise none. - Run the model tests and
project-check-entities; confirm column-drift detection still fires on a legacy 3-col header.
Files to touch
Section titled “Files to touch”| Location | Kind | Change |
|---|---|---|
plugin/lib/model/entity.ts#parseOperationsTable | modify | Reimplement over markdown-contract parse() + typed table read; drop the ## Operations string scan and |-split |
plugin/lib/model/tests/ | modify | Add/confirm a test pinning Operation[] output (columns map + legacy aliasing) over the new backing |
Acceptance criteria
Section titled “Acceptance criteria”- AC-1:
parseOperationsTablecontains no bespoke## Operationsheading scan or manual|-row split — it reads the table via markdown-contractparse(). - AC-2: For a D-0004 5-col Operations table,
parseOperationsTablereturns the sameOperation[](includingcolumns) as before — pinned by a test. - AC-3: For a legacy 3-col header, the aliasing (
Signature ← CLI,Description ← What it does,surface/pointernull) is preserved, andproject-check-entitiesstill flags the column drift (the T-E9C9 behavior). - AC-4:
bun testpasses forplugin/lib/model/.
Out of scope
Section titled “Out of scope”check_entities.ts’s ownreadOperationsHeaderCellsmirror — 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-stripFRONTMATTER_RE) —[T-Z51X-consolidate-frontmatter-regex-onto-shared-helper](/planning/tasks/consolidate-frontmatter-regex-onto-shared-helper/).
Dependencies
Section titled “Dependencies”- 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.
Post-mortem
Section titled “Post-mortem”Captured by /sdlc:task-work on 2026-06-30. PR: pending.
Acceptance criteria coverage
Section titled “Acceptance criteria coverage”- AC-1: auto — rewritten source reads the table via markdown-contract
parse().root.sections(selecting theOperationsH2) +section.blocks.find(b => b.kind === "table"); the## Operationsstring 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 thecolumnsmap) 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/pointernull);sdlc entities audit/project-check-entitiesstill emit the “legacy 3-column header” drift warning fordecision/milestone/principle, so T-E9C9 drift detection still fires (the out-of-scopecheck_entities.tsmirror was untouched). - AC-4: auto —
bun test plugin/lib/model/→ 326 pass / 0 fail.
What worked
Section titled “What worked”- markdown-contract’s
parse()projection plus theparse-touchpoints.tsprecedent 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 thecolumnsmap 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.
Friction and automation gaps
Section titled “Friction and automation gaps”- 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 onlymodel/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.tsreportedWrite/Editas hard gaps for the worktree path because this harness grants file-mutation tools at the top level rather than via path-scopedsettings.jsonentries; 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 anacceptEdits-equivalent harness mode) as covering the worktree path. → T-0AM0-preflight-probe-honors-runtime-edit-grant
Spawned follow-up tasks
Section titled “Spawned follow-up tasks”- T-BQRU-quality-normalize-ports-pids-timings — linked (existing open/ready task; normalizeFinding already owns the ephemeral PID/port/timing masking this bullet asks for)
- T-0AM0-preflight-probe-honors-runtime-edit-grant — linked (existing planning/draft task; verbatim owner of the preflight Write/Edit harness-grant false-positive)