Skip to content

renderRegion's hand-maintained sig can be made drift-proof by construction: derive the sig from an explicit view-model instead of a parallel hand-joined string #255

Description

@Vortiago

Problem

renderRegion(host, build, {sig}) requires the caller to enumerate, in a separate string, every value the build closure reads — CLAUDE.md documents the failure mode (forget one → the region silently goes stale) and mitigates it with an opt-in runtime audit (_auditSigCoversOutput, templates.js:106-117) plus long defensive comments at each call site (people.js:185-200's sig docstring is 9 lines of 'must list EVERY value'; spine.js:307-331 likewise). The audit detects drift only when enabled and only for states the e2e test happens to exercise; the correctness burden stays on hand-synchronising two representations of the same dependency set. The views' sigs are already ad-hoc serialisations of a model (people.js:195-200 maps people → strings; spine.js:323-331 maps sessions → strings) — the model just isn't reified.

Evidence / where

  • tapscribe/web/js/templates.js
  • tapscribe/web/js/next/views/people.js
  • tapscribe/web/js/next/components/spine.js

Suggested fix

Add a thin sibling primitive, e.g. renderRegionModel(host, model, buildFromModel), where model is a plain JSON-serialisable object the caller derives from state and sig = JSON.stringify(model) is computed inside the primitive; buildFromModel receives only the model, so the render cannot read a value that isn't in the sig — drift becomes impossible by construction rather than detectable by audit. Interaction-hold semantics (focus/selection guards, defer-without-advancing, replaceChildren swap, markRegionStale) are untouched, so this does not reopen ADR-0004's rejection of DOM-diffing; migrate views opportunistically (people.js is the cleanest first candidate) and keep the audit as the backstop for legacy closure-based call sites.

Severity: low


Filed from the 2026-07-03 adversarially-verified deep review (85 findings, 0 refuted). This issue survived an independent skeptic pass. Severity is the verifier's calibration; for nice-to-haves it is operator value.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:dashboardVanilla-JS operator dashboardtech-debtArchitecture / deepening / consistency cleanup

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions