diff --git a/agents/herb/work/queue-215/BUILD.md b/agents/herb/work/queue-215/BUILD.md new file mode 100644 index 000000000..e52b6b0e5 --- /dev/null +++ b/agents/herb/work/queue-215/BUILD.md @@ -0,0 +1,103 @@ +# Row 215 build record + +Owner: Herb. Reviewer: Filbert. Claim: herb-215-start, rev 2753. +Brief: docs/plans/2026-10-20_case2-rows.md, C2-04, pin +16847e94ae1768881711318fe81cbb5e6c66b971. Design: Gosalyn r11, +sections 1.1, 1.3, 2.1, M4, M9 and 7.2. + +Base: a2c72ac952df94ecab7e391d5042f49b7f88b816, row 212's reviewed +candidate, under Q177. Branch: herb/row215. Rebuild on the combined 212+197 +landing when Sage names it. Do not replay or revert landed changes. + +## Scope rulings (sage-1848) + +1. Add packages/bus/src/runtime.mjs for library adapter injection and the + library skip in protectedRoots. The skip has its own consumer test. Change + nothing else there. +2. Add packages/library/src/mirror.mjs, named mirror rather than store. It + creates a bare mirror without a remote, reads at the served commit and + reads sync state. Export one state filename constant, and export this + module through packages/library/src/index.mjs. Row 222 imports the constant. +3. Add packages/bus/src/server.mjs only to await library.file and + library.status, with socket tests proving their replies are results, not {}. +4. HTTP exposure is deferred. Row 225 owns the library.status route; + C2-11b owns library pages. Leave serve.mjs alone. webui/src/cli.mjs gets + only callback wiring next to the project-file callbacks on both listeners. +5. Reuse existing refusal codes where possible. +6. Build on a2c72ac95 until Sage supplies the 212 landing SHA. + +Additional ruling, sage-1852: library-state-invalid is approved for malformed, +unsafe or mismatched state, including a missing file while the served ref +exists. The version-1 shared contract below is approved. Row 222 also owns +runtime.mjs for its sync and timer wiring, recorded in that row's manifest. + +## Shared contract with row 222 + +Agreed in herb-0048, megavolt-0081/0082 and sage-1852. This section is the +contract Filbert reviews and Megavolt cites for Dewey. It is not a sync writer. + +Module: packages/library/src/mirror.mjs. Export +`SYNC_STATE_FILE = 'mosaic-library-sync.json'`. The full path is +`join(mirror, SYNC_STATE_FILE)`, inside the bare repository. Only sync.mjs +writes it, using a sibling temp file, fsync, rename, and directory fsync. + +Version 1 has exactly these keys: + +```json +{ + "version": 1, + "branch": "main", + "served": null, + "lastFetch": null, + "lastError": null, + "diverged": null, + "problems": [] +} +``` + +- branch is the configured mapping branch and must match the reader's branch. +- served is null or a full lowercase 40-hex commit ID. refs/heads/served is + authority. The state value must equal that ref, which must name a commit. +- lastFetch is null or a canonical ISO UTC timestamp. +- lastError is null or exactly {code, at}. code matches [a-z-]{1,64}; at is + canonical ISO UTC. It never holds stderr, prose or a credential value. +- diverged is null or exactly {commit, at}, with the same commit/time rules. +- problems uses C2-01's exact {code, path, message, line?}. code is R1-R7, + message is that code's fixed RULES text, line is a positive safe integer + when present. No file body or arbitrary message is accepted. +- An absent file and absent served ref return the empty object above with + the configured branch. An absent file with an existing served ref refuses. +- Unknown keys, bad types, unsafe filesystem metadata, invalid JSON and a + mismatch refuse library-state-invalid. Reads use a bounded regular owned + private file without following links. The read never repairs state. +- refs/remotes/library/ is not duplicated in this file. Row 222 may + read it when it needs the fetched tip. + +Exports consumed by row 222: + +| Export | Contract | +| --- | --- | +| mirrorPath({business, project, dataRoot}) | Require a library entry; return projectRepo's checked path | +| ensureMirror(context) | Promise of the mirror path; race-safe idempotent bare init; no remote or fetch | +| servedCommit(mirror) | Promise of full commit ID or null; refs/heads/served only | +| readSyncState(mirror, {branch}) | Promise of checked version-1 state; compare with served ref | + +Host adapter exports for this row: + +- libraryStatus(context): return {project, ...state}, with no repository path + or content. Do not fetch or rebuild on this read. +- readLibraryFile({...context, path}): read a mapped, readable file at the + served commit, returning {project, path, commit, content}. No caller-picked + commit, working-tree fallback, fetch or write. C2-01's R1/R2/R7 exclusions + apply. A never-synced mirror refuses library-commit for a file read. + +Row 222 owns syncCommand, sync, writeSyncState, the lock, the timer and state +publication. It rebuilds problems with existing readLibrary({repo, commit, +mapping}). It does not need a second rebuild function from this row. + +## Verification record + +Planning stage. No product code, test run, suite, image build, live operation +or push for row 215 has happened. Product implementation awaits Filbert's +manifest approval. Sage owns suite and full-gate grants. Row 197's proof +commit is separate and is not part of this row's changes. diff --git a/agents/herb/work/queue-215/MANIFEST.md b/agents/herb/work/queue-215/MANIFEST.md new file mode 100644 index 000000000..75f59afd0 --- /dev/null +++ b/agents/herb/work/queue-215/MANIFEST.md @@ -0,0 +1,183 @@ +# 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 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. +- Singles may run under COMMON; suites and any long run need Sage's grant. + 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/project/src/paths.mjs | Skip library entries in root overlap loop (:41-44); keep default projectRepo location | +| 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; project/tests/project.test.mjs; +bus/tests/project-runtime.test.mjs. All paths are under packages/. + +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. 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 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 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. +- [ ] 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. State filename, types and authority match sage-1852 and Megavolt's +agreement. Fetch, timers, locks for sync and HTTP stay with their later rows. +This is a plan only; no product edits or test execution precede approval.