Skip to content

MemPalace mining design — docs-site + ordna tasks (S518)

Point-in-time report, authored S518 (2026-07-31/08-01). Ratified rulings are in section 11 and in DR-112; where the two differ the decision record wins. The removal-*.txt path lists referenced below were a 2026-07-31 snapshot in an ephemeral scratchpad and are not committed — regenerate them from the queries in section 0.1 against current palace state before executing anything, which the execution sequence already requires as its step 2.

Design only. Nothing executed. No palace writes. All palace access was lock-free sqlite (mode=ro&immutable=1). The vector layer was never opened; no MCP mempalace tool and no mempalace CLI invocation was made.


0. Method, and what changed versus the brief

Section titled “0. Method, and what changed versus the brief”

All against file:$HOME/.mempalace/palace/chroma.sqlite3?mode=ro&immutable=1.

#PurposeShape
Q1Family rollupsource_file LIKE '%/knowledge-hub-docs-site/src/content/docs/%', family = first path segment after /src/content/docs/ via substr(...,instr(...,'/src/content/docs/')+18), grouped with COUNT(*) and COUNT(DISTINCT string_value)
Q2Wing / room / mode crosstabsfamily CTE LEFT JOIN a per-key CTE on id, for wing, room, extract_mode, ingest_mode, normalize_version, added_by
Q3Value-column auditSUM(string_value IS NOT NULL), int_value, float_value, bool_value per key — corrected Q2, which had missed int-typed keys
Q4Multi-wing testSELECT nw, COUNT(*) FROM (SELECT path, COUNT(*) nw FROM (SELECT DISTINCT path, wing …) GROUP BY path)
Q5Date-signal coverageper-family presence of content_date, source_mtime, authored_at, id_recipe, entities
Q6Extension splitdistinct source_file piped through sed 's/.*\.//' | sort | uniq -c
Q7Collections / segmentscollections, segments, embeddings JOIN segments JOIN collections
Q8Closet (KG) contentsmetadata keys + chroma:document samples restricted to the mempalace_closets collection id
Q9Entity qualityall entities values split on ;, frequency-ranked; then regex-tested for DR-, id-, S###, fr-
Q10Wing hygieneGROUP BY wing HAVING COUNT(*) < 50
Q11Unmined gapcomm -23 of on-disk .md set against mined .md set
Q12Removal-set sizingpython sqlite3 with a TEMP table of candidate paths, JOIN on string_value

Non-palace evidence: on-disk find/stat over $KH_PRIVATE_DOCS_DIR; git log; and reads of the installed miner source at ~/.local/share/uv/tools/mempalace/lib/python3.12/site-packages/mempalace/ (read-only).

0.2 Corrections to the brief’s given facts

Section titled “0.2 Corrections to the brief’s given facts”

Three of the inherited numbers did not survive verification. None invalidate the brief’s direction, but two change the design.

  1. docs-site total is 107,938 drawers, not 109,162 (Q1). The delta (1,224) is unexplained; the likely cause is a join that double-counted. I have used 107,938 throughout. There are zero mined docs-site paths outside src/content/docs/, so nothing is hiding elsewhere.
  2. extract_mode / ingest_mode / authored_at do not exist on the docs-site corpus at all — not “distributed somehow”, but 0/107,938 for each (Q5). Q3 shows both mode keys are written only on the convos path (sessions wing, value exchange, 225,940 drawers). The brief asked for their distributions; the correct answer is not applicable to projects mode, and no proposal should attempt to set them.
  3. content_date and source_mtime DO exist on projects-mode drawers — 90,782 (84%) and 106,847 (99%) of docs-site drawers respectively (Q5). The brief stated chronology must come entirely from the register/ordna/git because projects mode “gets no authored_at”. That is true of authored_at specifically, but source_mtime is a real, populated, per-file recency signal already in the palace, spread across 46 distinct days from 2026-04-28 to 2026-07-24 and matching on-disk mtime patterns. §5 is built on it.

I also confirmed the brief’s load-bearing claims that did hold: 480 orphans all genuinely absent from disk; tasks/ mined zero times; authored_at 0/107,938 on docs-site; exclude_patterns is projects-mode only.

0.3 Mechanism facts established from the miner source

Section titled “0.3 Mechanism facts established from the miner source”

These are what make the proposal actionable rather than speculative.

  • mempalace.yaml in the project root supplies wing, rooms: [{name, description, keywords}], and exclude_patterns (miner.py:484, :1780).
  • detect_room (miner.py:552) routes by path, outermost-segment-first. It walks path_parts[:-1] and, for each part, tests every room’s name and keywords; first match wins. Only then does it try filename, then content keyword scoring, then general.
  • Matching is token-based (_tokens splits on -, _, ., /), so keyword themes matches path part themes, and keyword prompts matches continuation-prompts.
  • exclude_patterns uses the same parser as .gitignore, including ! negation and ** globs (miner.py:308). .gitignore files in the tree are also honoured independently (load_gitignore_matcher, miner.py:386).
  • content_date (_extract_content_date, miner.py:1290) resolves in strict order: filename date → YAML frontmatter date/created/published → first ISO date in the first 10 body lines → filesystem mtime. It is computed once per file and stamped identically on every chunk of that file (miner.py:1499).
  • source_mtime is raw os.getmtime as a float (miner.py:1383).
  • Chunking: CHUNK_SIZE=800, CHUNK_OVERLAP=100, MIN_CHUNK_SIZE=50. Observed mean drawer length on surviving spec markdown is 679 chars.
  • A --wing override exists (wing_override, miner.py:1778) — this is how the ad-hoc specs / runbooks / reference / handoffs wings were created.

Assumption (stated, not verified): I could not execute the miner, so room routing, exclusion matching and drawer counts are reasoned from source, not observed. Every proposed config change below therefore carries a dry-run checkpoint in §7.


1.1 Per-family, layered onto the given file counts

Section titled “1.1 Per-family, layered onto the given file counts”

All docs-site drawers share normalize_version = 2, added_by = mempalace (with a small (none) tail from an older writer), room = general, and no extract_mode / ingest_mode / authored_at. Those columns are therefore omitted; they are constant.

