Skip to content

ID-132 SPEC — theme_config: OKF bundle index.md theme map (proposal, bl-463) [SUPERSEDED S547]

ID-132 SPEC — theme_config: OKF bundle index.md theme map

Section titled “ID-132 SPEC — theme_config: OKF bundle index.md theme map”

Kind: Spec proposal — a ratifiable proposal for WHERE the index.md theme→concept map lives and a concrete starter map. It does not implement; it gives the owner what is needed to ratify config-home + starter map, riding the already-landed theme_config seam in bundle_writer.build_index_themes. Authored: 15/07/2026 · placed post-conformance-wave. Source: copied verbatim from the session scratchpad okf-theme-config-proposal.md.

  • Option A — OWNER-RATIFIED. The theme map lives in a client-owned theme-map JSON at the bundle root, sibling of ontology-overlay.json (§2 Option A below). Ratified 2026-07-15 (recorded bl-463).
  • §5 item 5 (deploy-seed policy) — FOLDED into {45.10} (journaled there): whether the deploy drops a starter map at first-client mint sits on the {45.10} seeding boundary.
  • §5 item 7 (SSCM duplicate) — already OWNED by {45.11} (S465, the pre-{45.9} gate): the near-duplicate SSCM concepts are a data/producer question tracked there, not a theme-map question.
  • Items 2/3/4/6 — RATIFIED 2026-07-15 (post OKF-alignment check, recorded bl-463): item 2 file name = index-themes.json; item 3 Platform map = §3 as recommended (incl. the won-bid → Bids & Tendering and GreenCycle → Products calls); item 4 section order ratified in principle (§3 ordering stands; reorder is a cheap config edit); item 6 first-client themes noted — deferred to the client-bundle mint, ratified against that bundle’s real concepts.

OKF-alignment check (pre-ratification, upstream knowledge-catalog @ d44368c). The SPEC prescribes no theme model: index.md is optional, MAY be auto-generated, consumers MAY synthesize their own, and it never gates writes (§3/§6/§9 — a fixed taxonomy is an explicit non-goal). The upstream reference_agent groups index sections mechanically by frontmatter type (bundle/index.py) with per-directory recursive indexes — no configured map exists upstream. Content that doesn’t naturally fit is never discarded: it mints into references/ as first-class concepts (web-ingestion prompt) or is an explicit skip; the index only reflects what exists. Our theme_config is structurally gate-free (every concept appears exactly once; unclaimed concepts land under the unthemed heading; the map is consulted only at index render), so the editorial map cannot block or force-align concept writes — organic growth is preserved. Open recommendation (bl-463): the no-map DEFAULT should group by concept type (mirroring upstream) instead of one flat Other bucket, keeping index-themes.json as the optional editorial override; per-client maps are generated at onboarding (corpus-derived draft per §4’s method → client/owner ratification → {45.10} deploy-seed).

Pin caveat. Body file:line pins were captured at canonical 855118df (pre-conformance-wave); commit 9a795c5e shifted producer line numbers — verify pins before implementation (the plumbing seams themselves are unchanged).


Purpose. The concept producer regenerates a bundle-root index.md as a themes → concepts progressive-disclosure outline. The first Platform producer run passed no theme map, so all 18 published concepts render under a single ## Other heading. This document gives the owner everything needed to ratify (a) where the theme map should live and (b) a concrete starter map, plus a preliminary sketch for the first client bundle.

All file:line citations are against scripts/cocoindex_pipeline/producer/ in the canonical repo unless stated otherwise.


1. Current plumbing — how the index is themed today (and why it isn’t)

Section titled “1. Current plumbing — how the index is themed today (and why it isn’t)”

build_index_themes(theme_config, concepts, *, unthemed_heading="Other") (bundle_writer.py:418-470) is the pure-Python bridge from a caller-supplied theme_config: [(heading, [rel_path, ...]), ...] to the renderable IndexTheme list that regenerate_indexes (bundle_writer.py:391-415) turns into index.md. Its contract: every concept appears under exactly one theme; any concept no theme_config entry claims falls into a trailing unthemed_heading bucket (bundle_writer.py:455-469); a theme_config entry naming a rel_path not present in concepts is silently skipped rather than inventing an entry. So the mechanism is already safe against a stale/incomplete map — it just needs one supplied.

