Skip to content

S528 stale-concept census — shared grounding brief

S528 stale-concept census — shared grounding brief

Section titled “S528 stale-concept census — shared grounding brief”

ROUND 2 (owner review of the round-1 board). Read §0 FIRST — it corrects errors that are in the round-1 reports, including verdicts that looked well-evidenced.


0. What round 1 got wrong, and the grounding it was missing

Section titled “0. What round 1 got wrong, and the grounding it was missing”

0.1 Two DR-104 traps were sprung — by the brief’s own author, in this file

Section titled “0.1 Two DR-104 traps were sprung — by the brief’s own author, in this file”

Both are listed in §2.3 below. Both were used as load-bearing evidence anyway. If your round-1 report contains either pattern, revise it.

  • Trap 1 — “N consumers, therefore live.” The round-1 headline was taxonomy_domains/taxonomy_subtopics have 15+ live consumers → KEEP → retiring them would break live product.” That is consumer-counting presented as a liveness proof. Nobody asked whether the consumers are themselves stale surfaces. Owner: “‘classifier prompts’ (classifying what?), ‘batch reclassify’ (same), ‘break live product’” — a consumer that consumes a stale concept is evidence the rot spread, not evidence the concept is live. batch_reclassify is a transplant from a CLI that was retired and has no UI trigger anywhere; that is not a vote for keeping anything. The correct test is not “who calls it” but “what current product requirement does this serve, and is that requirement’s chosen mechanism this one?”
  • Trap 2 — “0 rows / never run, therefore dead.” Used against coverage_targets, content_propagation_version, form_requirement_templates and the propagation worker. Owner, verbatim: “no runs doesn’t mean not required — we don’t have more than one client, so of course there are no rows.” Emptiness is never evidence in this codebase. Delete the argument wherever you made it, and re-derive the verdict from purpose.

0.2 The requirement source we were missing — READ THIS BEFORE ANY VERDICT

Section titled “0.2 The requirement source we were missing — READ THIS BEFORE ANY VERDICT”

/Users/liamj/Documents/development/kh-client-content-archive/docs/client-briefs/

  • kh-client-feedback.md (38 lines — read in full). Real client feedback, 29/04/2026, from using the MCP tooling to complete an LBBD Cloud Security Principles DPIA. This is the origin of several concepts the census was treating as unexplained:
    • Item 3 is where scope_tag / anti_scope_tag come from, and it is a safety requirement, not a tagging nicety: “The most damaging mistakes … were the AI confidently pulling correct content from the wrong domain.” The Bitdefender case — content correctly tagged Internal IT surfaced as the answer for production-infrastructure questions — “Production infrastructure doesn’t run Bitdefender, so we’d have given LBBD a false claim.” Closing line: “A high-confidence match in the wrong scope is much more dangerous than a low-confidence match in the right scope.”
    • Item 2 explicitly requests secondary tags: “either the filter logic needs to be more permissive or the ingest needs to apply secondary tags so security-adjacent product content is reachable from a security search.” Round 1 proposed dropping secondary_domain/secondary_subtopic as read-never-written. That verdict is now on hold — the requirement says wire, not drop.
    • Item 2 also reports domain-filtering under-returning in the client’s hands.
  • kb-hub-gap-analysis-action-tracker.md (497 lines). Owner-named key items: B.1, B.2, B.4, B.5, B.6. B.1 and B.2 carry recorded “Liam decision” with spec pending, and both are built on guide_sections.expected_layer / subtopic_filter / content_type_filter and guides.domain_filter.

Owner directive on how to use these: “it’s not the approach outlined in the docs that’s important, but the underlying requirement that needs to be implemented.” Do not treat the proposed solutions as design authority — extract the requirement, then ask whether the current mechanism serves it.

0.3 The functional-direction arc: id-130 → id-165

Section titled “0.3 The functional-direction arc: id-130 → id-165”

Owner: “if you see the flow of tasks from around id-130 → id-165, you’ll see the direction the platform has focused on from a functionality perspective.”