familydrawersmined filesdrawers/filewings (drawers)on-disk .mdorphansunmined .md
workflow-evaluation43,887265165.6wing_canonical 43,887600
specs24,21257242.3specs 14,272 / wing_canonical 9,9405891110
ledgers16,18251131.7wing_canonical 16,18224429023
initiatives7,07711064.3wing_canonical 7,07710900
themes6,5839867.2wing_canonical 6,5830980
continuation-prompts3,53727512.9wing_canonical 2,708 / handoffs 8292287023
reference2,3053467.8wing_canonical 1,946 / reference 35914410120
runbooks2,0014940.8wing_canonical 1,396 / runbooks 60540123
product-functionality1,3792849.3wing_canonical 1,3792800
ontology4523911.6wing_canonical 4523900
reports142623.7wing_canonical 14237031
design140623.3wing_canonical 140600
handover-guides39219.5wing_canonical 39200
(root)212.0wing_canonical 200
total107,9381,99654.11,473480310

1.2 The five findings that drive the whole design

Section titled “1.2 The five findings that drive the whole design”

F1 — 41% of the corpus is machine telemetry, not documents. workflow-evaluation is 43,887 drawers from 265 files, of which only 6 files (208 drawers) are markdown. The rest:

extdrawersfiles
.jsonl42,43092
.yaml1,13075
.json11992
.md2086

These are sessions/session-<uuid>/<run>/events.jsonl machine event streams. The repo already ruled on them: .gitignore line 47–48 excludes src/content/docs/workflow-evaluation/sessions/ with the comment “eval-run artefacts; not canonical docs — S396”. The palace is holding 43,679 drawers of content the project has already classified as non-canonical.

F2 — a mempalace.yaml already exists, and its exclusion is both partial and unenforced. $KH_PRIVATE_DOCS_DIR/mempalace.yaml (411 bytes, 2026-07-28) sets wing: wing_canonical and one exclusion:

exclude_patterns:
- src/content/docs/workflow-evaluation/sessions/**/events.jsonl

Two gaps. (a) It matches 42,430 drawers / 92 files — leaving 1,457 drawers / 173 files of .yaml and .json in the same tree unexcluded. (b) It has no rooms: key, which is the direct and complete explanation for why all 107,938 drawers are room = general: load_config falls back to a single {name: general} room. The exclusion prevents future re-mining; it never removed what was already filed (filed_at for this family is 2026-07-03 and 2026-07-24 only — no mine has run since the yaml landed).

F3 — the Decision Register’s individual records are entirely absent from the palace. src/content/docs/reference/decisions/ holds 114 DR files on disk and 0 are mined (Q11). So does reference/change-log/ (2 files, 0 mined). The only decision content in the palace is the 82-drawer rollup reference/decision-register.md. Under DR-010, the register is the “what was decided” leg of the three-way routing — and it is the single least-represented surface in the palace. This is a larger correctness problem than the orphans.

More broadly, 310 on-disk markdown files have never been mined: reference 120, specs 110, reports 31, ledgers 23, continuation-prompts 23, runbooks 3. reports is 31 unmined of 37 on disk — the family the owner called “incredibly valuable context” is ~84% absent.

F4 — the orphan set is almost entirely superseded predecessors of the two corpora being mined.

orphan familyfileswhat superseded it
ledgers290 (backlog 142, tasks 127, roadmap 16, top-level 5)Corpus Btasks/id-N.md
themes98 (all of them)initiatives/ — deleted in commit fa71b6bf “chore: repurpose roadmap ledger to be initiative-focused”
continuation-prompts70rotated out point-in-time
runbooks12rewritten
reference10rewritten
specs1

The themes question the brief flagged has a clean answer: themes/ is the predecessor of initiatives/, removed wholesale by a single deliberate commit. 100% orphaning is not decay, it is a rename-and-restructure. Likewise ledgers/tasks/ + ledgers/backlog/ (269 files) are the pre-ordna per-item ledger — the exact content Corpus B replaces.

F5 — no file is double-mined; the wings are merely incoherent. Q4 is unambiguous: all 1,996 files sit in exactly one wing each. The specs (259 files), handoffs (70), runbooks (19) and reference (8) wings are disjoint subsets, not duplicates — the residue of ad-hoc --wing mines. So there is no content-level dedup problem inside the docs-site corpus; there is a routing problem. Any recall filtered to wing_canonical silently misses 356 files, and any filtered to wing=specs misses 313 of the 572 mined specs.

1.3 Non-markdown load outside workflow-evaluation

Section titled “1.3 Non-markdown load outside workflow-evaluation”
path classdrawersfiles
ledgers/task-list.json3,9961
ledgers/product-retros.json7701
ledgers/product-backlog.json2801
ledgers/product-roadmap.json521
ledgers/umbrellas.json31
specs/**/*.versions.jsonl, *.line-attribution.json, plan-subtasks.json1,40193
initiatives/**/corpus-reframe-review.html751

ledgers/task-list.json alone is 3,996 drawers — one machine-readable array of the pre-ordna task list, chunked at 800 chars into ~4,000 fragments of JSON syntax. All five ledger JSON files are already orphaned (gone from disk). The specs sidecars are spec-authoring machinery (.versions.jsonl, .line-attribution.json), not prose.


delete_by_source is exact-match on the full stored path, so the deliverable is explicit path lists. These files are produced but NOT executed.

fileclassfilesdrawers
removal-R1-orphans.txtGone from disk (independently verified)48118,037
removal-R2-workflow-eval-telemetry.txtMachine telemetry, all non-.md under workflow-evaluation/25943,679
removal-R3-legacy-json-ledgers.txtPre-ordna machine ledgers (*.json)55,101
removal-R4-spec-sidecars.txtSpec-authoring machinery (.versions.jsonl, .line-attribution.json, plan-subtasks.json)931,401
removal-R5-stray-binary.txtStray rendered HTML175
removal-UNION.txtde-duplicated union83363,153

Overlap is small and accounted for: all 5 R3 files and 1 R4 file are also in R1. Union impact: 63,153 of 107,938 docs-site drawers removed (58.5%), 44,785 survive. As a share of the whole 402,267-drawer palace, this is a 15.7% reduction.

All paths are absolute and in the exact form stored in source_file, e.g. /Users/liamj/Documents/development/knowledge-hub-docs-site/src/content/docs/ledgers/task-list.json.

2.1 The DR-010 judgement: what gets deleted vs re-homed vs retained

Section titled “2.1 The DR-010 judgement: what gets deleted vs re-homed vs retained”

The brief asks specifically whether the 290 orphaned ledgers and 98 orphaned themes should be deleted, given DR-010 says the palace uniquely holds “what was argued”. My recommendation splits them, because they are not the same kind of thing.

