Repository navigation
Private books in the teaching corpus; collectEntities compares by content - #32
Conversation
…tent - privateBooksDir (off by default): one markdown file per book, cut on its chapter headings or page markers into one private entity with many fragments (buildPrivateBooks, fromPrivateBook). The heading goes into the fragment's text and nowhere else; publicTitle and requireReview land on the entity's policy; the metadata lint runs over the whole book before npm run index drafts or embeds anything; two pieces that share a locator are refused by locator. The example content does not change. - collectEntities: two descriptions of one entity compare with object keys sorted at every level instead of by property insertion order (the review nit on #23), with a regression test; order inside a list is still content. - A bare year in frontmatter (YAML reads `date: 1900` as a number) is a date at year precision instead of a silently dropped field. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01F2ti456guy1bXVmjA3fBTV
Automated Checks (advisory, non-blocking)✅ All checks passed. Standards ComplianceThis PR directly touches boundary-sensitive code ( Also, the prior review nit about page-marker-derived ids being described too strongly looks fixed: the new comments in SummaryThis adds a new private-book ingest path: one markdown file becomes one private entity with many fragments, and What to pay attention to
Things I noticed🟡 Yellow flags — consider for this PR or a follow-up:
Good patterns
Suggested improvements
Questions for the author
Surmado Code Review (v1.5-mt, review #4 on Want to change your STANDARDS.md or YML? Edit it directly, or tune it with our AI agent Scout. Comment |
A chapter value is a parsed number or an ordinal, but a page value is the author's own marker token, so the duplicate-locator refusal echoed an authored value. It now names the two positions and the locator scheme; the test asserts the token never reaches the message. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01F2ti456guy1bXVmjA3fBTV
|
Verified, and it was a real exception. For a book, a On the suggested tests: Generated by Claude Code |
The id carries the locator values; a locator is public surface by contract and every value goes through the metadata lint before the id goes anywhere. A comment at the id states that, and the test shows a page marker's token refused by the lint, named by field and never echoed. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01F2ti456guy1bXVmjA3fBTV
… construction A page marker's token becomes a locator value and part of the fragment id, both printed by every refusal that names a fragment. The reader now refuses a marker that is not a short label of letters, digits, '.' and '-', by piece position and never echoed. The earlier push's assertion was wrong for the same reason it was worth having: the lint's own message names the id. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01F2ti456guy1bXVmjA3fBTV
|
Traced it, and the honest answer was "not pinned anywhere": 963c784 closes it at the source instead. Generated by Claude Code |
The two comments called a bounded page token "structural by construction". It is an authored string in the linted bucket (STANDARDS §1, §4): the adapter's metadata lint checks it against the book; the reader's bound only gives the lint's own refusal a safe error surface. The comments and the changelog line now say that. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01F2ti456guy1bXVmjA3fBTV
|
Yes, the lint sees the raw token. The wording was the overstatement, and 58b0045 fixes it: both comments and the changelog line now say "linted, not structural", with the bound described as what it is. The page-token test already pins the id story on both sides (four markers refused by position without being echoed; four accepted and their ids spelled out); the lint's coverage of locator values is pinned in Generated by Claude Code |
What & why
Refs #24, #28. The keyless half of the demo
semanticentity: the core can now ingest a whole private book as one entity with many fragments, whichdemo/andexample-content/could not express. The demo wiring (the Gutenberg novel, its drafted gists, the gold route cases) is the next PR and needs a keyeddemo:build.privateBooksDirinarchive.config.ts, off by default; the example content does not change. One markdown file per book.buildPrivateBooks(src/corpus.ts) reads the frontmatter (title,about, and optionallyauthors,identifiers,date,version,themes,exposure,publicTitle,requireReview,fragmentBy,maxFragmentChars,firstPage) and cuts the body with the existingfragmentByHeadings/fragmentByPageMarkersinto pieces whose locators are structural. A leading# Titlethat repeats the frontmatter title is the title page, not a chapter.fromPrivateBook(src/adapters/teaching.ts) yields one private entity and one fragment per piece (book:x#ch12,book:x#ch12.s3,book:x#p184). The heading goes into the fragment's text and nowhere else;publicTitleandrequireReviewland on the entity'spolicy; the section 6 metadata lint runs over the whole book beforenpm run indexdrafts or embeds anything, so a quoting title fails in the reader's output, not after a paid build. Two pieces that share a locator are refused by locator.npm run indexreads the books beside the records and the notes; nothing aftercollectEntitieschanges, because the pipeline already drafts and resolves per fragment with the entity's whole text as the lint context.collectEntitiescompared two descriptions of one entity byJSON.stringify, so the same entity emitted with its properties in another insertion order was refused as "described two ways" (the nit from the 3.0.0 substrate: the archive contract, then Phase 1 in steps #23 review). It now compares by content with object keys sorted at every level; the order inside a list (creators, identifiers) is still content. Regression test added.date: 1900as a number) was dropped silently by every reader; it is now the date at year precision. No shipped frontmatter uses one, so nothing in the example content or the demo changes.Docs: README section 1 and "Make it yours" step 4,
docs/CONTRACT.md§12,CHANGELOG.md.test/books.test.tsis new (invented novella fixture); the served-index test asserts that no phrase of the book survivestoServedIndexand that a hit carriesch. 2, the title, the URL, and the ISBN, never the text.Checklist
npm testpasses without an API key (the offline CI gate) — 109/109, no live callsnpm run typecheckpassesFragment: its text has no field to travel in, and the new served-index test pins itnpm run test:distpasses andnpm run demo:runholds the headline (7/7, rho 1.0000).github/STANDARDS.md— the new reader's messages name the file and the field, never a value🤖 Generated with Claude Code
https://claude.ai/code/session_01F2ti456guy1bXVmjA3fBTV
Generated by Claude Code