The theme_config value threads cleanly through the whole chain, defaulting to empty at every hop:

HopSiteWhat it passes
write_bundle parambundle_writer.py:731theme_config: Sequence[...] = () (default empty)
write_bundle → builderbundle_writer.py:844-847build_index_themes(theme_config, written) then declare_file(bundle_dir/index.md, ...)
run_producer_flow paramflow_def.py:339theme_config: Sequence[...] = () (default empty)
run_producer_flowwrite_bundleflow_def.py:403-408theme_config=theme_config
default_producer_entry_point → flowtrigger.py:120run_producer_flow(pool=..., bundle_dir=..., **flow_kwargs)
Real invocation (post-walk)flow.py:4768-4773trigger_producer_post_walk(run_op_id, deltas, pool=..., re_target=..., repo_path=...)no theme_config

Conclusion. The pipe is fully built; nothing fills it. The production trigger in flow.py passes only pool / re_target / repo_path, so theme_config inherits its () default end-to-end. build_index_themes(()) claims nothing, so all 18 concepts land in the trailing Other bucket — which is exactly what the published canonical-okf-showcase/index.md (pre-rename canonical-okf-platform) shows (one ## Other heading over all 18 links). This is a missing configuration source, not a code bug.

The precedent surface: how per-client config (client_overlay) is supplied

Section titled “The precedent surface: how per-client config (client_overlay) is supplied”

The {132.34} client ontology overlay is the pattern theme_config should copy:

  • A client-authored file at the bundle repo root. OVERLAY_FILENAME = "ontology-overlay.json" (bundle_writer.py:170). read_client_overlay(bundle_dir) (bundle_writer.py:633-664) reads bundle_dir / OVERLAY_FILENAME, validates it, and returns an OV-6 provenance-wrapped mapping (source, sha256, dimensions) or None when absent (absence is not an error).
  • Read at runtime, threaded in by write_bundle. write_bundle composes overlay = client_ontology_overlay if ... is not None else read_client_overlay(bundle_dir) (bundle_writer.py:780-784), then hands it to write_ontology_artefact(bundle_dir, client_overlay=overlay) (bundle_writer.py:848, def at 667-696). The explicit kwarg is only a test/escape-hatch override; the real source is the file in the bundle.
  • Producer never writes it (DR-016 client-ownership). OVERLAY_FILENAME is in _RESERVED_BUNDLE_FILENAMES (bundle_writer.py:171), so cocoindex’s orphan-delete reconciliation leaves it alone (S464 rider R1, noted at bundle_writer.py:646). The producer only ever reads it.
  • Single-clone model (DR-055). bundle_dir == repo_path == OKF_BUNDLE_DIR (flow.py comment ~4758). The producer auto-resolves bundle_dir from the OKF_BUNDLE_DIR env var (flow_def.py:197-215). Config that lives in the bundle therefore needs no extra wiring to be found.
  • Bundle self-carries its effective config (DR-027). write_ontology_artefact ships the materialised ontology into the bundle so a consumer needs no other repo.

This is the surface theme_config should ride: a sibling client-owned file at the bundle root, read at runtime, never producer-written.


2. Config-home options — and the recommendation

Section titled “2. Config-home options — and the recommendation”

The theme map is editorial, client-owned data about the client’s own bundle (BI-5: the theme→concept mapping is “an owner’s call”). That fact drives the decision more than any mechanical concern. Weighed against client-ownership (DR-016), portability, producer determinism, and the {45.10} deploy-time seeding boundary:

Option A — a bundle-root client-authored file (RECOMMENDED). A new sibling of ontology-overlay.json, e.g. index-themes.json, at the bundle repo root. Add a read_theme_config(bundle_dir) reader mirroring read_client_overlay, add the filename to _RESERVED_BUNDLE_FILENAMES (bundle_writer.py:171), and feed its result into the existing theme_config= seam (write_bundle at bundle_writer.py:780-ish, right where read_client_overlay is already called).

  • Client-ownership (DR-016): the map lives where the client edits it — in their own bundle repo, exactly like the overlay. ✔
  • Portability: travels with the repo (DR-027 self-carrying posture; DR-055 single-clone means it’s already on disk where the producer looks). ✔
  • Producer determinism: a theme edit re-shapes index.md only — no concept bodies change, no re-embed, no new commits beyond the regenerated index. Cheapest possible change surface. ✔
  • Cost: one small reader + one line in the reserved-filenames set + one line at the write_bundle compose site. Mirrors code that already exists.

Option B — producer-side per-client config (env/config in the platform deploy). The theme_config kwarg already threads all the way down, so the plumbing would work if the platform supplied it at flow.py:4768. But this puts the client’s editorial map on the platform’s deploy surface, splitting client config across two homes (overlay in the bundle, themes in the platform) and violating the DR-016 posture that the client owns their bundle’s shape. Rejected as the primary home.

Option C — env var (OKF_THEME_CONFIG as JSON). Env is for deploy-time wiring (OKF_BUNDLE_DIR), not structured editorial data. A ~17-theme × N-concept map is too large and too frequently edited for an env var, and it’s not client-editable. Rejected.

Recommendation: Option A. Co-locate the theme map with the overlay it is a sibling of, on the same client-owned surface, read the same way. It keeps DR-016, DR-055, and DR-027 intact and is producer-deterministic.

Interaction with the {45.10} deploy-time seeding boundary. Because the theme map is client-editorial (not producer-generated), it sits on the client side of the seeding boundary — the same side as the overlay. Concretely: the deploy seed can drop a starter index-themes.json (the §3 draft below) when the bundle repo is first minted, so the very first producer run yields a themed index.md instead of all-Other; from then on the client owns and edits it. The producer never regenerates or overwrites it (reserved filename). This keeps deploy-time seeding to a one-time scaffold and leaves steady-state ownership with the client.

File format. theme_config is an ordered [(heading, [rel_path, ...]), ...]; section order in index.md follows config order. JSON has no tuples, so the readable client-editable shape is an ordered array of objects; the reader converts to the tuple form build_index_themes expects:

[
{ "heading": "Company Overview", "concepts": ["company/overview.md"] },
{ "heading": "Products & Service Offerings", "concepts": ["products/stocksense-ordering-portal.md", "..."] }
]

3. DRAFT Platform-bundle theme map (all 18 concepts, no leftovers)

Section titled “3. DRAFT Platform-bundle theme map (all 18 concepts, no leftovers)”

Themes are business-domain (bid-writer’s navigation), derived from concept frontmatter/tags plus the corpus signals (the Platform seed corpus carries a capability statement → company, procurement forms → tendering, methodology/sector-intel → services), and cross-checked against the first client’s real “BID RESPONSE TOPIC INDEX” row vocabulary. Order below is the intended index.md section order (who we are → what we sell → what we’re accredited for → how we operate → proof).

As a Python literal for theme_config (also the shape the JSON reader would produce):

theme_config = [
("Company Overview", [
"company/overview.md",
]),
("Products & Service Offerings", [
"products/stocksense-ordering-portal.md",
"products/rapidrestock-managed-print-service.md",
"products/greencycle-furniture-programme.md",
]),
("Accreditations & Standards", [
"certifications/iso-9001.md",
"certifications/facilities-assurance-standard-fas-2100.md",
"certifications/sscm.md",
"certifications/sustainable-supply-chain-mark-sscm.md",
]),
("Quality Management", [
"topics/quality-management.md",
]),
("Data Protection & Information Governance", [
"topics/data-protection.md",
]),
("Sustainability & Social Value", [
"topics/social-value.md",
]),
("Bids & Tendering", [
"topics/procurement--tender-evaluation.md",
"case-studies/won-bid/northgate-borough-council.md",
]),
("Client Case Studies", [
"case-studies/corvedale-academies-trust.md",
"case-studies/northgate-borough-council.md",
"case-studies/ridgeway-commercial-services-ltd.md",
"case-studies/st-aldhelm-s-nhs-foundation-trust.md",
"case-studies/wyndale-metropolitan-borough-council.md",
]),
]

Coverage check: 1 + 3 + 4 + 1 + 1 + 1 + 2 + 5 = 18 concepts, zero left to Other.

Rationale / non-obvious calls:

  • Concepts are claimed exactly once (build_index_themes invariant), so cross-cutting concepts get one editorial slot. greencycle-furniture-programme is tagged sustainability/circular-economy/social-value but is a product — placed under Products (it is a sellable programme); its social-value story is reachable via the Sustainability theme’s own concept.
  • The won-bid case study (case-studies/won-bid/…) is placed under Bids & Tendering alongside the tender-evaluation topic — it is the “proof we win tenders” evidence a bid-writer wants there, and it keeps the two Northgate concepts from sitting side-by-side under one heading. The five non-won engagements stay under Client Case Studies. This mirrors the bundle’s own won-bid/ subdirectory split (README).
  • Quality Management and Data Protection are kept as their own single-concept themes because a bid-writer navigates by those exact topics (the client’s real bid index has distinct rows for both). If the owner prefers fewer headings, the cheapest merge is folding Quality Management into Accreditations & Standards (ISO 9001 is the shared anchor) — a one-line edit.
  • Observation (out of scope, flag only): certifications/sscm.md and certifications/sustainable-supply-chain-mark-sscm.md are near-duplicate concepts (same SSCM accreditation). Both are mapped here; whether one should be removed is a data/producer question, not a theme-map question.

4. Preliminary first-client theme sketch (headings only)

Section titled “4. Preliminary first-client theme sketch (headings only)”

No client bundle exists yet (no concepts minted), so this is candidate headings, not a concept map. The first client corpus already contains its own “BID RESPONSE TOPIC INDEX” (in the corpus’s deduplicated bid-library markdown) — i.e. the client has, in effect, already authored the ~17-theme business-domain classification this feature is built for. These candidate themes are lifted from that index (they are generic bid-topic categories, not client-identifying):

Candidate themeOne-line rationale
Company Overview & CredentialsAnchor “who we are” row; every bid opens here.
Products & ServicesThe client’s service/digital-product catalogue (corpus 02-services-and-digital-products).
Data Protection & GDPRDistinct, high-frequency bid topic; corpus has a dedicated section.
Security & Information GovernanceISMS / governance answers, separate from data-protection specifics.
Penetration Testing & Vulnerability ManagementRecurrent technical-assurance bid question.
Encryption & Data SecurityEncryption-at-rest/in-transit answers.
Business Continuity & Disaster RecoveryStandard resilience bid section.
Development & Change ManagementSDLC / change-control assurance.
Support & ImplementationOnboarding, SLAs, implementation model (corpus bid-library “Implementation & Support”).
Insurance & CommercialInsurances, pricing, commercial terms.
Corporate Social Responsibility & Social ValueCSR / social-value row (corpus 06-company-values-and-ethos).
Sub-Contractors & Supply ChainSupply-chain and sub-contractor disclosures.
Staffing & PeopleTeam structure, key people, vetting (corpus 05-team-structure-and-key-people).
Secure Disposal & Data SanitisationEnd-of-life data handling.
Audit & MonitoringLogging, audit trails, compliance monitoring (corpus 07-compliance-governance-and-certifications).

That is ~15 headings — squarely the “~17 business-domain themes” BI-5 anticipates, and materially broader than the Platform seed’s 8. It confirms the design premise: theme count and shape are per-client, so the map must be a per-bundle client-owned config, not a producer constant.


5. What remains the owner’s call (ratification list)

Section titled “5. What remains the owner’s call (ratification list)”
  1. Config home — ratify Option A (a client-owned index-themes.json at the bundle root, sibling of ontology-overlay.json) vs Option B/C. (Recommendation: A.)
  2. The file nameindex-themes.json? theme-config.json? (Needs to be added to _RESERVED_BUNDLE_FILENAMES.)
  3. Platform theme headings + membership — ratify the §3 map, including the specific calls: won-bid → Bids & Tendering; GreenCycle → Products; keep vs merge the Quality Management and Data Protection singletons.
  4. Section order — ratify the §3 ordering (or reorder).
  5. Deploy-seed policy — should the deploy drop the §3 starter map so the first client producer run is themed out of the box, or is an all-Other first index acceptable until the client authors their own? ({45.10} boundary decision.)
  6. First-client themes — the §4 sketch is for planning only; the actual client theme map is ratified when/if a client bundle is minted, against that bundle’s real concepts.
  7. SSCM duplicate — a separate data/producer decision, flagged here for visibility.