Post-165 the platform adopted ordna, and old backlog tasks were renumbered into the 400s. Two consequences:

  1. The 130–165 range is the reliable functional-direction record — it sits above DR-106’s “below ~130 is presumed stale” waterline and below the renumbering.
  2. A 400-series id can be renumbered old backlog carrying pre-130 framing while looking current. DR-106’s id-based heuristic does not catch it. For any 400-series task, check created_at and the body’s vocabulary, not the id.

Worked example — search. The retrieval direction is hybrid_search plus two facets:

  • id-144 (archived) — hybrid_search completion with scope_tag projection = client Item 3
  • id-162 (todo) — cross-grain keyword facet; “Only source_documents.ai_keywords exists today; q_a_pairs + reference_items carry NO keyword array” = client Item 2
  • id-135 (archived, 31 subtasks) — built the human search surface on hybrid_search, unifying human + AI search

So domain/subtopic filtering in search is what those replace, not something to preserve. It is also what the client reported failing, and what silently returns nothing over the answer grain. Round 1 counted search/browse and review-queue filters as consumers carrying the KEEP verdict; on this evidence they are on the superseded path.

0.4 The ontology direction is register → DB, NOT DB → register

Section titled “0.4 The ontology direction is register → DB, NOT DB → register”

Round 1 (lane 3) recommended “invert the authority: generate baseline_values from the live DB.” Withdrawn. Owner: “wouldn’t inverting and being DB-driven be a move away from the purpose of an ontology, with cleanly defined terminology?” — and lane 3’s own §1 documents the ratified flow as register markdown → parse flow → DB → linter, i.e. the register is upstream and normative.

An ontology generated from the schema can only describe what the DB happens to contain; it loses the power to say a value is wrong, and it makes the OKF linter circular. What lane 3 genuinely proved is that there is no enforcement in either direction. The fix that follows from the ratified design is to build the missing register → DB sync (the never-implemented parse-flow.py) and gate CI on the DB conforming to the register.

0.5 Settled by the owner at the round-1 board — do not re-open

Section titled “0.5 Settled by the owner at the round-1 board — do not re-open”
ItemRuling
scope_tagValid concept, remains. Not a retirement candidate.
id-364 F1 / C5A reference item does NOT need a source_document. Different concepts: source documents are walked, reference items are not. Consistent with the S511 pivot (“reference_items ≠ source_documents — SEPARATE concepts”) and with the keep_and_watch = re-walked retention semantics. Closed; wants a DR.
bid form_typeDrop from the vocabulary everywhere. The migration reclassified biditt (not tender) under a guarded DELETE that fired only at zero referencing rows.
classification_disputesRetire.
/api/cron/classification-qualityDelete the route.
goose service actorKEEP — id-71 is in flight. Round-1 proposed retiring it; struck.
B1 / B2 / B4 delete listsApproved, execute.
A1 A2 A3 A4Fix. A5 held behind the taxonomy ruling; A6 retire the sync trigger; A7 folded into the taxonomy ruling.
C2 (secondary_*)HELD — conflicts with client Item 2. Re-derive, do not drop.
Non-responsesOwner: “for anything I didn’t respond to, it’s due to incorrect framing, or a wider revisit being required.” Reframe; never re-ask the same question.

0.5b A TASK DIRECTIVE IS NOT AUTHORITY (owner ruling, S528 — round 3)

Section titled “0.5b A TASK DIRECTIVE IS NOT AUTHORITY (owner ruling, S528 — round 3)”

This is the same trap as consumer-counting, one level up, and round 2 sprang it.

Owner, verbatim: “Many tasks that have been minted, pre and post id-130, will have done so on exactly the premise this session set out to resolve — just because a task says include it, doesn’t mean it’s correct.”

Worked example, and it is a live round-2 verdict. Round 2 overturned the read that domain/subtopic search filtering is superseded, by citing id-144 OD-3“Wire kind/domain/subtopic/date filters server-side” — and concluding that keeping domain filtering is the ratified response to the client feedback. That reasoning is not available. id-144 was minted inside the frame under investigation. Its directive records that someone decided it; it does not record that the decision was re-derived from the requirement.

