100%

Goal

Invert surfaces.py + envelope.py from “bundle precomputed surfaces, emit row counts” into “select computed analytics and assemble the full tier catalogue” (R1, R4). Deliver the full 15-tier catalogue — 14 analytic selectors, each a selector over an EXISTING intel-query surface returning rows + a dark flag, plus 1 coverage-gaps meta-tier realized from those dark flags.

Implementation Sketch

The catalogue is 15 tiers: 14 analytic selectors over live intel-query surfaces plus 1 coverage-gaps meta-tier. This 15-tier total is what sc-0002 (All 15 tiers emit {rows, dark}) gates against — the count below is the deliverable side of that acceptance gate.

  • New scripts/intel_package_runtime/tiers.py holds a single tier registry. Each registry entry is one tier keyed by tier name, carrying four fields: source_query (the exact intel-query.sh subcommand or cypher query the selector runs), row_schema (the shape of one emitted row), dark_condition (the concrete predicate that flips dark: true), and owning_target_kinds (which target kinds include the tier — the vocabulary target-kind-tiers s-aa802633 tier_selection.py consumes). This registry is the SSOT for tier names; genre-inversion-tiers and target-kind-tiers share it so neither re-declares a tier vocabulary.
  • The 14 analytic selectors (one registry entry each): terrain (file-symbols), dependency-graph (cypher intra-set CALLS), semantic neighbors (similar), hidden cross-module callers (cypher), surprises (cypher surprise_score + call-resolution-artifact flag), co-change (Commit TOUCHED cypher), community span (IN_COMMUNITY cypher), test-posture (tests:: caller cypher), per-symbol churn (hotspot_score cypher), bug cluster (title+subsystem + blocks), missing-abstraction (MISSING_ABSTRACTION cypher), plan ownership (symbol-plans), prior-art (cross-repo similar SOURCE + fixed/search/compare), sentiment (pain_pctile + FailureMode cypher).
  • Tier 15 — Declared coverage gaps (meta-tier): not an independent surface selector. It is the union of every analytic tier whose dark flag is true, rendered as one honesty block naming which tiers returned no live rows and why (R8). It is realized entirely by the per-selector dark flag — no new query — and rounds the catalogue to 15.
  • Each analytic selector returns {rows, dark}. The dark_condition predicate is per tier class:
    • graph-backed tiers (cypher, file-symbols, callers/callees): dark: true when intel-query.sh status reports the graph stale: true, or the query returns zero rows.
    • query tiers (symbol-plans, blocks, fixed/search/compare): dark: true on a zero-result return.
    • similar (embedding-backed) tiers: dark: true when embeddings are stale per intel-query.sh status, or on a zero-neighbor return.
    • This predicate is consistent with query-selfheal (s-fa1f33bc) serve-stale-and-flag: a refresh-failure degraded read still serves rows AND sets dark: true rather than dropping the tier. The shared _is_dark(surface_name, rows, graph_status) -> bool utility is owned HERE in tiers.py — it realizes each registry entry’s dark_condition, so it lands with the batch-A registry work item (w-08b753bd). The cypher-reachable graph properties (hotspot_score, IN_COMMUNITY, MISSING_ABSTRACTION, pain_pctile, FailureMode) are graph node/property names reached via the cypher surface, not dedicated intel-query.sh subcommands; a property absent from the live graph schema simply returns zero rows for its selector, flipping that tier dark: true via _is_dark (R8 honesty) — no separate existence-verification deliverable is needed.
  • Retire the envelope.py counts-manifest emission (the ### surface - N rows shape).

Dependencies and Test Gates

  • Predecessor: walking-skeleton (s-9948c200) wires ONE tier end-to-end and lands the minimal surfaces.py adjacency-row builder; this section fans that one wired tier out to the full 15-tier catalogue and owns the _is_dark predicate + the tier registry. Successor: the tests section (s-15e599fa) gates the per-tier rows/dark fixtures (a missing graph property surfaces as a dark tier through _is_dark, so it needs no dedicated existence gate). The plan is an INV-19 strict-linear-DAG: the lexorank chain already places walking-skeleton before this section and this section before the tests section, so those contracts are encoded by linear order — no explicit depends_on edge is added (a diamond depends_on would violate INV-19).
  • Test gates are owned by the tests section (s-15e599fa); this section does not duplicate the matrix. s-15e599fa delivers, per the tier registry: one non-dark rows fixture and one dark-condition fixture per tier class, a counts-manifest-absent negative (asserting no ### surface - N rows line is emitted, so sc-0002 cannot pass with always-empty selectors), the per-target-kind tier-subset matrix (sc-0005), and tier_selection active_tiers verification. Tier 15 coverage-gaps is gated by a fixture forcing >=1 tier dark and asserting the honesty block names it.

Spec References

  • scripts/intel_package_runtime/surfaces.py (184 lines), envelope.py (369 lines)
  • scripts/intel-query.sh (read-surface inventory)
  • intel-package-rebuild-notes.md R1, R4; intel-package-example.md §3-§8

Work Items

  • Tier registry + analytic selectors batch A (9 of 14): terrain, dependency-graph, semantic neighbors, hidden callers, surprises (+artifact flag), co-change, community span, test-posture, churn. Establishes the registry entry shape (source_query, row_schema, dark_condition, owning_target_kinds).
  • Analytic selectors batch B (5 of 14): bug cluster, missing-abstraction, plan ownership, prior-art (cross-lang SOURCE + issues), sentiment. Plus tier 15 — the coverage-gaps meta-tier rendered from the union of dark-flagged analytic tiers (no new surface query). Batch A + batch B + coverage-gaps = 15 tiers, satisfying sc-0002.
  • Retire the envelope.py counts-manifest; the 15-tier set replaces it as the package body model.