7The deterministic ``roadmap_render_sections`` layout (Plan 142 P1.6 seam, §6.1).
11``roadmap_render_sections`` is in the §3.2 logical hash but has **no §3.1 source
12entity** (the P1.4 build leaves it empty on rebuild — umbrella §12 wave-3). P1.6
13owns **defining + seeding** the deterministic render-section set so the
14lean / EXTENDED / changelog / root views are produced deterministically and the
15logical-state hash is stable. The build-vs-render seam: ``regenerate``'s builder
16calls ``seed_render_sections`` so the rows land in the hashed store (the §6.1
17"re-pointed at the lean/EXTENDED/changelog file_kinds" requirement).
21- ``RENDER_SECTIONS`` — the canonical, sorted, deterministic section manifest.
22- ``seed_render_sections(conn)`` — insert it into the temp DB during the build.
23- ``validate_render_sections(...)`` — reject an **active** row with a retired
24 file_kind (AC §8.24); retired kinds (``roadmap_exec_order``/``roadmap_todo``)
25 are kept ``active=0`` (L-a superset CHECK + active-only liveness rule).
26- ``sections_for(conn, file_kind)`` — the renderer's data-driven section list.
33 L3 - per-file_kind section counts
34 L4 - per-row insert tracing
38- The manifest is a Python constant (no source entity); it is deterministic +
39 byte-stable, so seeding it keeps the logical-state hash reproducible.
40- ``query_params_json`` is stored as canonical JSON so two rebuilds match and
41 the §3.2 ``_canonical_query_params`` re-emit is a no-op.
45- oc_diagnostics (structured debug logging via _dbg)
46- oc_chat.registry.canonical (canonical_json)
48from __future__
import annotations
51from dataclasses
import dataclass
52from typing
import Any, Optional
54from oc_diagnostics
import _dbg
58_dbg(
"OC_CHAT.MAIN",
"module",
"loaded", 4)
61FILE_KIND_LEAN =
"roadmap"
62FILE_KIND_EXTENDED =
"roadmap_extended"
63FILE_KIND_CHANGELOG =
"roadmap_changelog"
64FILE_KIND_ROOT_CHANGELOG =
"root_changelog"
68RETIRED_FILE_KINDS = frozenset({
"roadmap_exec_order",
"roadmap_todo"})
71@dataclass(frozen=True)
73 """One declarative render section (a ``roadmap_render_sections`` row)."""
79 query_params: Optional[dict[str, Any]] =
None
80 required_min_version: Optional[str] =
None
90_NON_PHASE_TRACKS = {
"track_kinds": [
"pre_m0_era",
"backlog",
"vision"]}
97RENDER_SECTIONS: tuple[RenderSection, ...] = (
99 RenderSection(FILE_KIND_LEAN,
"era-overview", 0,
"prose_only"),
100 RenderSection(FILE_KIND_LEAN,
"milestones", 1,
"m_phase_table"),
101 RenderSection(FILE_KIND_LEAN,
"phase-detail", 2,
"phase_section_with_subrows",
103 RenderSection(FILE_KIND_LEAN,
"backlog", 3,
"backlog_track_table", _NON_PHASE_TRACKS),
105 RenderSection(FILE_KIND_EXTENDED,
"era-overview", 0,
"prose_only"),
106 RenderSection(FILE_KIND_EXTENDED,
"milestones", 1,
"m_phase_table"),
107 RenderSection(FILE_KIND_EXTENDED,
"phase-detail", 2,
"phase_section_with_subrows",
109 RenderSection(FILE_KIND_EXTENDED,
"backlog", 3,
"backlog_track_table", _NON_PHASE_TRACKS),
110 RenderSection(FILE_KIND_EXTENDED,
"git-workflow", 4,
"git_workflow_table"),
112 RenderSection(FILE_KIND_CHANGELOG,
"changelog", 0,
"prose_only"),
114 RenderSection(FILE_KIND_ROOT_CHANGELOG,
"product-changelog", 0,
"prose_only"),
116 RenderSection(
"roadmap_exec_order",
"retired", 0,
"prose_only", active=
False),
117 RenderSection(
"roadmap_todo",
"retired", 0,
"prose_only", active=
False),
122 """An active render section names a retired output file_kind (AC §8.24)."""
126 """Reject any **active** section whose file_kind is retired (AC §8.24)."""
129 for section
in sections
130 if section.active
and section.file_kind
in RETIRED_FILE_KINDS
133 raise RetiredFileKindActiveError(
134 f
"active render section(s) {offenders} use a retired file_kind "
135 f
"{sorted(RETIRED_FILE_KINDS)}; retired kinds must be active=0"
140 """Backstop the active-only liveness rule against the built store (AC §8.24)."""
143 "SELECT file_kind FROM roadmap_render_sections "
144 "WHERE active = 1 AND file_kind IN (?, ?)",
145 tuple(sorted(RETIRED_FILE_KINDS)),
149 f
"store has active render rows for retired file_kind(s) "
150 f
"{sorted({row[0] for row in rows})}"
155 """Seed the canonical render-section manifest into ``conn`` (the build seam).
157 Idempotent: clears + re-inserts the deterministic set. Returns the row count.
158 Called by the P1.4 builder so the rows enter the hashed store (§6.1).
160 func =
"seed_render_sections"
162 conn.execute(
"DELETE FROM roadmap_render_sections")
163 for section
in RENDER_SECTIONS:
166 "INSERT INTO roadmap_render_sections(file_kind, section_path, sort_order, "
167 "query_kind, query_params_json, required_min_version, active) "
168 "VALUES(?,?,?,?,?,?,?)",
169 (section.file_kind, section.section_path, section.sort_order,
170 section.query_kind, section.params_json(), section.required_min_version,
171 int(section.active)),
173 _dbg(
"OC_CHAT.MAIN", func, f
"seeded {len(RENDER_SECTIONS)} rows", 3)
174 return len(RENDER_SECTIONS)
177def sections_for(conn: sqlite3.Connection, file_kind: str) -> list[sqlite3.Row]:
178 """Return the active sections for ``file_kind`` in render order (§6.1)."""
179 conn.row_factory = sqlite3.Row
182 "SELECT section_path, sort_order, query_kind, query_params_json "
183 "FROM roadmap_render_sections WHERE file_kind = ? AND active = 1 "
184 "ORDER BY sort_order, section_path",
191 "FILE_KIND_EXTENDED",
192 "FILE_KIND_CHANGELOG",
193 "FILE_KIND_ROOT_CHANGELOG",
194 "RETIRED_FILE_KINDS",
197 "RetiredFileKindActiveError",
198 "validate_render_sections",
199 "assert_active_file_kinds_in_db",
200 "seed_render_sections",
Optional[str] params_json(self)
int seed_render_sections(sqlite3.Connection conn)
list[sqlite3.Row] sections_for(sqlite3.Connection conn, str file_kind)
None assert_active_file_kinds_in_db(sqlite3.Connection conn)
None validate_render_sections("tuple[RenderSection, ...]" sections=RENDER_SECTIONS)