docs: plan C2-04 library business entry and shared mirror contract (row 215)
This commit is contained in:
1 parent
a2c72ac952
commit
6a508804f0
2 files changed
+286
No files matched your search
@@ -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/<branch> 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.
|
||||
@@ -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.
|
||||
Reference in new issue
Block a user