# C2-04 library business entry implementation plan > Execution: Herb implements in this session using the executing-plans skill. > Filbert approves this manifest before any product implementation. **Goal:** Load a library mapping without giving an agent a checkout, protect all project mappings from organization writes, and expose human-only bus reads of the bare mirror's served commit and sync status. **Architecture:** C2-01 checks mappings and reads commits. Business consumers skip library entries. A host-only mirror module owns local mirror reads and initialization. runtime.mjs injects that module into the broker, preserving the broker's sandbox import closure. Sync and HTTP routes belong to later rows. **Tech stack:** Existing Node ESM, node:test and bounded local Git helpers. No dependencies. node_modules remains the plain canonical symlink. **Spec:** Pinned C2-04 brief; Gosalyn's approved design 1.1, 1.3, 2.1, M4/M9 and 7.2; BUILD.md's sage-1848/1852/1860 scope and shared-state contract. ## Constraints and review focus - Base a2c72ac95; rebase only onto Sage's named 212 landing on a new branch. - Existing code-project entries retain root/repo/remote behavior. Library entries have no root or repo override and use C2-01 checkMapping. - Preserve the broker's pinned imports. No library import in broker.mjs and no new runner mount. Do not edit serve.mjs or add an HTTP route. - No fetch, remote config, credential read, timer, sync writer, bus write or live operation. All tests use made-up content and local repositories. - All test/probe runs require Sage's grant (sage-1867), including singles. Stop and report two failures in one gate. Do not push without authorization. Review focus, with owning tasks below: library-first iteration must not break a later code project (T1); serialized documents cannot bypass M9 (T2); racing initialization must not replace another mirror (T3); stale or unsafe state must not leak content (T3); async socket replies must preserve the result and human-only admission (T4). ## Paths and evidence from the base | Product path | Allowed change | | --- | --- | | packages/business/src/business.mjs | Dispatch kind:library to checkMapping before code-root validation (:228-244) | | packages/business/src/organization.mjs | Compare canonical projects in write before publication (:177 onward) | | packages/business/src/cli.mjs | Skip library roots in validate, resolve --project and credential exclusion roots (:60-61, :71-79, :118) | | packages/business/src/resolve.mjs | Skip library entries while matching a project file (:44) | | packages/business/src/instance-settings.mjs | Skip library before projectFilePath (:142), nothing else | | packages/seat/src/checkout.mjs | Skip library entries in ensureCheckouts (:315-327) | | packages/cli/src/launcher.mjs | Pass only code entries to ensureCheckouts (:354) | | packages/cli/src/config.mjs | Skip library entries in trackerFor's root loop (:61-63), per sage-1860 | | packages/project/src/paths.mjs | Skip library entries in overlap loop (:41-44); default record use refuses library, mirrorPath internally opts into allowLibrary | | packages/library/src/mirror.mjs | New bare init, checked state and served-commit file read module | | packages/library/src/index.mjs | Export mirror module interface and SYNC_STATE_FILE | | packages/bus/src/broker.mjs | New injected library adapter, human-only admission and two async verbs | | packages/bus/src/runtime.mjs | Library adapter wiring, mirror initialization and protectedRoots skip only | | packages/bus/src/server.mjs | Await the two library verbs only | | packages/webui/src/cli.mjs | Two library read callbacks beside project callbacks on main and tester listener; no routes | packages/business/src/project.mjs need not change: callers skip a library before projectFilePath or loadProject. Do not weaken their code-root checks. scripts/stack-copy.sh already copies packages/library/src and needs no edit. Test paths to modify: business/tests/business.test.mjs, cli.test.mjs, resolve.test.mjs, instance-settings.test.mjs; seat/tests/checkout.test.mjs; cli/tests/launcher.test.mjs, trackers-boot.test.mjs; project/tests/project.test.mjs; bus/tests/project-runtime.test.mjs. All paths are under packages/. T5 dependency-audit addition: cli/tests/stack-release.test.mjs and its stack-release-fixture.mjs helper copy packages/library/src alongside business/src, whose validator now imports the mapping checker. No assertions or timeouts change. Whole-file dependent singles require a later Sage slot. New tests: packages/business/tests/library-projects-protected.test.mjs; packages/library/tests/mirror.test.mjs; packages/bus/tests/library.test.mjs; packages/webui/tests/library-cli.test.mjs. Reuse existing made-up fixtures. If a new fixture is needed, define it within the corresponding new test file. No unrelated test assertion or timeout changes. Record paths: agents/herb/work/queue-215/{MANIFEST.md,BUILD.md}, later source manifest and test receipts there; one append-only BUILD-LOG.md phase for this row when implementation begins. Sage owns docs/SESSIONS.md. ## T1. Mapping and consumer skips - [ ] Add failing business tests: accept a frozen library mapping without a root, retain mixed/only-library documents, and propagate C2-01's mapping and playground refusal codes. A role publish naming a library project refuses at load, even with an otherwise valid fork identity and remote. Existing code validation remains unchanged. - [ ] Add a separate regression for each consumer: CLI validate and its credential exclusion roots; CLI resolve --project books has no project layer; resolveInstance finds a later real project; instance settings still writes an unrelated setting; ensureCheckouts returns no library checkout; launcher creates no books workspace clone; projectRepo skips a library while still rejecting overlap with a real code root. Put books first. - [ ] Add the real bus boot regression in cli/tests/trackers-boot.test.mjs: tracker.baseUrl is set and books is a library entry; boot succeeds, books produces no tracker, and a later real tracker project still works. - [ ] projectRepo(context, {allowLibrary = false} = {}) refuses a library entry by default with project-invalid. Only mirrorPath opts in. Test that ordinary human and tester record initialization leave no directory at the future mirror path; no public verb can supply allowLibrary. - [ ] Add runtime's protectedRoots consumer regression in T4's real runtime fixture. A valid credential outside code/data roots still loads with a library entry, and existing credential protections still refuse unsafe files. - [ ] Run each changed test singly, confirm the new cases fail for the intended root assumption, then implement only the table's guards and checkMapping dispatch. Re-run the same files singly. CLI validate reports a library entry as library, without an absent-project warning. CLI resolve --project books resolves without loading a project file, as the design's skip requires. It does not fabricate project vars or a root. ## T2. M9 at the organization writer - [ ] In the new business test file, call organizationWriter.write directly with a changed playground prefix and, separately, a changed hybrid entry. Both refuse organization-projects-protected. Snapshot business bytes and changes.jsonl before each call; assert both remain identical afterward. - [ ] Cover addition, deletion and changes to code projects too, since M9 protects all projects. Reordered object keys are allowed; array changes are changes. An ordinary non-project write still succeeds and logs. - [ ] Add a serialization regression so a proposed object's serialized projects cannot differ from those checked before publication. - [ ] Run the file singly to see the missing refusal, compare canonical projects against old disk JSON and the document that actually lands, before any publication/receipt, then re-run it singly. ## T3. Bare mirror, served file and read-only state - [ ] Add failing tests for mirrorPath and ensureMirror: exact projectRepo default, no checkout/remote, retained no-links/overlap protections, idempotence, simultaneous creators and preservation of a pre-existing non-mirror directory or unsafe Git metadata. - [ ] Add failing state tests for BUILD.md's full version-1 contract: missing state/ref, valid state, malformed JSON, unknown keys/types, branch mismatch, invalid times/IDs/problem shape, symlink/hardlink/non-private file and missing or mismatched served ref. Assert reads change no bytes. - [ ] Add shared-lock tests: withLibraryLock(mirror, operation) waits at most 5000 ms, refuses library-sync-busy on timeout and releases after a throw. A reader arriving between the publisher's served move and state write waits, then reads the consistent pair. Cover first sync and fast-forward in independent processes, same-process independent acquisitions and release after owner death. A callback-scoped reader expires on release. - [ ] Implement the lock by async spawn of flock on the parent's inherited fd 3, as pinned in BUILD.md. Await acquisition-child exit before calling operation. Never spawnSync during acquisition, which could freeze the bus for 5 s. A parallel ordinary bus read remains responsive while a library read waits. Only the publication/read lock is shared; fetch is outside it. - [ ] Add served-read tests against local fixtures containing spaces, capitals, a .canvas and unmapped top-level note. Seed a served ref and state without fetching. A newer unrelated HEAD does not change the returned file. R1/R2/R7 exclusions, traversal, a missing path, a caller commit and a never-synced mirror refuse. Status contains no fixture body marker. - [ ] Implement mirror.mjs and index exports using the shared contract. Initialization stages into an owned private sibling directory and renames atomically; a losing creator validates the winner and removes only its own staging directory. Git uses existing bounded safe helpers, no remote. - [ ] Run mirror.test.mjs singly, red then green. Re-run C2-01 reader.test.mjs singly if a reader assumption changes. Do not change C2-01 reader rules. mirror.mjs validates a bare mirror's metadata before Git reads it, including no remote/include/indirection. It does not apply inspectGit's working-tree .git assumption to a bare repository. Failures reuse library-git or the approved library-state-invalid code. State reads are bounded and private. ## T4. Human-only bus reads and deferred Console callbacks - [ ] Add broker tests for library.file and library.status using injected adapters. Each agent session refuses human-required before adapter access, including bad arguments and an absent adapter. A reader or tester does not gain a human capability. Human calls validate keys, then use checked library context. Secret checking and bounded refusal evidence remain. - [ ] Add real startBroker/socket tests for both verbs. Seed only local fixture mirror data. Assert result shape/content and an actual status field, not merely successful transport or {}. Cover never-synced status and runtime's protectedRoots skip. Keep the pinned import-closure test. - [ ] Inject a separate library adapter via runtime, initialize only library mirrors, add the two async request paths, and include them in server's awaited list. Do not import library or fs into broker. - [ ] Unsafe or pre-existing non-mirror initialization refuses bus boot, closes opened resources and never publishes a ready socket. Add that runtime regression in library.test.mjs. - [ ] Wire main and tester Console callbacks beside project-file callbacks. No HTTP route is shipped. The new CLI test captures the startServer options with a test-only module loader and calls the supplied callbacks against a fixture bus, checking the verb and transport identity. No product test seam, UI page, endpoint or serve.mjs change is needed. - [ ] Run library.test.mjs, project-runtime.test.mjs and library-cli.test.mjs individually, red then green. Human admission may use the existing fixture authentication seam; do not forge or bypass a live identity. ## T5. Review packet and Sage's gate - [ ] Search packages/*/tests and scripts/test-*.sh for each changed source path, imports and 64-hex pins. Record the resulting test-file list and run each changed/touched test singly. Stop and report a scope error. - [ ] Self-review the diff against this manifest and all acceptance cases. Record results, commands, skips, source hashes and remaining limits in BUILD.md. Request Sage's slot for the required business, seat, cli, project, bus and webui suites and every scripts/test-*.sh. Do not run them ungranted. - [ ] After applicable tests are green, make the source commit, build the candidate manifest from the authorized pushed tip, and move the row with scripts/mosaic. Sage posts token-requiring requests; Filbert reviews the candidate. Push only when authorized. No merge or live change by Herb. ## Self-review Every brief acceptance item is in T1-T4. Added host support paths match sage-1848/1860. BUILD.md records the complete 14-hit root audit. State shape and authority match sage-1852; the shared read/publication lock follows sage-1860 and Megavolt's agreement. Fetch, timers, outer sync serialization and HTTP stay with their later rows. This is a plan only; no product edits or test execution precede approval. Landing update: prepared herb/row215-on212 on fefd54e13 (includes row 212 landing ee7b2f137), without history replay or stash. Only BUILD-LOG.md and cli/tests/stack-release.test.mjs overlap the a2c72ac95-to-landing change set. BUILD-LOG preserves all landed entries and appends the existing Herb entry. stack-release preserves the landing plus only the library/src copy list addition. No test has run on this landing yet. All further execution requires a Sage grant (supersedes the original COMMON singles rule). Current merge preparation per sage-1983/1987: new branch herb/row215-on-f827 on f8276c5eb3ecb0aad353ee65e3dd8f7faac5dd87. Nine landed overlaps are recorded in current-MERGE.json and the exact candidate deltas against the landing are in current-merge-only.patch. Two textual conflicts were resolved manually: Broker keeps hostKeys and adds library; business.mjs keeps CREATIVE_DEFINITIONS and adds checkMapping. The other seven merges preserve the landing plus the original C2-04 edits. All other product/test paths match the early215 tree byte for byte. The landing's BUILD-LOG is preserved and the existing Herb entry is appended. No history replay, stash, force push, branch deletion or source commit. No test/probe/syntax check ran on the merged tree after the slot was cleared. All16 whole singles need a new Sage grant before candidate commit/review. The10 old-base315/0 results are early reads only, never merged gate evidence. Candidate status: source commit e333af4dc7af3a9d3789c8c0db26a0df8bdddfee. The16 whole singles passed451/0 under sage-2008 with zero skips/cancellations; raw outputs and merge receipts are in this packet, indexed by EVIDENCE.json and SOURCE.json. Sage keeps the landing gate. Filbert's implementation review and that gate remain pending. Prior315/0 on fefd54e13 is early read evidence only. Sage-2016 authorizes the seat-only source/packet push.