Re-read the client evidence without id-144 as authority: Item 2 reports domain filtering over-excluding“returned good content but excluded product-feature and implementation entries which is where a lot of the LMS-specific answers live.” Round 2 read Item 2’s remedies as presupposing domain filtering must exist. They equally read as workarounds for a mechanism that did not fit. The question is open.

The rule, and it now governs every verdict in this census:

  • A task’s goal text, AC or owner-directive is evidence of INTENT AT THAT TIME, never of correctness — exactly as DR-104 treats code, and DR-106 treats stale docs. This closes the last gap: DR-104 covered code, DR-106 covered docs, nothing covered task files.
  • This applies to pre-130 AND post-130 ids. The 130–165 arc (§0.3) shows the direction of functional focus; it does not certify that any individual task’s mechanism choice was re-derived. Do not read §0.3 as a warrant for the tasks inside it.
  • What DOES carry weight, in order: the client’s actual reported need and failure modes (§0.2); measured current behaviour; then a ratified DR that explicitly ruled on the question you are asking.
  • When a task directive and the requirement point different ways, say so and surface it — that is a finding, not a conflict to resolve by deferring to the task.

Owner’s framing of the correct method: “the approach we use for e.g. 144 should be based on the valuable context that we’ve started properly surfacing here.”

0.6 Recall is the first move, not the last (owner directive)

Section titled “0.6 Recall is the first move, not the last (owner directive)”

“Most context that we need is already available in mempalace, and where there’s uncertainty, I can add more info.”

Search memory BEFORE deriving anything from code. Several round-1 “findings” were already settled — C5 had “been answered multiple times”.

The mempalace MCP tools are deregistered this session. Use the lock-free FTS. Note the documented recipe is broken — a bare hyphenated term raises no such column; FTS5 needs each term double-quoted. Working form:

Terminal window
DB="file:$HOME/.mempalace/palace/chroma.sqlite3?mode=ro&immutable=1"
sqlite3 "$DB" "SELECT substr(replace(string_value, char(10),' '),1,300)
FROM embedding_fulltext_search
WHERE string_value MATCH '\"taxonomy_domains\" AND \"retire\"'
AND string_value NOT LIKE 'CHECKPOINT:%'
ORDER BY rowid DESC LIMIT 8"

If recall and code disagree, say so explicitly rather than silently preferring the code. And if a question needs owner context you cannot find, say what you need — the owner has offered to supply it, and that is cheaper than a wrong verdict.


Every lane MUST read this file and .user-scratch/stale-codebase-concepts-and-context-rot.md (the owner’s own feedback) before forming any verdict. They are the frame. A finding that contradicts them needs stronger evidence than a finding that agrees with them, but a finding that contradicts them and IS well-evidenced is exactly what we want — say so plainly.


1. The standing frame (owner directive, S528) — this overrides ordinary caution

Section titled “1. The standing frame (owner directive, S528) — this overrides ordinary caution”
  • The platform is NOT live. It is in development. There are no clients, no users, no bookmarks, no external integrations depending on any URL, route, table or column.
  • ALL data in Platform staging AND Platform prod is synthetic. The staging/prod split exists only to prove the deployment configuration is correct.
  • Therefore these arguments carry ZERO weight and must never appear in a verdict:
    • “prod has rows that staging doesn’t”
    • “we’d break existing links / bookmarks / saved views”
    • “we need a migration path / compatibility shim / deprecation window”
    • “it’s referenced in prod so it must be live”
  • The default disposition for something no longer required is DELETE IT ENTIRELY — not deprecate, not shim, not leave-with-a-comment. Half-retirement is the exact debt this census exists to clear. If a thing should go, say “delete these N files / these N columns” and list them.
  • A file with more comment than code, or a comment explaining why something was forced to fit, is a finding in itself. Flag it.

2. Evidence precedence — docs outrank code (DR-104)