Delete outright — no argument content is lost:

  • R2 (43,679 drawers). Machine event streams. There is no argument in an events.jsonl chunk; DR-010’s “what was argued” protection does not attach to telemetry. The project already ruled these non-canonical in .gitignore at S396. This is the single highest-value deletion in the whole plan and it is uncontroversial.
  • R3 (5,101 drawers). task-list.json and friends are serialisations of state, not discussion. Their human-readable successor — the ordna task files, with the ## Progress journal that actually holds the reasoning — is Corpus B and is being mined for the first time. Deleting the JSON while adding the markdown is a strict information gain.
  • R4 + R5 (1,476 drawers). Authoring machinery and a rendered HTML duplicate.
  • The 269 ledgers/tasks/ + ledgers/backlog/ orphans inside R1. Same argument as R3: superseded item-by-item by tasks/id-N.md. Caveat below.

Re-home, do not delete — this is where I disagree with a blanket orphan purge:

  • The 98 themes/ orphans (6,583 drawers). These are not decayed; they were deleted by commit fa71b6bf when the roadmap ledger was repurposed toward initiatives. They are the only surviving record of the pre-initiative framing, and under DR-010 they are precisely “what was argued”. They are also, notably, the reason initiatives/ looks the way it does. Recommendation: retain the drawers, and re-home them by re-mining from git (see §4) so they carry an explicit superseded marker. Deleting them destroys lineage the register does not hold.
  • The 70 orphaned continuation-prompts (subset of R1). The owner explicitly values these for lineage. They are transitory by design but their sequence is the lineage. Retain.

The caveat I want the owner to rule on (see §9, Q1): R1 as delivered is the whole 481-path orphan set, including themes and continuation-prompts. If my re-homing recommendation is accepted, R1 should be split before execution into R1a-supersessions (the 269 ledger tasks/backlog files — delete) and R1b-lineage (themes + continuation-prompts + the rest — retain and re-mine from git). I have deliberately not pre-split it, because “delete the lineage” vs “preserve the lineage” is an owner decision under DR-010, not mine. Executing removal-UNION.txt as-is deletes the lineage.

Worth stating because the brief anticipated one: there is no duplicate-content problem within the docs-site corpus. Q4 proved one-wing-per-file. The palace-wide duplicate head documented in runbooks/mempalace-repair.md §10.3 (89,053 redundant copies) is agent-brief and dispatch- template boilerplate on the convos path, out of scope here.


Four tiers. The tier determines mine/don’t-mine, room, and cadence.

  • T1 Authoritative — kept current, re-mined on change. Citable as authority.
  • T2 Lineage — one-shot, point-in-time. Valuable as “what was argued”; never citable as current state.
  • T3 Stale-marked — mined, but explicitly marked so recall can down-rank it (§4).
  • T4 Excluded — never mined.
