Conversation
Co-authored-by: devlux76 <86517969+devlux76@users.noreply.github.com>
…d references from neighbor graph code Closes #92 Co-authored-by: devlux76 <86517969+devlux76@users.noreply.github.com>
ingestText() now calls buildHierarchy() after page creation to produce the full Books→Volumes→Shelves hierarchy on every ingest call. IngestResult extended with books[], volumes[], shelves[] fields. Closes #90 Co-authored-by: devlux76 <86517969+devlux76@users.noreply.github.com>
…peline Query.ts now routes through Shelf→Volume→Book→Page hierarchy using rankShelves/rankVolumes/rankBooks before flat page scoring. Combines hierarchy-discovered pages with hotpath pages for comprehensive results. Closes #91 Co-authored-by: devlux76 <86517969+devlux76@users.noreply.github.com>
- FullNeighborRecalc now derives maxNeighbors from computeNeighborMaxDegree instead of using a fixed default of 16 - Query.ts uses getResidentCount() as proxy for corpus size to avoid scanning all pages on the hot path for computeSubgraphBounds Closes #93 Co-authored-by: devlux76 <86517969+devlux76@users.noreply.github.com>
…ms-derived max degree HebbianUpdater.decayAndPrune now derives maxDegree from computeNeighborMaxDegree(graphMass) instead of a fixed 16, matching the design requirement for Williams-bound-driven edge pruning. Closes #95 Co-authored-by: devlux76 <86517969+devlux76@users.noreply.github.com>
Check off all implemented and tested items across Hippocampus (HierarchyBuilder, FastNeighborInsert, Ingest), Cortex (Ranking, MetroidBuilder, KnowledgeGapDetector, OpenTSPSolver, Query, QueryResult), Daydreamer (ExperienceReplay, ClusterStability), integration tests, benchmarks, CI hardening, documentation, and product surfaces. Update section headings with ✅ COMPLETE markers and refresh the summary table to reflect v0.1/v0.5/v1.0 phases as complete. Only P3-A (WebGL provider) and P3-H (GitHub sync smoke test) remain pending. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…implementation Update PLAN.md to reflect full completion of Hippocampus (100%), Cortex (100%), and all documentation sections. Update TODO.md checkboxes for all completed P1/P2/P3 items. Fix HierarchyBuilder test to expect volumes/shelves from ingestText. Closes #94 Co-authored-by: devlux76 <86517969+devlux76@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Aligns implementation with the design across ingestion, querying, consolidation budgets, and semantic-neighbor naming by integrating hierarchy construction into ingest, adding hierarchical routing to queries, and removing remaining “metroid neighbor” terminology.
Changes:
- Update ingest to build full Pages→Books→Volumes→Shelves hierarchy and return it in
IngestResult. - Add Shelf→Volume→Book→Page routing in
query()and avoid full-corpus scans for subgraph bounds by using resident count. - Replace hardcoded neighbor degree defaults with Williams-derived
computeNeighborMaxDegree(...), and clean up semantic-neighbor naming in code/tests + sync plan/todo docs.
Reviewed changes
Copilot reviewed 19 out of 20 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/sharing/SubgraphExchange.test.ts | Renames mock neighbor map from metroidNeighbors to semanticNeighbors. |
| tests/integration/IngestQuery.test.ts | Updates integration assertions for multi-book ingest + persisted volumes/shelves. |
| tests/hippocampus/Ingest.test.ts | Adjusts ingest unit test expectations for hierarchy output. |
| tests/hippocampus/HierarchyBuilder.test.ts | Updates hierarchy-builder ingest expectations to include volumes/shelves and multi-book coverage. |
| tests/daydreamer/PrototypeRecomputer.test.ts | Renames mock neighbor map to semanticNeighbors. |
| tests/daydreamer/HebbianUpdater.test.ts | Renames mock neighbor map to semanticNeighbors. |
| tests/daydreamer/FullNeighborRecalc.test.ts | Renames mock neighbor map + sync accessor naming. |
| tests/daydreamer/ClusterStability.test.ts | Updates comments/stubs to semantic-neighbor naming. |
| tests/cortex/Query.test.ts | Adds coverage ensuring query works with hierarchy-based routing present. |
| tests/SalienceEngine.test.ts | Removes obsolete Metroid-neighbor stub methods from mock store. |
| package-lock.json | Locks added dev deps (@vitest/coverage-v8, husky) already reflected in package.json. |
| lib/storage/IndexedDbMetadataStore.ts | Updates v3 store comment to semantic-neighbor wording. |
| lib/sharing/SubgraphExporter.ts | Renames local variables/comments to “semantic neighbors”. |
| lib/hippocampus/Ingest.ts | Changes ingest to insert semantic neighbors + build hierarchy; expands IngestResult. |
| lib/daydreamer/HebbianUpdater.ts | Derives maxDegree from Williams bounds when not provided. |
| lib/daydreamer/FullNeighborRecalc.ts | Derives maxNeighbors from Williams bounds when not provided. |
| lib/daydreamer/ClusterStability.ts | Renames documentation/comments to semantic-neighbor terminology. |
| lib/cortex/Query.ts | Implements hierarchical routing and uses resident count for Williams-derived subgraph bounds. |
| TODO.md | Updates completion status and “Last Updated” date. |
| PLAN.md | Updates plan status text to reflect completed modules and corrected terminology. |
lib/hippocampus/Ingest.ts
Outdated
| @@ -149,8 +102,20 @@ export async function ingestText( | |||
| metadataStore, | |||
| }); | |||
|
|
|||
| // Run hotpath promotion for the newly ingested pages and book. | |||
| await runPromotionSweep([...pageIds, bookId], metadataStore); | |||
| // Build the full hierarchy: Pages → Books → Volumes → Shelves. | |||
| // buildHierarchy handles medoid selection, adjacency edges, prototype | |||
| // computation, Williams fanout enforcement, and promotion sweeps. | |||
| const hierarchy = await buildHierarchy(pageIds, { | |||
| modelProfile, | |||
| vectorStore, | |||
| metadataStore, | |||
| }); | |||
There was a problem hiding this comment.
@copilot use your best judgement to resolve the issue in accordance with the DESIGN to the best of your abilities even if that means the DESIGN will need to be updated as well. Try to solve the problem while minimizing downstream and knock-on effects.
There was a problem hiding this comment.
Fixed in 4335f67. Swapped the ordering so buildHierarchy() runs before insertSemanticNeighbors() — this ensures books/volumes (and their reverse indexes) exist when the neighbor inserter traverses page→book→volume to flag dirty volumes via flagVolumeForNeighborRecalc(). Also addressed the other review items in the same commit: renamed the misleading hierarchyPageIds in Query.ts to per-tier variables (volumeIdsFromShelves, bookIdsFromVolumes, pageIdsFromBooks) and strengthened the integration test to enforce exactly-once book membership.
lib/cortex/Query.ts
Outdated
| if (hotpathShelfEntries.length > 0) { | ||
| const topShelves = await rankShelves( | ||
| queryEmbedding, | ||
| hotpathShelfEntries.map((e) => e.entityId), | ||
| Math.max(2, Math.ceil(hotpathShelfEntries.length / 2)), | ||
| rankingOptions, | ||
| ); | ||
| for (const s of topShelves) { | ||
| const shelf = await metadataStore.getShelf(s.id); | ||
| if (shelf) { | ||
| for (const vid of shelf.volumeIds) hierarchyPageIds.add(vid); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // Rank volumes — include both hotpath volumes and those found via shelf drill-down | ||
| const volumeCandidateIds = new Set<Hash>([ | ||
| ...hotpathVolumeEntries.map((e) => e.entityId), | ||
| ...hierarchyPageIds, | ||
| ]); | ||
| hierarchyPageIds.clear(); | ||
|
|
||
| if (volumeCandidateIds.size > 0) { | ||
| const topVolumes = await rankVolumes( | ||
| queryEmbedding, | ||
| [...volumeCandidateIds], | ||
| Math.max(2, Math.ceil(volumeCandidateIds.size / 2)), | ||
| rankingOptions, | ||
| ); | ||
| for (const v of topVolumes) { | ||
| const volume = await metadataStore.getVolume(v.id); | ||
| if (volume) { | ||
| for (const bid of volume.bookIds) hierarchyPageIds.add(bid); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| const hotResults = await rankPages(queryEmbedding, hotpathIds, topK, rankingOptions); | ||
| // Rank books — include both hotpath books and those found via volume drill-down | ||
| const bookCandidateIds = new Set<Hash>([ | ||
| ...hotpathBookEntries.map((e) => e.entityId), | ||
| ...hierarchyPageIds, | ||
| ]); | ||
| hierarchyPageIds.clear(); | ||
|
|
||
| if (bookCandidateIds.size > 0) { | ||
| const topBooks = await rankBooks( | ||
| queryEmbedding, | ||
| [...bookCandidateIds], | ||
| Math.max(2, Math.ceil(bookCandidateIds.size / 2)), | ||
| rankingOptions, | ||
| ); | ||
| for (const b of topBooks) { | ||
| const book = await metadataStore.getBook(b.id); | ||
| if (book) { | ||
| for (const pid of book.pageIds) hierarchyPageIds.add(pid); | ||
| } |
There was a problem hiding this comment.
@copilot use your best judgement to resolve the issue in accordance with the DESIGN to the best of your abilities even if that means the DESIGN will need to be updated as well. Try to solve the problem while minimizing downstream and knock-on effects.
There was a problem hiding this comment.
Fixed in 91b10f4. Ranking functions (rankShelves/rankVolumes/rankBooks/rankPages/spillToWarm) now return RankedResult { id, score, childIds } — child IDs are captured during the scoring pass and returned alongside the score. Query.ts uses s.childIds/v.childIds/b.childIds directly from ranking results instead of re-fetching entities from IDB.
lib/cortex/Query.ts
Outdated
| // Collect candidate page IDs from hierarchical routing. | ||
| const hierarchyPageIds = new Set<Hash>(); | ||
|
|
||
| // Shelf → Volume → Book → Page drill-down | ||
| if (hotpathShelfEntries.length > 0) { | ||
| const topShelves = await rankShelves( | ||
| queryEmbedding, | ||
| hotpathShelfEntries.map((e) => e.entityId), | ||
| Math.max(2, Math.ceil(hotpathShelfEntries.length / 2)), | ||
| rankingOptions, | ||
| ); | ||
| for (const s of topShelves) { | ||
| const shelf = await metadataStore.getShelf(s.id); | ||
| if (shelf) { | ||
| for (const vid of shelf.volumeIds) hierarchyPageIds.add(vid); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // Rank volumes — include both hotpath volumes and those found via shelf drill-down | ||
| const volumeCandidateIds = new Set<Hash>([ | ||
| ...hotpathVolumeEntries.map((e) => e.entityId), | ||
| ...hierarchyPageIds, | ||
| ]); | ||
| hierarchyPageIds.clear(); | ||
|
|
There was a problem hiding this comment.
@copilot use your best judgement to resolve the issue in accordance with the DESIGN to the best of your abilities even if that means the DESIGN will need to be updated as well. Try to solve the problem while minimizing downstream and knock-on effects.
There was a problem hiding this comment.
Already addressed in 4335f67 — hierarchyPageIds was renamed to per-tier variables: volumeIdsFromShelves, bookIdsFromVolumes, pageIdsFromBooks. Each variable now clearly indicates its tier.
| // Every page must belong to exactly one book | ||
| const allBookPageIds = result.books.flatMap((b) => b.pageIds); | ||
| for (const page of result.pages) { | ||
| expect(storedBook!.pageIds).toContain(page.pageId); | ||
| expect(allBookPageIds).toContain(page.pageId); | ||
| } |
There was a problem hiding this comment.
@copilot it makes more sense if "exactly" is replaced with "at least". There's a small albeit non-zero chance that we hit the same page across multiple books. For instance legal corpora are famously long and duplicative and full of cross citations.
There was a problem hiding this comment.
Fixed in 91b10f4. Changed the test comment to "Every page must belong to at least one book" and removed the exactly-once membership assertion. Pages can now appear in multiple books without failing the test.
…line Swap the ordering so that Books/Volumes/Shelves (and their reverse indexes) exist before semantic neighbor insertion attempts to flag dirty volumes via page→book→volume traversal. Also: rename misleading `hierarchyPageIds` in Query.ts to per-tier variables (`volumeIdsFromShelves`, `bookIdsFromVolumes`, `pageIdsFromBooks`) and strengthen the integration test assertion to enforce exactly-once book membership. Co-authored-by: devlux76 <86517969+devlux76@users.noreply.github.com>
…book membership assertion
Ranking functions (rankShelves/rankVolumes/rankBooks/rankPages/spillToWarm)
now return RankedResult { id, score, childIds } so Query.ts can use child
IDs from the ranking pass directly instead of re-fetching entities from IDB.
Test assertion for page-to-book membership relaxed from "exactly one" to
"at least one" per user feedback — pages can legitimately appear in
multiple books (e.g., legal corpora with cross-citations).
Co-authored-by: devlux76 <86517969+devlux76@users.noreply.github.com>
metroidNeighborsvariables and commentsingestText()now callsbuildHierarchy()for full hierarchybuildHierarchy()now runs beforeinsertSemanticNeighbors()so reverse indexes (page→book→volume) exist when dirty-volume flags are sethierarchyPageIdsin Query.ts to per-tier variables (volumeIdsFromShelves,bookIdsFromVolumes,pageIdsFromBooks)RankedResult { id, score, childIds }so Query.ts uses child IDs from the ranking pass directly instead of re-fetching entitiesOriginal prompt
💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.