Goal
- Land the canonical Jinja2 template + rewrite
render.pyto render it through one renderer. - Per-section DIG DEEPER command blocks parameterized by the objective symbol set (R5).
- Dense and machine-readable (R2) — no mermaid, no rendered diagrams.
Implementation Sketch
- Install
scripts/intel_package_runtime/templates/package.md.j2from the approved repo-root./intel-package-template.md.j2(target-kind tier guards, verdict slots, dark-tier lines, DIG DEEPER blocks). The approved template’s tier guards use an underscored/abbreviated vocabulary —{% if 'depgraph' in active_tiers %},neighbors,hidden_callers,cochange,community,test_posture,bug_cluster,missing_abstraction,plan_ownership,prior_art,defect, plussurprises/churn/sentiment— which is NOT thetiers.pyREGISTRY SSOT vocabulary (dependency-graph,semantic-neighbors,hidden-callers,co-change,community-span,test-posture,bug-cluster,missing-abstraction,plan-ownership,prior-art). Bindingactive_tiersstraight from the registry hands the template hyphenated keys that no{% if %}guard matches, so every analytic tier guard is false and the package renders only BLUF/terrain/gaps boilerplate with zero tiers.render.pyowns the reconciliation seam below. render.pybinds thepkgcontext object (target / graph_state / active_tiers / objective / per-tier rows+verdict+dark) AND owns an explicit registry-key -> template-guard-key normalization map (e.g.dependency-graph -> depgraph,semantic-neighbors -> neighbors,hidden-callers -> hidden_callers,co-change -> cochange,community-span -> community,test-posture -> test_posture,bug-cluster -> bug_cluster,missing-abstraction -> missing_abstraction,plan-ownership -> plan_ownership,prior-art -> prior_art, withsurprises/churn/sentiment/terrainidentity-mapped).active_tiersis bound to the registry tier names normalized through this map. This map is the single seam between thetiers.pyregistry SSOT and the template guard vocabulary; nothing else translates tier keys.active_tiersdefault = the FULL registry tier set (normalized to the template’s guard vocabulary).template-renderrenders every registry tier and is independently completable on its own. The per-target-kind subset is a later narrowing:target-kind-tiers(s-aa802633, the lexorank successor) consumes the registryowning_target_kindsand NARROWSactive_tiersper target kind. That is a forward-handoff in lexorank order — the successor narrows what this node bound to the full set — NOT adepends_onedge to a successor (a backward/diamond edge would violate INV-19).template-renderrequires nothing fromtarget-kind-tiersto complete.render.pycallsverdicts.verdict_for(name, rows, dark, joins)per tier and feeds the returnedVERDICT:line into that tier’s Jinja2 per-tier context, honoring thes-1c925ea6(verdict-synthesis) forward-declared contract thatverdict_foris the stable producer this renderer swap consumes. The opt-in BLUF/[JOIN]overlay (bluf.render_with_overlay) threadsjoins_by_tierthrough the same render path, so deterministic (joins=None) and overlay renders share one verdict source.- The rewrite RE-POINTS the existing call sites onto the Jinja2 renderer and RETIRES the hand-built path:
generate.py:_write_package(currently dispatchesrender_dossierat line 107) andbluf.render_with_overlay(currently imports and callsrender_dossierat lines 23/142-143) both target the new Jinja2 render entry point, and the hand-builtrender_dossier()/_render_tier()/_tier_dig_deeper()are removed. One renderer, no parallel producers — the one-producer disciplinegenre-inversion-tiers+verdict-synthesisalready established. - DIG DEEPER commands interpolate the objective seeds into
=~ '.*(a|b|c).*'regex predicates so each rendered package’s commands run as-is. The pkgobjectivecontext binds BOTHobjective.symbols_csv(template lines 83/119/131/163/195) ANDobjective.surface_csv(template lines 105/153) —surface_csvis required by the hidden-callers (3.3) and test-posture (3.7) DIG DEEPER cypher, so binding onlysymbols_csvleaves those tiers’ commands unrenderable. Both seeds are produced by a predicate builder that escapes regex metacharacters in each symbol and emits a false-matching predicate when the objective set is empty (mirroring thetiers.py:_name_set_predicateempty ->falseconvention so an empty objective never produces a match-everything regex).
Spec References
./intel-package-template.md.j2(the approved template, repo-root-relative; its own header self-describes the install destination asscripts/intel_package_runtime/templates/package.md.j2)scripts/intel_package_runtime/render.pyscripts/intel_package_runtime/tiers.py(REGISTRY — the tier-name SSOT the normalization map translates from)scripts/intel_package_runtime/verdicts.py(verdict_forcontract fed into the per-tier Jinja2 context)scripts/intel_package_runtime/generate.py+scripts/intel_package_runtime/bluf.py(the two render call sites re-pointed)intel-package-rebuild-notes.mdR2, R5
Work Items
- Install
templates/package.md.j2from the approved repo-root./intel-package-template.md.j2(underscored guard keys, verdict slots, dark lines, DIG DEEPER blocks) with ONE documented deviation: the installed template strips the staticVERDICT:prefix on the 9 per-tier verdict lines plus the 2[JOIN]lines, emitting> {{ tier.verdict }}where the approved form has> VERDICT: {{ tier.verdict }}. The strip is required de-dup, not a divergence from intent:verdicts.verdict_for(verdicts.py:23) is the single VERDICT producer and already returns aVERDICT:-prefixed string, so the approved static prefix would renderVERDICT: VERDICT:. Cross-refverdict-synthesis(s-1c925ea6) as the one-producer contract; a render test asserts exactly oneVERDICT:per tier line. - Rewrite
render.pyto bind thepkgcontext and render the template, including: (a) the registry-key -> template-guard-key normalization map (the single registry-to-template-vocabulary seam) withactive_tiersdefaulting to the FULL normalized registry set (target-kind-tierss-aa802633narrows it later via forward-handoff, never adepends_onedge); (b) per-tierverdicts.verdict_for(name, rows, dark, joins)calls feeding theVERDICT:line into the Jinja2 per-tier context per thes-1c925ea6forward-declared contract; (c) re-pointinggenerate.py:_write_package(line 107) andbluf.render_with_overlay(lines 23/142-143) onto the new Jinja2 entry point and retiring the hand-builtrender_dossier()/_render_tier()/_tier_dig_deeper()— one renderer, no parallel paths; (d) constructing the Jinja2Environmentwithundefined=StrictUndefinedso any template field thepkgcontext fails to bind raisesUndefinedErrorat render time instead of silently emitting an empty string — the mechanical guard that the adapter populates EVERY field the approved template references. - DIG DEEPER command interpolation: bind BOTH
objective.symbols_csvandobjective.surface_csvinto the pkg context via a predicate builder that escapes regex metacharacters per symbol and emits a false-matching predicate for an empty objective set.
Test Pins — golden coverage owned by s-15e599fa (referenced, not duplicated)
The matrix lives in the tests section (s-15e599fa); this section references the pins its StrictUndefined adapter must satisfy (ordered after this node by the lexorank chain, NOT a depends_on edge):
- POSITIVE: rendering the approved
package.md.j2(withundefined=StrictUndefined) over a FULLY-populatedpkgfixture —pkg.consumed_at,bluf,gaps,entry_points,target.repo,active_tiers,objective.symbols_csv+objective.surface_csv, and every per-tierrows/verdict/darkobject — succeeds with noUndefinedError. This pins that the adapter binds every field the approved template references. - NEGATIVE: a
pkgfixture missing ANY template-referenced field raisesUndefinedErrorunderStrictUndefined— the renderer fails loud on an unbound field rather than emitting a silently-empty dossier. - POSITIVE:
active_tiersbound to the full normalized registry set renders every analytic tier guard true (no tier silently dropped by a registry-key vs template-guard-key mismatch).
sc-0004 verification — the four predicates this section’s StrictUndefined adapter must satisfy are ALREADY pinned by existing test_render.py cases (matrix owned by s-15e599fa; named here, not duplicated):
- StrictUndefined unbound-field rejection:
test_strict_undefined_negative_missing_field_raises+test_strict_undefined_negative_missing_nested_field_raises. - Every-tier-guard rendering on the per-kind narrowed registry set:
test_active_tiers_bug_set_renders_every_bug_analytic_guard_true(+test_active_tiers_is_per_kind_narrowed_set). - DIG DEEPER bash-block presence per rendered tier: the render golden
test_generate_writes_dossier_through_jinja2+ the walking-skeleton goldentest_positive_adjacency_verdict_and_dig_deeper. - Empty-objective never-match predicate (empty objective set -> false-matching regex, never match-everything):
test_objective_csv_empty_is_never_matching+test_empty_objective_seeds_render_never_matching_predicate; regex metacharacter escapingtest_objective_csv_escapes_metacharacters.
Forward-refs — owned by sibling sections, NOT in-section gaps
These are advisory forward-handoffs surfaced during review; template-render does NOT fix them:
- The per-kind
active_tiersselection (active_tiers(kind)= registry inversion ofowning_target_kinds) is owned bytarget-kind-tiers(s-aa802633, w-605bd83b).template-renderbinds the FULL normalized registry set viaactive_tiers_from(tiers.keys())(render.py:66); the per-kind narrowing is the successor’s deliverable. - The bug-only
defectguard-key is delivered HERE: the template guard{% if 'defect' in active_tiers %}(package.md.j2:41) + the render.py-bounddefectblock (render.py:388, None-guarded for non-bug). Butdefectis NOT a registry tier and_REGISTRY_TO_GUARDnever emits it, so it is absent from the default full-setactive_tiers— the approved ”## 1. The defect” section (intel-package-example.md:31, bug-only) renders ONLY onces-aa802633’s per-kind selection addsdefectto thebugactive set.s-aa802633’s current per-kind matrix OMITSdefectfrombug; closing the approved-example conformance requiress-aa802633to include it (cross-section, owned by the successor, NOT this section). - Dynamic
target.repobinding (vs the hardcoded'ori'atrender.py:366) is owned bytarget-kind-tiers(s-aa802633) kind/target metadata. UndefinedErrorstructured-failure handling at thegenerateerror path is owned by the generate error path, not this renderer swap.