family / subtreetiermineroomcadencejustification
reference/decisions/** (114 files)T1YES — currently 0decisionskept currentDR-010 “what was decided” leg. Owner: reference must be “the most accurate record”. F3: the biggest gap in the palace.
reference/*.md, reference/change-log/T1yesdecisionskept currentSame owner ranking. Routing note below.
runbooks/T1yesrunbookskept currentOwner: “most accurate record of something”. Operational truth.
specs/**/*.md (id ≥ 130)T1yesspecskept currentOwner: high-value, should be kept up to date.
specs/**/*.md (id < 130)T3yesarchiveone-shotDR-106: specs below ~130 not citable unless re-affirmed.
ontology/T1yesreferencekept currentUnnamed by owner. Schema/taxonomy definitions — behaves like reference, small (452 drawers), low risk.
initiatives/ (except below)T1yesinitiativeskept currentOwner: intended home for initiative substrate.
initiatives/core-product/canonical-pipeline/intended-architecture/** except 01-vision.mdT3yesarchiveone-shotDR-106 names it stale explicitly.
initiatives/**/phase-0-investigation/**T3yesarchiveone-shotDR-106: the whole OQ-resolution chain.
ledgers/initiatives/ (12 files)T1yesinitiativeskept currentDR-101 canonical store for task placement (Linked tasks:). Load-bearing for Corpus B cross-links.
ledgers/retros/ (127 files)T1yesretroskept currentS### retro records — the session leg of the cross-link web.
ledgers/archive/ (105 files)T2yesarchiveone-shotOwner: should move to co-locate with ordna tasks. See §9 Q2 — the move is a docs-site refactor, not a mining decision; mine in place now, re-point after.
continuation-prompts/T2yeslineageone-shot per file, append-onlyOwner: transitory but show lineage. Never authority.
reports/T2yes — currently 6 of 37lineageone-shotOwner: point-in-time, “incredibly valuable context”. 84% missing today.
handover-guides/ (2 files)T2yeslineageone-shotUnnamed by owner. Tiny, handoff-shaped — same class as continuation-prompts.
design/ (6 files)T1yesreferencekept currentUnnamed by owner. Warm Meridian design system is live and cited from CLAUDE.md.
product-functionality/T3yesarchiveone-shotOwner: “almost all current docs outdated”. Mined-but-marked, not deleted — under DR-010 the arguments retain value.
themes/ (98, orphaned)T3yes — re-mine from gitarchiveone-shot, frozenPredecessor of initiatives/. Lineage under DR-010. See §4.2.
workflow-evaluation/*.md (6 files: friction-register.md, s-number-crosswalk.md, …)T2yeslineageone-shotProse in a telemetry tree. The existing yaml comment already says “prose docs in the same tree stay mined” — preserve that.
workflow-evaluation/sessions/**, reports/** (non-.md)T4NOF1. Already gitignored as non-canonical (S396).
**/*.json, **/*.jsonl, **/*.yaml anywhere under src/content/docs/T4NOR2+R3+R4. Machine serialisations; the prose successor is mined instead.
**/*.htmlT4NOR5.
subtreetiermineroomcadencejustification
tasks/id-N.md where status ∈ {backlog, todo, doing}T1yestaskskept currentLive work.
tasks/id-N.md where status = doneT1yestaskskept currentThe ## Progress journal is the richest “what was argued” surface in the repo. Done ≠ stale.
tasks/id-N.md where status = archived (139 files)T2yestasksone-shotRetain — DR-010. Distinguished at recall time by the status: line inside the drawer text, not by room.
tasks/AGENTS.mdT1yestaskskept currentConventions home.
tasks/ID-CROSSWALK.mdT1yestaskskept currentThe bl-*id-N map — required to resolve historical references.

Sizing. 376 task files, 2,166,668 bytes total (median 2,041 B, p90 12,930 B, max 87,576 B). At the observed 679 chars/drawer this is ≈3,190 drawers — 2.9% of the current docs-site footprint, for the corpus that ties everything together. Cheap.

One project, one config, both corpora — tasks/ sits at the docs-site repo root, so a single root-scoped mine covers both. This replaces the ad-hoc per-subdirectory --wing mines that produced the incoherent wings in F5.

wing: wing_canonical
# Rooms route by PATH, outermost segment first, first match wins
# (miner.py:552 detect_room). Keywords must never include 'src',
# 'content' or 'docs' — those segments precede every family and would
# capture the entire corpus into one room.
rooms:
- name: decisions
description: Decision Register, individual DR records, change-log
keywords: [reference, decisions, change-log]
- name: runbooks
description: Operational runbooks
keywords: [runbooks]
- name: specs
description: Task spec chain (RESEARCH/PRODUCT/TECH)
keywords: [specs]
- name: tasks
description: ordna task ledger
keywords: [tasks]
- name: initiatives
description: Initiative substrate and initiative ledger records
keywords: [initiatives]
- name: retros
description: Session retro records
keywords: [retros]
- name: reference
description: Ontology and design-system reference
keywords: [ontology, design]
- name: lineage
description: Point-in-time handoffs, continuation prompts, reports
keywords:
[continuation-prompts, reports, handover-guides, workflow-evaluation]
- name: archive
description: Superseded — retained for lineage, never citable as current
keywords:
[
archive,
_archive,
themes,
product-functionality,
intended-architecture,
phase-0-investigation,
]
exclude_patterns:
# F1 — machine telemetry (belt-and-braces; .gitignore also covers sessions/)
- src/content/docs/workflow-evaluation/**
- "!src/content/docs/workflow-evaluation/*.md"
# R3/R4/R5 — machine serialisations everywhere in the docs tree
- src/content/docs/**/*.json
- src/content/docs/**/*.jsonl
- src/content/docs/**/*.yaml
- src/content/docs/**/*.html
# keep the mine to the two corpora
- src/**
- "!src/content/docs/**"
- public/**
- node_modules/**
- .astro/**

Three routing constraints I verified and had to design around, stated plainly:

  1. reference/decisions/ cannot get its own room while reference/ also has one. Path parts are walked outermost-first, so reference (depth 3) is always tested before decisions (depth 4). I therefore named the room decisions and gave it the reference keyword, folding the whole reference tree into it. Rationale: under DR-010 the register is the reference surface’s reason for existing, and 114 of 144 files in that tree are DRs. The DR-vs-prose distinction remains recoverable from source_file.
  2. specs id ≥ 130 vs id < 130 cannot be split by directory keyword — both are specs/id-N-…. Splitting requires enumerating the ~60 stale spec dirs as archive keywords. Each dir name (id-61-bid-rename) matches its path part exactly, so it works, but the block is long and needs regenerating as tasks age. I have not inlined it; §7 step 4 generates it. This is the one place the design needs maintenance, and §9 Q3 asks whether it is worth it.
  3. ledgers/ deliberately has no room keyword. Its three children (initiatives, retros, archive) each match a room at depth 4 — but only because ledgers at depth 3 matches nothing. Adding a ledgers keyword anywhere would capture all three. Fragile but correct; flagged in §7’s dry-run checkpoint.

Under DR-010, stale ≠ don’t-mine: “what was argued” is exactly what the palace uniquely holds, and DR-106’s stale families are dense with argument. The requirement is that recall can tell.

4.1 Mechanism: the archive room is the marker

Section titled “4.1 Mechanism: the archive room is the marker”

The only metadata dimension that is (a) settable at mine time with no code change, (b) available as a recall filter, and (c) currently entirely wasted, is room. Every one of the 107,938 docs-site drawers is room = general today, so the room dimension carries zero information and is free to repurpose.

Concretely: room = archive is the stale marker. DR-106’s named families (intended-architecture/** minus 01-vision.md, phase-0-investigation/**, sub-130 specs), plus product-functionality/ and themes/, route there by path keyword. Recall then has a one-token discriminator: room != archive for authority questions, room = archive when deliberately asking what was argued before.

This is preferable to the alternatives:

  • A separate wing (wing_stale) would work, but wings are the coarse project axis and the palace already suffers from wing sprawl (§8). Adding one compounds F5.
  • A metadata key (stale: true) requires a miner code change — _build_drawer_metadata (miner.py:1327) writes a fixed key set with no passthrough for custom fields.
  • Content prefixing would pollute the embedded text and shift similarity.

Known limitation, stated: 01-vision.md is DR-106’s explicit exception inside an otherwise stale tree, and path-keyword routing cannot exempt a single file — intended-architecture matches at directory depth before the filename is ever considered. 01-vision.md will land in archive with its siblings. Options: accept it (it is one file, and it is findable, just under-ranked); or move it up one directory in the docs-site, which is a content change outside this design’s remit. Flagged in §9 Q4.

themes/ is gone from disk, so it cannot be re-mined in place, and the brief correctly forbids staging directories (the miner skips symlinks, and hardlinks record the staging path as source_file, recreating the orphan class).

The clean route: git worktree add the parent commit of fa71b6bf into a path, mine it, and remove the worktree. That records real file paths at a real checkout — but they are the worktree’s paths, which is the same orphan problem one step removed.

I therefore recommend the honest alternative: leave the existing 6,583 themes drawers in place and do not delete them, accepting that their source_file points at a path that no longer exists. An orphaned source_file is only a problem for re-mine dedup and for “open the source” affordances; it does not degrade recall. Re-mining themes to fix the path is not worth a worktree dance. What it does need is the stale marker — and since the drawers already exist with room = general, that requires a post-mine metadata write (§5.3), not a re-mine.

This is the one place where my recommendation cannot be delivered by mine-time config alone.


The failure mode to defeat, from S517: a 1 July proposal out-ranked the DR-023/DR-025 ruling that superseded it two days later, with nothing in the result marking the supersession.

5.1 What is settable at mine time — no code change

Section titled “5.1 What is settable at mine time — no code change”

source_mtime (float, unix epoch) is already written on 99% of projects-mode drawers and is the recency signal the brief assumed did not exist. It is real os.getmtime, spread over 46 distinct days, and it tracks on-disk edit time. For a corpus edited in place in a working checkout — which the docs-site is — it is a faithful “when was this last touched”.

content_date is also written (84% coverage) but must be treated with suspicion. Its resolution order is filename → frontmatter date/created/published → first ISO date in the first 10 body lines → mtime. Observed range on docs-site is 2011-09-10 to 2026-12-07 — a 14-year span including a date four months in the future, on a corpus that is at most two years old. Those are dates mentioned in text, not authorship. It is a useful tiebreaker and a bad sort key.

Two mine-time consequences worth acting on:

  • ordna task files will get content_date = mtime. _try_frontmatter_date reads only date, created, publishednot ordna’s created_at / updated_at. And _try_content_body_date scans only the first 10 lines after frontmatter, which for a task file is ## Goal prose. So both earlier rungs miss and it falls through to mtime. That is accidentally the right answer, and it means no ledger schema change is needed. I considered recommending a date: alias key in task frontmatter (ordna round-trips extra keys untouched per AGENTS.md §3) and recommend against it: it would make content_date equal updated_at, which is day-granularity and bumped by every CLI write — no better than mtime, at the cost of polluting the ledger schema.
  • content_date is stamped once per file (miner.py:1499) and applied identically to every chunk. So per-section or per-## Progress drawer granularity does not buy per-entry dates. This is a direct input to §6.

source_mtime answers when. It does not answer what replaced this. Nothing in the metadata schema expresses supersession, and _build_drawer_metadata has no custom-key passthrough, so a superseded_by field is a miner code change, not a config change.

The room = archive marker from §4 is the available approximation: it is coarse (family-level, not document-level) but it is free and it defeats the specific S517 failure for the DR-106 families, which is where the brief says the risk concentrates.

Two things, both writes to embedding_metadata after mining, not miner changes:

  1. Re-room the retained orphans (themes, and any other lineage kept under §2.1) from general to archive. They cannot be re-mined, so config cannot reach them.
  2. Optionally stamp superseded_by on specific drawers where a DR explicitly supersedes a named document. This is the only mechanism that gets document-level supersession, and it requires a curated input list — realistically the [RATIFIED-RETIRE] rows of 07-collapse-list.md, which the brief notes are usually sound.

I am not proposing (1) or (2) as part of the initial execution. Both are palace mutations beyond delete-and-re-mine, both need the vector layer healthy, and (2) needs owner input on the supersession list. They are follow-on work. §9 Q5.

None of this makes vector search rank the ratified ruling above the superseded proposal. Similarity scoring is unchanged. What it provides is a filter (room != archive) and a sort key (source_mtime) that a disciplined recall path can apply. Per runbooks/mempalace-repair.md §10.5 — “the recall path is tuned in the hook, not in the tool” — the place to apply them is the recall wrapper, not the miner. The mining design can only make the discriminators exist; it cannot make recall use them. That is a recall-grounding skill change and is out of scope here, but the design is worthless without it. §9 Q6.


Mine tasks/ as part of the same docs-site project, into wing_canonical / room = tasks, one drawer per ~800-char chunk of the whole file (i.e. the default chunker, no custom granularity). Do not build a knowledge-graph layer. Make the cross-links traversable by relying on the identifiers already present in the text.

The reasoning follows.

Same wing (wing_canonical), own room (tasks). Rationale: the task ledger is not a separate project — its entire value is being cross-referenced against specs, DRs and retros in the same repo. Putting it in a separate wing would repeat exactly the mistake of F5, where filtering to one wing silently hides related content. A room is the right granularity: it segments without isolating.

tasks/ sits at the docs-site repo root, so a root-scoped mine picks it up with no extra invocation, and the tasks keyword matches path part tasks at depth 1.

6.3 Granularity: whole file, default chunking

Section titled “6.3 Granularity: whole file, default chunking”

The brief asks whether to split per-section or per-## Progress entry. Whole file, default chunker. Three reasons, in order of weight:

  1. Per-entry drawers would not gain per-entry dates. content_date is computed once per file (§5.1). A ## Progress entry from May and one from July would carry the identical content_date and identical source_mtime. The main reason to split — chronological precision — does not materialise.
  2. The corpus is small enough that splitting solves no cost problem. 376 files, 2.17 MB, ≈3,190 drawers. Median file is 2,041 bytes — three drawers. Splitting a 3-drawer file by section produces sub-MIN_CHUNK_SIZE fragments that the chunker would merge back anyway.
  3. Dated ## Progress headers survive into the drawer text. The chronology is in the content, where a reader (human or model) can see it — which is more robust than metadata that recall may not filter on.

The p90 is 12,930 bytes (≈19 drawers) and the max 87,576 bytes (≈129 drawers). Even the largest task file is a rounding error against task-list.json’s 3,996. There is no volume argument for splitting.

Where I’d revisit this: if the owner wants recall to answer “what happened on task 163 in S499 specifically”, per-## Progress-entry drawers with the entry date pushed into content_date would help — but that needs the miner change from §5.2, so it is the same follow-on decision.

Section titled “6.4 Cross-link traversability — and why the KG tools are the wrong vehicle”

Assessment: the KG tools are not the right vehicle. I recommend against them. The brief asked me to judge from documented semantics plus the palace’s actual KG contents. Both point the same way.

Evidence 1 — the KG is empty. ~/.mempalace/knowledge_graph.sqlite3 holds 74 entities and 37 triples. Against a 402,267-drawer palace and a 376-task ledger, this is an unused feature, not a substrate. Nothing has been invested that would be lost by not using it.

Evidence 2 — the auto-extracted entities are path noise and cannot carry the links that matter. The mempalace_closets collection (4,611 closets) stores documents shaped <label>|<entities>|<date>:<line-range>|→<drawer ids>. Ranking every entity value across the palace:

rankentitycount
1Status22,086
2canonical20,309
3Supabase15,291
Users5,523
Documents5,401
Bash4,919

Users and Documents are segments of /Users/liamj/Documents/…. The top entity in the entire palace is the word Status. Now the decisive test — do the identifiers the owner actually wants to tie together appear?

identifier classdistinct captureduniverse
DR-NNN44114 DR files on disk
id-N148376 task files
S###14~500 sessions
fr-NNN0friction register

And the single most frequent DR-shaped entity is the literal placeholder string DR-NNN (189 occurrences) — more than any real DR (DR-081 at 26). The extractor is capturing template text. An auto-built graph over these entities cannot express task ↔ spec ↔ DR ↔ session ↔ retro.

Evidence 3 — the existing tunnels confirm it. ~/.mempalace/tunnels.json is 228 KB of auto-generated kind: entity tunnels linking wing_canonicalsessions on shared entity names. Samples: entity:AccordionItem, entity:AccordionTrigger — React component names. Every one carries access_count: 0. The mechanism works and produces nothing anyone has used.

Evidence 4 — the links are already deterministic, so a probabilistic graph is the wrong tool. This is the real argument. The cross-links the owner wants are not latent facts needing extraction; they are declared in frontmatter and enforced by a documented convention:

tiewhere it is already declared
task → initiativeinitiative: frontmatter (170/376 files), resolving to ledgers/initiatives/<N>.md via kebab(title) — DR-101
initiative → tasksLinked tasks: line in the initiative record — DR-101 requires both halves
task → sessionsession_refs: [S491, S492] (240/376)
task → docscross_doc_links: (108/376)
task → gitcommit_refs: (52/376)
task → taskdepends_on: (89/376)
task → spec dirnaming convention specs/id-N-<slug>/ — 100 of 124 spec dirs match id-*
session → retroledgers/retros/S###.md naming convention

These are exact string keys. A vector-similarity graph is strictly worse than grep at following them.

What I recommend instead: mine the task files whole (§6.3), so every one of those frontmatter keys lands inside drawer text and is both semantically searchable and lexically exact-matchable via embedding_fulltext_search. Traversal is then a deterministic two-step available to any agent, with no palace feature involved:

  1. Recall on the task room to find the relevant id-N.
  2. Resolve its declared links by direct file read — tasks/id-N.md frontmatter → specs/id-N-<slug>/, ledgers/initiatives/<N>.md, ledgers/retros/S###.md, git show <sha>.

This is the DR-010 three-way routing working as designed: the palace answers what was argued, the frontmatter and git answer when and where, the register answers what was decided. The KG would be a fourth, lossier copy of ties that are already exact.

Caveat on my confidence: I could not call kg_add / kg_query / create_tunnel / follow_tunnels, so this judgement rests on stored state and documented semantics, not on observed behaviour. If the owner has seen kg_query do something useful that these 37 triples do not reflect, that would change the assessment.


Precondition: the vector layer is deadlocked and must be repaired first. Per runbooks/mempalace-repair.md §12.1 the cause is hnsw:sync_threshold: 2, written by repair --mode from-sqlite, which sets divergence tolerance to 2 × threshold = 4; the fix is pinning it to 1000. Nothing below runs until a mempalace status returns OK.

Two operational rules from the runbook govern every mutating step:

  • §10.3a — bulk writes cannot get a clean window on a live machine. mempal-stop-hook.sh spawns a palace writer at the end of every turn of every live Claude session, including the one running the repair. A process sitting at 0% CPU is blocked on mine_palace_lock, not working. Quiesce all sessions first.
  • §12.3 — route bulk deletes through a warm MCP server, never a cold script. A cold client deadlocks in chromadb_rust_bindings before opening any palace file. This is the same pathology that blocked this design session.
#StepCheckpoint / rollback
0Repair the vector layer (runbook §12.1): rollback artefact first, quiesce per §12.2, pin hnsw:sync_threshold: 1000, restore, verify.mempalace status OK at unchanged divergence. Stop if not.
1.backup snapshot of chroma.sqlite3.Snapshot exists and is non-zero. Restore point for everything below.
2Re-run the §0.1 census from lock-free sqlite; regenerate removal-*.txt against current state.Counts match this document (107,938 / 833 / 63,153) or the delta is explained. The lists here are a 2026-07-31 snapshot.
3Owner decision gate — split R1 per §2.1 (delete supersessions vs retain lineage).Explicit owner sign-off on which of themes / continuation-prompts are deleted. Blocking: this is irreversible.
4Generate the sub-130 stale-spec keyword block for the archive room (§3.3 constraint 2).Enumerated dir list reviewed. Or skip per §9 Q3.
5Land the new mempalace.yaml (§3.3).Config only, no palace effect. Reversible by git.
6Dry-run the mine (no writes) and dump the projected file→room mapping.The critical checkpoint. Verify: tasks/ present; reference/decisions/** present; DR-106 families → archive; ledgers’ three children → correct rooms (§3.3 constraint 3); zero .json/.jsonl/.yaml/.html; src/ code absent. Iterate on the yaml until clean — no palace state has changed yet.
7Pilot delete: 10 paths from removal-R2 via warm MCP.sqlite row counts drop to 0 for exactly those paths. Per §11 the metadata segment applies deletes even when the job reports failure — verify sqlite, not the job’s exit status.
8Full delete wave, class by class, R2 → R3 → R4 → R5 → R1(as gated at step 3).Per-class sqlite verification against the §2 manifest. Existing tooling: ~/.mempalace/tools/prune-stage.py.
9repair --mode from-sqlite in a solo window, then runbook §6 verify.Re-pin sync_threshold: 1000 afterwards — the rebuild writes 2 (§12.1). This is the step that most recently broke the palace; do not skip the re-pin.
10Mine the docs-site root (both corpora, one pass).Expect ≈44,785 surviving + ≈3,190 tasks + the 310-file backfill. Verify room distribution is no longer 100% general.
11Post-mine verification census.reference/decisions = 114 files mined; tasks room ≈3,190 drawers; archive room populated; no family still 100% general.
12(Deferred) Re-room retained orphans to archive (§5.3).Separate window, separate decision.

Ordering rationale: delete before re-mine, because re-mining first would leave both the old and new drawers present with no way to tell them apart (filed_at would differ, but §2 shows past mines already span three dates). The repair at step 9 sits between delete and mine because it is what makes the vector-side deletion real (§11) — mining into a store with pending un-applied delete logs is how the poisoned WAL happened.


Flagging, not solving — but it is smaller than the brief’s estimate suggests.

158 distinct wings exist. The junk tail is 147 wings holding 722 drawers between them — 0.18% of the palace. Named examples: wing_dataflow (38), wing_subo_backlog (35), wing_audit (34), wing_subo_147_waves (28), wing_id104 (24), wing_off (19), wing_35 (11). The pattern is clear: ad-hoc --wing values from sub-agent dispatches, mostly wing_subo_*.

Recommendation, in priority order:

  1. Fix the cause first. These come from --wing overrides on ad-hoc mines. The §3.3 config removes the need for overrides on this repo by making one root mine correct. Any remaining dispatch that passes --wing should be found and stopped, or the tail regrows.
  2. The high-value merge is not the tail — it is F5. Folding specs (14,272), handoffs (829), runbooks (605) and reference (359) back into wing_canonical fixes a correctness problem: today, recall filtered to wing_canonical misses 356 docs-site files, and recall filtered to wing=specs misses 313 of 572 mined specs. This happens automatically as a consequence of the §7 re-mine — those files are deleted or re-mined into wing_canonical with the config wing. No separate merge step needed.
  3. The 722-drawer tail is genuinely low priority. It is small enough to leave. The runbook already documents a “wing-name normalisation sweep” (§5) if the owner wants it swept; it is not worth a dedicated window on its own and should ride along with a future repair.

Q1 — Delete or retain the orphaned lineage? (blocking, §2.1, §7 step 3) removal-UNION.txt as delivered deletes all 481 orphans, including 98 themes/ files (6,583 drawers) and 70 continuation-prompts. I recommend retaining both under DR-010 — themes is the predecessor of initiatives/, deleted by one deliberate commit, and is the only surviving record of that framing. But 290 orphaned ledgers/ files that ordna superseded should go. I have not pre-split the list because “delete the lineage” is your call, not mine. Executing the union as-is destroys the themes lineage.

Q2 — ledgers/archive co-location (§3.1). You said ledgers/archive should move to co-locate with the ordna tasks. That is a docs-site content refactor, not a mining decision, and doing it after mining creates 105 fresh orphans. Do you want the move done first, or should I mine in place and accept re-pointing later?

Q3 — Is the sub-130 spec split worth its maintenance cost? (§3.3 constraint 2) Routing stale specs to archive requires enumerating ~60 spec directories as room keywords, and regenerating that block as tasks age. The alternative is one specs room where DR-106’s “below ~130 unless re-affirmed” rule stays a matter of reader discipline. I lean toward skipping it — the rule has an “unless re-affirmed” clause that no static config can evaluate anyway — but it is your stale-content risk.

Q4 — 01-vision.md (§4.1). DR-106 exempts it from an otherwise-stale tree, but path-keyword routing cannot exempt one file: it will land in archive with its siblings. Accept, or move it up a directory in the docs-site?

Q5 — Post-mine metadata writes (§5.3). Re-rooming retained orphans to archive, and stamping document-level superseded_by from 07-collapse-list.md’s [RATIFIED-RETIRE] rows, are both palace mutations beyond delete-and-re-mine. I have deliberately excluded them from §7. Do you want them scoped as follow-on work?

Q6 — Who applies the discriminators? (§5.4) This design makes room != archive and source_mtime exist. It cannot make recall use them. Per runbook §10.5 the recall path is tuned in the hook, not the tool. Without a matching recall-grounding / hook change, the supersession mechanism has no effect on what you actually see. Is that in scope for the same initiative?

Q7 — Confirm the KG verdict (§6.4). I recommend against the KG tools based on 74 entities / 37 triples, entity extraction whose top result is the word Status and whose most common DR is the literal string DR-NNN, and 228 KB of tunnels with access_count: 0. I could not call kg_query to test it live. If you have seen it work, say so — it is the one conclusion here I could not verify by execution.

Q8 — reports/ and the 310-file backfill (§1.2 F3). 120 unmined reference files (114 of them DRs), 31 of 37 reports/, 110 specs. I have tiered all of them as mine-me. Is any of that deliberately withheld — anything with client IP or otherwise not wanted in the palace?


All in /private/tmp/claude-501/-Users-liamj-Documents-development-canonical/46f450b2-86bd-4602-b2da-8d0097252e2a/scratchpad/:

filecontents
mine-design-docs-site-and-ordna.mdthis document
removal-R1-orphans.txt481 paths — gone from disk
removal-R2-workflow-eval-telemetry.txt259 paths — machine telemetry
removal-R3-legacy-json-ledgers.txt5 paths — pre-ordna JSON ledgers
removal-R4-spec-sidecars.txt93 paths — spec authoring machinery
removal-R5-stray-binary.txt1 path — stray HTML
removal-UNION.txt833 de-duplicated paths, 63,153 drawers
gap-unmined-md.txt310 on-disk markdown files never mined
ondisk_md.txt, mined_md.txtinputs to the gap comparison

Pre-existing from the salvaged session: mined_paths.txt, orphan_paths.txt, prune-targets.txt, prune-backup-s355-stage.jsonl.


10. S518 deltas — owner rulings and corrected preconditions

Section titled “10. S518 deltas — owner rulings and corrected preconditions”

Added by the main session after the design was written. Two owner rulings and three corrections. Where this section conflicts with anything above, this section wins.

10.1 Owner ruling — workflow-evaluation/ is pruned entirely, not tiered

Section titled “10.1 Owner ruling — workflow-evaluation/ is pruned entirely, not tiered”

Owner, S518: “where we’ve changed our dev-workflow and no longer use cmux — the previous content can now be pruned, with no current requirement to mine until we update our docs-site workflow evaluator agent, based on the new dev workflow.”

This supersedes §3.1’s T2 row for workflow-evaluation/*.md. The 6 markdown files (208 drawers) join the telemetry in the removal set rather than being mined as lineage.

  • New list: removal-R6-workflow-eval-all.txt265 paths, 43,887 drawers (the whole family, not just the non-.md telemetry).
  • New union: removal-UNION-v2.txt839 paths (was 832).
  • §3.3 yaml changes: drop the !src/content/docs/workflow-evaluation/*.md negation and exclude the tree outright. Drop workflow-evaluation from the lineage room keywords.
  • Re-mining is gated on the workflow-evaluator agent being rebuilt for the new dev workflow — not on this design.

One narrow exception flagged for owner confirmation, deliberately NOT actioned: workflow-evaluation/s-number-crosswalk.md is an S-number crosswalk, and §6.4 names S### as one of the six cross-link legs that make the ordna structure traversable. friction-register.md is likewise the home of the fr-NNN identifier class. Both are prose reference material that happens to sit in a telemetry tree. Pruning them is consistent with the ruling as stated; re-homing them (e.g. to reference/) before the prune would preserve two load-bearing crosswalks. Owner’s call — the default here is prune, per the ruling.

10.2 Owner ruling — themes/initiatives/ was a rename

Section titled “10.2 Owner ruling — themes/ → initiatives/ was a rename”

Owner, S518: themes/ was renamed to initiatives/.” This confirms §1.2 F4, which reached the same conclusion independently from commit fa71b6bf.

It does not settle §9 Q1. If the rename carried the content forward, the 98 orphaned themes/ files are a duplicate of initiatives/ under an old name and the DR-010 lineage argument for retaining them weakens considerably. If it was a restructure that dropped material, the argument stands. Q1 remains open, with that framing added.

10.3 Correction — §7 step 0’s stated precondition is wrong

Section titled “10.3 Correction — §7 step 0’s stated precondition is wrong”

§7 step 0 says the vector layer is fixed by pinning hnsw:sync_threshold to 1000, citing runbook §12.1. That is no longer the operative diagnosis, and S518 disproved it directly: the threshold was reverted 1000 → 2 and back, and the deadlock was unchanged either way. The pin is still required (DR-110) but it is not the fix for the current fault.

The current fault is a deadlock — every operation that opens the chroma collection hangs in chromadb_rust_bindings (_pthread_cond_wait/__psynch_cvwait), reproduced six ways: warm MCP search, warm MCP delete dry-run, cold client with the daemon up, cold client with zero palace holders, cold client with device=cpu, cold client with sync_threshold=2.

Step 0 is therefore: run ~/repair-palace-s518.sh — a from-sqlite rebuild with the DR-110 re-pin built in as a verified step, plus smoke tests on both broken paths. It is not a confirmed fix for this failure mode (S506 proved the rebuild against the segfault pathology, not this one); if its smoke tests fail, pgvector migration is the standing escalation per id-299’s S452 ruling.

10.4 Correction — §7’s two operational rules are both stale

Section titled “10.4 Correction — §7’s two operational rules are both stale”
  • “§12.3 — route bulk deletes through a warm MCP server, never a cold script.” S518 disproved this: the main session’s warm MCP server deadlocked identically to a cold one. Warm-vs-cold is not the variable. Until the repair lands and its smoke tests pass, no route works and no delete step below can be attempted.
  • §7 step 8 cites ~/.mempalace/tools/prune-stage.py as the tooling. That script does from mempalace.mcp_server import tool_delete_by_source in a fresh process — it is the cold-client shape §12.3 warns against. Do not use it as written.

10.5 Correction — runbook §12.2’s stale-lock guard has never worked

Section titled “10.5 Correction — runbook §12.2’s stale-lock guard has never worked”

The lock file ~/.mempalace/locks/mine_palace_<hash>.lock begins with a NUL byte, so §12.2’s P=$(awk '{print $1}' "$L") always returns empty, ps -p "" always fails, and the lock is cleared unconditionally — including when a live writer holds it. The guard has never fired. Correct parse: tr -d '\0' < "$L" | awk '{print $1}', refusing on an empty result. Fixed in ~/repair-palace-s518.sh; the runbook still needs patching.


11. Ratified — S518 owner decisions (board s518-mine-design-oq-board)

Section titled “11. Ratified — S518 owner decisions (board s518-mine-design-oq-board)”

Eight of nine ratified by the owner. These are binding; where they conflict with §3–§9, these win.

QRulingvs recommendation
Q1Delete all 481 orphans, including themes/overridden (rec: split)
Q2Do the ledgers/archive move as a docs-site refactor before the mineas recommended
Q3Skip the sub-130 spec split — one specs room; DR-106 stays reader disciplineas recommended
Q4Move 01-vision.md up a directory so path routing keeps it out of archiveas recommended
Q5Scope both post-mine metadata writes as follow-on after the mine landsas recommended
Q6In scope for id-383 — update recall-grounding + the recall hook in the same initiativeas recommended
Q7NOT RATIFIED — see §11.2reframed by owner
Q8Nothing withheld — mine all 310 backfill filesas recommended
Q9Prune the entire workflow-evaluation/ tree, both crosswalks includedoverridden (rec: re-home)

11.1 Reversibility corrections to the two overrides

Section titled “11.1 Reversibility corrections to the two overrides”

Both overrides are cheaper than the board implied. Verified S518:

  • Q1 / themes/ — the board card said “no on-disk copy exists”. Literally true, but it understated recoverability: git ls-tree fa71b6bf^ -- src/content/docs/themes/ returns all 105 files. The lineage is fully recoverable from git and can be re-mined from a worktree at that ref if it is ever wanted. §4.2 already assumed exactly that mechanism. Deleting is reversible; the correction does not change the ruling, and it removes the stated risk.
  • Q9 / crosswalksfriction-register.md (31 KB) and s-number-crosswalk.md (6 KB) are still on disk; workflow-evaluation has zero orphans. Pruning removes them from the palace only. Re-mining restores them when the evaluator agent is rebuilt for the new dev workflow.

Net effect on the removal set: removal-UNION-v2.txt is executed as delivered, unsplit — 839 paths. Q1 requires no sub-setting of R1, and Q9 is already folded in as R6.

11.2 Q7 — the KG verdict is NOT ratified; owner reframed the question

Section titled “11.2 Q7 — the KG verdict is NOT ratified; owner reframed the question”

Owner, S518: “The intent is to be able to use the KG, stripping back redundancy and duplication, to reveal what’s valuable.”

This inverts §6.4’s framing. §6.4 read the KG’s current state (74 entities / 37 triples, top entity Status, path segments as entities) as evidence the feature is unusable. The owner’s position is that those symptoms are downstream of the palace’s redundancy, and that the prune is the precondition for evaluating the KG, not a reason to abandon it.

That is a materially better read than §6.4’s, and the ordering favours it: every input to §6.4’s verdict changes under the ratified plan.

§6.4 evidencewhat the ratified plan does to it
Top entities are Status, Users, Documents, Bash — path/telemetry noiseQ9 removes 43,887 telemetry drawers; R3/R4 remove machine serialisations. The dominant noise source is deleted.
Only 44 of 114 DR-NNN capturedQ8 mines all 114 DR files — currently 0 are mined. The register cannot be in the graph because it was never in the palace.
Only 14 of ~500 S### capturedRetros + session_refs frontmatter enter via the tasks corpus (≈3,190 drawers, currently 0).
id-N: 148 of 376All 376 task files mined for the first time.

Revised recommendation: do not rule on the KG now. Re-run the §6.4 entity census after step 11 of the execution sequence and rule on the post-prune, post-backfill numbers.

Two caveats that the prune does not fix, and which must be checked in that re-census:

  1. The extractor captures template text. The most frequent DR-shaped entity is the literal placeholder string DR-NNN (189×) — it out-ranks every real DR. Pruning duplication does not stop the extractor matching placeholders in doc templates; mining 114 real DR files should swamp it, but verify rather than assume.
  2. access_count: 0 across 228 KB of tunnels. Nothing has ever read them. Even a clean graph is inert without a consumer — which is the same dependency as Q6. The KG question and the recall-path question resolve together, or neither does.

Action: §6.4’s “recommend against” is withdrawn and replaced by a deferred re-evaluation gate at step 11. Q7 stays open.