Section titled “2. Evidence precedence — docs outrank code (DR-104)”

The codebase is KNOWN to be inaccurate. Large parts are half-repointed work. So:

  1. Ratified docs are the authority, in this order: corpus-reframe-review.html (R1–R8), the Decision Register (reference/decision-register.md + reference/decisions/), reference/platform-direction.md, reference/entity-glossary.md, the OKF doctrine, then the task’s own spec chain.
  2. Code is evidence of what EXISTS, never of what is CORRECT. An existing reference is the symptom under investigation, not proof the thing is right.
  3. These four readings are NOT evidence and each produced a wrong verdict in S515:
    • “N callers import it, so it’s live.” Many callers is what incomplete repointing looks like. Check whether the callers were migrated (do they pass the CURRENT fields?).
    • “There are rows in the DB, so the write path works.” Both Platform DBs are internal dev environments. A populated column proves nothing; an empty one may be unexercised, not dead.
    • “Grep found nothing, so it never existed.” Absence is the thing being fixed.
    • “Every assertion binds a live table, so this spec matches the model.” A test can bind live tables while asserting a retired concept. Check naming and framing, not just identifiers.
  4. A term with no reference/entity-glossary.md entry is a drift risk. A coinage that only ever appeared in a session working note is not vocabulary.

2a. THE LIMIT OF DR-104 ON THIS TASK (owner ruling, S528) — read this before citing any doc

Section titled “2a. THE LIMIT OF DR-104 ON THIS TASK (owner ruling, S528) — read this before citing any doc”

DR-104 says ratified docs outrank code as evidence of correctness. It assumes the doc was derived from a correct model. This census is investigating the fundamentals the platform was built upon — so a doc written while those fundamentals were assumed is not independent evidence that they are right. It may be the assumption’s carrier.

Worked example, and the pattern to apply generally. initiatives/core-product/canonical-pipeline/intended-architecture/01-vision.md is [CURRENT-CANONICAL], “last verified 28/07/2026”, and retains classification/taxonomy in §1.1, §3.2 and §5.2. That looks like it settles the taxonomy question. It does not:

  • The doc’s own header describes that re-verify as “§5 workspace-era language rewritten to activity terms per DR-038, stale docs/… path refs re-pointed to their docs-site homes, heritage-table paths refreshed.” A path-and-terminology pass. It never touched §1.1/§3.2/§5.2. The stamp certifies the paths, not the model.
  • The doc supersedes Phase-0.9 0.9-intended-architecture.md, whose heritage table lists Knowledge Hub — Platform Overview.md (23/04/2026) as [PARTIALLY-SUPERSEDED] — IMS-adjacent vintage.

The generalisable rule: a “last verified” date certifies only what that verification pass actually did. Read the verification note before treating the stamp as a warrant for the claim you care about. If the pass fixed paths and renamed terms, it did not re-derive the model.

Same caution for the Decision Register. A DR that ruled within the inherited frame does not validate the frame:

  • Narrow, reliable: DR-034 retires the content_items-era coverage feature. That is a real retirement ruling on a specific thing.
  • Scope-limited: DR-027 rules on the CV medium (where the ontology lives), not on whether the concepts the CVs encode are correct.
  • Say what each DR actually ruled on, and whether the fundamental was inside its scope. Where a DR is being asked to do more work than it did, flag it for the owner — do not treat it as closed. Recall the S516 shape: a ruling can retain a table while enumerating a shape that excludes the columns in question.

This does NOT license ignoring the docs. It licenses one specific move: treating a doc’s claim about a fundamental as an input with stated provenance, not as a closure. When you cite a doc for a fundamental, say when the claim was written, what verified it, and what that verification actually covered. Prefer an empirical answer (“here is the flow as it runs today”) over a doctrinal one wherever the question admits of measurement.

2b. Settled by the owner — do not re-litigate (S528)

