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