Section titled “2b. Settled by the owner — do not re-litigate (S528)”
  • scope_tag is a VALID concept and REMAINS. Not a drift risk, not a retirement candidate, despite its absence from entity-glossary.md. Reporting on how it is implemented is still useful; proposing its retirement is not.
  • The Layer-1 concepts are already known to be incorrect from the owner’s own research, and the taxonomy tier is IMS-inherited. Treat both as established starting context.

Not every doc is a north-star doc (DR-106) — citing a stale doc is the same error as citing code

Section titled “Not every doc is a north-star doc (DR-106) — citing a stale doc is the same error as citing code”

Presumed STALE, read for how we got here, never cite as authority:

  • initiatives/core-product/canonical-pipeline/intended-architecture/**EXCEPT 01-vision.md, which IS current. 09-diagrams.md and 02-data-flow.md are stale.
  • canonical-pipeline/phase-0-investigation/** (the whole OQ-resolution chain).
  • Any spec or invariant for a task id below ~130 unless a current doc re-affirms it.
  • 07-collapse-list.md is the qualified case: its [RATIFIED-RETIRE] rows are usually still sound; its retain / NOT-COLLAPSING entries are the ones most likely to have flipped; absence from it is evidence of nothing.

Two failure shapes seen in S516:

  1. Following a citation instead of opening it. When a verdict rests on “doc A says X per doc B”, read doc B. S516 found a doc claiming “RESOLVED-RETAIN per X §5.2.1” where X actually said DEFER-COCOINDEX.
  2. Retain-the-table read as retain-the-shape. A ruling can keep a table while enumerating a shape that excludes the columns you’re asking about.

Read-never-written: which half is stale? (S516)

Section titled “Read-never-written: which half is stale? (S516)”

A column with readers and no writer has two diagnoses. The discriminator is: does a current ratified writer exist that COULD have written it and did not?

  • No such writer → the reader is right, the writer is missing. Wire it.
  • Yes, and it deliberately omits the column → the reader outlived its model. Retiring the read surface may be right. “Wire the writer” is the wrong default.

Zero population never distinguishes the two. The writer’s column list does.


3. What is already measured — do not re-derive, but DO challenge if you find contrary evidence

Section titled “3. What is already measured — do not re-derive, but DO challenge if you find contrary evidence”

Measured this session against Platform staging rbwqewalexrzgxtvcqrh:

FactValue
taxonomy_domains inbound FKsexactly 2: coverage_targets.domain_id, taxonomy_subtopics.domain_id (self-hierarchy)
taxonomy_subtopics inbound FKszero
coverage_targets rows0 — and DR-034 already ruled the content_items-era coverage feature RETIRED, not re-pointed
content_types, platformsnot tables — they exist only as vocabulary inside scripts/tests/fixtures/taxonomy_snapshot.json
taxonomy_domains / taxonomy_subtopics rows7 / 34 — exactly matching the snapshot
content_itemstable was DROPPED at ID-131 M6 (S450). It does not exist on any environment.

The taxonomy VALUES leaked into six tables as unconstrained free text with NO foreign key:

  • source_documents: primary_domain, primary_subtopic (both NOT NULL DEFAULT 'unclassified'), secondary_domain, secondary_subtopic, content_type, classification_confidence, classification_reasoning, classified_at
  • reference_items: primary_domain, primary_subtopic, layer
  • form_requirement_templates: primary_domain, primary_subtopic, secondary_domain, secondary_subtopic (0 rows)
  • guides.domain_filter; guide_sections.subtopic_filter / content_type_filter / expected_layer; review_assignments.filter_domains / filter_content_types (0 rows)

The working hypothesis this census must confirm or kill: the taxonomy TABLES feed exactly one dead consumer, while the taxonomy VOCABULARY proliferated across six tables with no referential integrity — i.e. the concept was carried over from the forked IMS repo and then “forced to fit” the Canonical model rather than being retired and re-designed as an ontology layer. This is a hypothesis, not a verdict. Schema topology alone cannot settle it (DR-104). Kill it if the evidence says otherwise.


4. Origin context — why these concepts exist at all

Section titled “4. Origin context — why these concepts exist at all”

Canonical was forked from an IMS repo when it was called knowledge-hub. The IMS’s sole purpose was: the owner personally ingested content from LinkedIn / Reddit / websites, then searched it for useful information. taxonomy_domains, taxonomy_subtopics and coverage_targets are all IMS-era concepts. Canonical now does ETL through cocoindex and the OKF three-layer model — a fundamentally different approach.

The owner’s position (to be tested, not assumed): the platform should not still be anchored to that IMS use case. Taxonomy should be a layer of the ontology, per initiatives/core-product/knowledge-base-foundations/ontology/phase-b-prerequisite-1-onthology-pipeline.md and its -feedback-investigation.md sibling — not a pair of standalone tables plus free-text columns scattered across the schema.

The only coverage concept that should survive: how “covered” a form instance is, based on available q&a pairs for completing it — with gaps filled by manual entry, wider-corpus search including OKF, etc. That is NOT what coverage_targets models.

Historical-only context on the original client requirement (outdated, cite for origin only): kh-client-content-archive/docs/client-briefs/{Product_KB_Dev_Brief,Sector-Intelligence-Brief-Liam-Final,sector-intelligence-analysis}.md


5. Tooling routing — use these, do not grep-first

Section titled “5. Tooling routing — use these, do not grep-first”
  • Discovery / “where is X used”mcp__memtrace__find_symbol, find_code, get_symbol_context. Memtrace is indexed for this repo.
  • Impact / blast radius / renamesGitNexus is the authority (DR-071), pass repo: 'canonical'. Index is fresh at HEAD c3286753f (17,991 nodes / 38,341 edges). When GitNexus and Memtrace disagree on blast radius, GitNexus wins.
  • Column-level wiring verdicts → the four-surface protocol: bun run ast-dataflow-py schema-uses --exclude-tests > /tmp/py.json then bun run ast-dataflow schema-coverage --evidence /tmp/py.json --report out.md. Protocol doc: specs/id-399-census-protocol/census-protocol.md. Three hard qualifications (S511 {377.6} trials):
    1. Never drill down with column-writes — 18.5% false positives over 65 hand-checked sites; 44% of rows come back for any column name, including non-existent ones.
    2. A plain-run unwired is not “dead” — without the Python sidecar, 7% of unwired verdicts are live Python-written columns. Run it with the sidecar or not at all.
    3. Trustworthy at VERDICT granularity, untrustworthy at SITE-COUNT granularity. Open every site before acting on a retire. Price no site count you did not verify by hand.
  • Dead exports / unused depsknip. Duplicationjscpd. Codemodsts-morph.
  • Prior evidence already banked, read before re-deriving: specs/id-399-census-protocol/sweep-inbox-377-6-cleanup-evidence.md.

Write your findings to the path named in your lane brief, and return a SHORT summary (not the file contents). The file must be a markdown table plus per-finding detail, where every row has:

FieldRule
Targetpath/to/file.ts:LINE, or table.column, or docpath.md §N. Exact.
Verdictone of RETIRE / KEEP / REWRITE / UNDECIDABLE
Confidencehigh / medium / low
Evidencethe ratified anchor (DR-NNN, doc + section, or a tool run you can name). Not “it looks unused”.
Blast radiuswhat else must go with it — files, tests, columns, docs, migrations
Owner actionone line the owner can act on, e.g. “delete these 4 files”

Rules for the report:

  • UNDECIDABLE is a first-class answer and is strongly preferred over a guessed RETIRE. Say exactly what evidence would settle it.
  • Group by concept, not by file. The owner wants “this concept goes, and here are its 23 files”, not 23 unrelated rows.
  • Separate the provably-dead from the judgement calls. Lead the report with a “delete these now, zero ambiguity” section if you have one.
  • Do not edit any file. This is a census. Report only.
  • State what you could NOT cover and why. Silent truncation reads as full coverage.
  • Cite line numbers you actually opened. Do not cite a site count a tool gave you without opening the sites.