Compare commits
17
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
21bf30db67 | ||
|
|
2848d832e1 | ||
|
|
4fb11e74a3 | ||
|
|
7932c979ab | ||
|
|
8a47c462a4 | ||
|
|
234dfe3048 | ||
|
|
904b313831 | ||
|
|
06bf87724a | ||
|
|
a024b403ca | ||
|
|
c88344cc71 | ||
|
|
b9b0bf5842 | ||
|
|
4b9d7b45db | ||
|
|
f25d1d59ac | ||
|
|
2aeda82f48 | ||
|
|
e3a1387fbf | ||
|
|
381e1001cb | ||
|
|
99baa507dd |
No files matched your search
@@ -5585,3 +5585,8 @@ claim. Receipts and limitations in agents/lena/work/queue-296/BUILD.md and run-r
|
||||
2026-10-24T23:37:46Z | Sage | Row 329 (#1827, P1 pinned model proxy, Magica) LANDED 61ebf2c34, pushed 23:31:27Z 10-24. Before: refactor at 669cdf42c. After: Magica's 4 commits (94cde633e, c65fb34ee, d1f6f2b19, 61ebf2c34) cherry-picked onto 669cdf42c, diff vs the gated tree limited to drift paths, range-diff in scratch. Gate: one unchanged full landing gate in /mnt/storage/scratch/sage-r329l/wt (gate.sh 8b4978887 c8a9bcf94), started 23:08:12Z at load 14.35, GATE GREEN 23:29:14Z at load 8.67, no rerun, no test or timeout edit (mos-1634). Host note: two Codex workers cleaned T3 worktrees from about 23:14Z (mos-1639, Vikunja 632). Approvals: Dewey r2 c31643 rev 4047, Boyd r2 c31642 rev 4048. #1827 comment 31650, closed. Vikunja 237 comment 1109 (stays open for P2-P7); C4 P10 red filed as Vikunja 631. Queue: note rev 4063, done rev 4064, committed b24d917ec. DEFERRED: N2 and R2-N1 bound to row 331, N1 bound to P7. Overlap: row 284 (packages/bus/src/broker.mjs) and row 157 (scripts/mosaic-config.mjs) rebase onto 61ebf2c34 before their gates.
|
||||
2026-10-25T00:15:48Z | Sage | Row 284 (#1789, C2-05c library.get scope, Della) LANDED ce0b6ed41, pushed 00:09:52Z 10-25. Before: refactor at 9e2689dff (row 329 landing docs). After: fast-forward to Della's 7 rebased commits 78b932feb..ce0b6ed41; 8 paths, manifest-r4 de505cfd matches the landed tree. Gate: one unchanged full landing gate in /mnt/storage/scratch/sage-r284l/wt (the 329 gate.sh with D and the compose project renamed), started 23:51:19Z at load 1.62, GATE GREEN 00:09:04Z at load 5.20, no rerun, no test or timeout edit. Counts: bus 1020/0, business 283/0, cli 999 pass 1 allowed skip (FED R7), harness 177/0, library 156/0, seat 218/0, webui 458/0, every scripts/test-*.sh rc 0; queue 34 and task 87/93 skips are the standing ones. Della's whole-file runs on ce0b6ed41: library-lookups 12/0, library-tools 5/0, query 34/0. Approval: Webby round 2 delta c31654 rev 4070 (round 1 rev 4062). Overlap sweep: rows 13, 157, 309, gyro/row310 and fethry/row332-p1 clean. #1789 comment 31655, closed. No Vikunja task for the row. Queue: note rev 4071, done rev 4072, committed 7c8dc2033. DEFERRED: Webby N1 (whole-tree listing per get) bound to the next library read-path row; N2 needs nothing; N3 is this gate.
|
||||
2026-10-25T00:18:35Z | Sage | Follow-up tasks for mos-1647 item 3 (Q109): Vikunja 633 (P7 cutover, CLIProxyAPI 8.0.42 startup Grok-npm and antigravity version refreshes, from row 332 isolated runs) and 634 (row 284 Webby N1, library get lists the whole tree). DEFERRED gains the 633 entry and the 634 number for row 284 N1. Filing script and receipt in agents/sage/work/vikunja-setup/.
|
||||
## 2026-10-23 — Row 288 C2-02 build starts (Bubba, #1793)
|
||||
|
||||
Before: agents can read the library; supporting-path and push-credential dependencies are separate unlanded heads. After intended: per-seat supporting writes/deletes with exact notices, human revert and one commit per Console save. Work is on bubba/row288, chained on original 283/285/286 commits; 284 tool base still pending. Plan and acceptance evidence: agents/bubba/work/queue-288/BUILD.md. Untested; no Node/test/build/gate run before Sage CLEAR. Landing waits for every dependency's own gate.
|
||||
|
||||
2026-10-25 | Bubba | Row288 #1793 gate after landed284 | Candidate2848d832e:289 whole files rc0 (2604 pass,1 covered FED skip),12 scripts rc0,6 mutants killed/restored controls rc0. Own bare-delete and fixture reds fixed with driver restarts; no pre288 red. Per-run hashes/rc in agents/bubba/work/queue-288/gate-r6-results.tsv. Slot returned Sage bubba-0034; manifest review/landing pending.
|
||||
@@ -0,0 +1,501 @@
|
||||
# Row 288 (#1793): C2-02 agent writes, notices, revert and Console saves
|
||||
|
||||
Owner: Bubba, Codex gpt-6.1-sol, thread 3d6672b5-5544-4b95-9e09-e79574f1aa14.
|
||||
Reviewer: Webby. Gate and landing: Sage. Append-only build record.
|
||||
|
||||
## 2026-10-23 start and implementation plan
|
||||
|
||||
Base 45dca69f6 on bubba/row288. Direct mandate mos-1499 (Q245), confirmed sage-3613.
|
||||
The canonical `scripts/mosaic queue next bubba` returned `nothing`; Sage owns the owner transition.
|
||||
The initial worktree invocation refused linked-worktree use; retried in canonical checkout.
|
||||
|
||||
Spec: docs/plans/2026-10-20_case2-rows.md, C2-02; agents/gosalyn/work/case2/DESIGN.md ruling after r11.
|
||||
Handoffs: Honker queue-283 BUILD.md Q4 (missing/non-100644 delete refuses); Morgana
|
||||
queue-225 BUILD.md A2 at 66f7becf2 (strict revert arguments/result), confirmed by Fethry fethry-0005.
|
||||
`project` is the business.projects key, never a chat UUID.
|
||||
|
||||
Dependencies merged with their commits intact, no amend or force-push:
|
||||
- 283 origin/honker/row283-build 88ec5e8e4 (new E4 head to follow).
|
||||
- 285 origin/scrooge/row285-r2 53db6b7ca.
|
||||
- 286 origin/ludwig/row286-r2 573fa0be0.
|
||||
- 284: no named remote branch yet. Tool changes come last.
|
||||
All merge commits are marked Untested. Nothing has run under Node. Sage's latest instruction
|
||||
sage-3613 reserves **all Node and test runs** for CLEAR; this supersedes the initial targeted-test
|
||||
permission. Build untested now as instructed, then run RED/GREEN checks/mutants on CLEAR.
|
||||
|
||||
Architecture: one host-only plumbing writer under the existing library lock; broker authorizes and
|
||||
selects credentials, then inserts the fixed notice directly. Human save/revert use editor +
|
||||
library-human independently of seat lifecycle. No worktree, deferred push, role expansion or UI.
|
||||
|
||||
### Tasks and acceptance
|
||||
|
||||
- [ ] Plumbing: new library/src/write.mjs, mirror state publication and package exports; new
|
||||
library/tests/write.test.mjs. One-parent write/delete, byte-exact content, seat identity/trailer,
|
||||
stale-base, nonregular/missing deletes, retry once on an unrelated remote change, push failure
|
||||
without local ref movement, human editor and revert reachability/single-path checks.
|
||||
- [ ] Broker + runtime: library.doc.write/delete, library.file.save, library.revert; strict keys,
|
||||
creative + library.write + live unheld project grant; direct exact notice; human-only save/revert.
|
||||
New bus/tests/library-write.test.mjs covers authorization, unchanged refs, A2 contract and socket.
|
||||
- [ ] Transport: only human library.file.save joins the existing large-save exception. New tests
|
||||
prove >64 KiB save accepted, agent writes retain 64 KiB bound, malformed/oversize saves refuse.
|
||||
- [ ] Tools after 284: library_doc_write/delete in harness tools; exact eight/ten-name snapshots
|
||||
in a new harness test; cli dispatch wiring.
|
||||
- [ ] Verify on CLEAR: new tests, library/bus/harness suites and all scripts/test-*.sh on Sage's
|
||||
assigned slot. Mutants remove supporting guard and force push; both must fail. Fixture-token
|
||||
search over captured output. No gate, landing or queue move claimed before Sage's acceptance.
|
||||
|
||||
Pre-flight interfaces: broker consumes library writer {project,path,commit}; revert adds reverted;
|
||||
writer consumes C2-03b push {pushed} or {raced,tip} and supportingPath(mapping,universe,path,tree).
|
||||
State publication must add optional lastPush while preserving old state readability. Refusals must
|
||||
restore fetched remote-tracking refs as well as preserve served. Runtime must bind credential file
|
||||
references itself; no file, identity, message or credential is supplied by the agent.
|
||||
|
||||
Necessary file-scope additions: packages/bus/src/runtime.mjs (actual adapter injection),
|
||||
packages/library/src/index.mjs (exports), and CLI's existing console adapter if saves require it.
|
||||
Reported to Sage in bubba-0002. Existing tests will not be edited without Mos's OK via Sage.
|
||||
|
||||
Review focus: remote tip races, nonregular entries and folded paths, unsafe human credential before
|
||||
git, revert after revoked/fired writer, and large-request exception never extending agent writes.
|
||||
|
||||
### Existing-test changes
|
||||
|
||||
None authored by Bubba yet. Dependency test changes remain their owners' original commits.
|
||||
|
||||
## 2026-10-23 first static build (not a gate result)
|
||||
|
||||
283 E4 merged intact at 76d651dba. Honker next reports e67c27b32 (test environment only), to merge
|
||||
before push. Della reports row 284 has not been built/pushed; row224's 23816cf28 is not a 284 head.
|
||||
No speculative 284 pin. Typed tools and served-blob lookup integration remain pending 284.
|
||||
|
||||
Implemented (unrun): bare plumbing writer, one-parent commit and fixed trailer, exact-content blobs,
|
||||
base comparison, supporting path guard and delete regular-blob check, one remote-race retry,
|
||||
ref restoration on refusal, immediate served publication/lastPush; human save and reachable one-path
|
||||
business-trailer revert; broker grants/identity/notice path; runtime adapter wiring; large-save socket
|
||||
exception. New tests: library/tests/write.test.mjs and bus/tests/library-write.test.mjs. No old test edits.
|
||||
|
||||
### Approved scope additions, sage-3628
|
||||
|
||||
Every hunk outside the brief's owned list:
|
||||
- `packages/bus/src/runtime.mjs`: `humanWriteOptions` checks editor before choosing the human slot;
|
||||
three library adapter entries bind dataRoot, command and the host-only seat store reference. These
|
||||
are needed to make actual bus calls reach the new writer without accepting agent-supplied files.
|
||||
- `packages/library/src/index.mjs`: export writeLibraryDoc/saveLibraryFile/revertLibraryCommit so
|
||||
runtime's existing library module adapter can call them.
|
||||
- `packages/library/src/sync.mjs`: shared write guard with opaque token compared by identity;
|
||||
push's race fetch recognizes only that owner and does not clear its guard; ordinary sync and a
|
||||
forged token still refuse library-sync-busy. Narrow publishLibraryWrite validates ids, reads the
|
||||
locked state, rebuilds the index via readLibrary, moves served/tracking and writes lastPush. The
|
||||
raw writeState remains private and is not exported through index.mjs.
|
||||
|
||||
Ludwig ludwig-0064 reviewed the intended interface and requested the opaque token (implemented),
|
||||
narrow publication (implemented), whole library + bus library-credential checks and a token mutant
|
||||
(planned). His 573fa0be0 remains current and unrun. 288's additions are separate commits, not folded
|
||||
into 286. Tracking restores only if the whole write refuses, not between the first race and retry.
|
||||
|
||||
Known tradeoff, accepted by Sage sage-3628 for MVP: the brief holds the flock through network Git;
|
||||
readers can hit the existing 5-second library-sync-busy while it runs. Structural supportingPath
|
||||
preflight is pure and precedes Git; served/fetched listTree checks repeat it with the real tree before
|
||||
any remote write. A tree-dependent check necessarily reads the tree first (Honker honker-0184 item2).
|
||||
|
||||
### Mutants for CLEAR
|
||||
|
||||
- Remove the `pathOf` calls for agent operations: the M12 broker/bare-ref tests must fail on a
|
||||
story/unsupported path while checking remote, tracking and served refs.
|
||||
- Prefix the push refspec in syncCommand with `+`: write.test moved-target race must fail, because
|
||||
its command seam preserves production syncCommand's refspec (only remote URL/protocol are fixture).
|
||||
- Replace `writers.get(mirror) === writeToken` with `true`: write.test forged-token case must fail.
|
||||
|
||||
No Node, test, build or mutant has run. `git diff --check` is clean. Counts are not claimed.
|
||||
|
||||
### Console dispatch integration
|
||||
|
||||
The existing cli delegates its transport wiring to console-bus.mjs, so the save dispatch needs that
|
||||
file too: one null `librarySave` option and one `--send` assignment to library.file.save. cli.mjs
|
||||
threads that option into startServer; row C2-11b owns the actual route/page that consumes it. New
|
||||
webui/tests/library-write-options.test.mjs covers --send and the user transport boundary. 225 owns
|
||||
libraryRevert dispatch; 288 adds only librarySave to avoid overlapping that ownership. Additional
|
||||
scope reported to Sage and Fethry. No changes to an existing test.
|
||||
|
||||
## 2026-10-23 second static pass and dependency review
|
||||
|
||||
First push: c867cada9 on origin/bubba/row288, reported bubba-0005 to Sage/Webby/Mos and
|
||||
bubba-0006 to Fethry/Ludwig. No PR created and no review/gate requested as complete.
|
||||
|
||||
Ludwig ludwig-0065 checked the exact sync diff against 286 without running anything. Token identity,
|
||||
ordinary-sync exclusion, 286 R1 preservation, guard key and end-of-write tracking restoration fit.
|
||||
F1: state publication could fail after served moved, leaving old state/new served. Root cause is
|
||||
publication order without rollback, compared against sync's existing rollback. Added an unrun
|
||||
filesystem rename-failure regression, then changed the narrow helper: on failure it validates the
|
||||
state under the same lock, CAS-restoring served if state has not published the new commit. If rename
|
||||
succeeded but directory fsync failed and the state validates against new served, it keeps that pair.
|
||||
All failure cases answer outcome-unknown; tracking remains the commit already pushed remotely.
|
||||
Verification is pending CLEAR, so this is a proposed fix, not a tested resolution.
|
||||
|
||||
Added the actual broker-to-bare tests for each M12 path refusal (both agent verbs), unchanged remote
|
||||
and all mirror refs, exact write/delete notices, and human revert after fixture grant removal/hold.
|
||||
The bare fixture moved to new library/tests/write-fixture.mjs so broker tests reuse real plumbing.
|
||||
Added the raw socket proof-nested-save-name case to pin the parsed-verb large-request check.
|
||||
Missing editor/slot tests now make the mirror metadata invalid first: reaching Git would change the
|
||||
refusal, so the tests check the pre-Git gate rather than merely count remote commands.
|
||||
|
||||
Additional required doc hunk: packages/library/README.md appends the four verb contracts,
|
||||
lastPush shape, exact notice, lock tradeoff and publication failure behavior. No existing test from
|
||||
the dependency/base has changed. All new/modified tests in this pass were authored by this row.
|
||||
`git diff --check` remains clean. No Node/tests/build/mutants run; 284 and CLEAR remain pending.
|
||||
|
||||
## 2026-10-23 early review follow-up (Webby E1-E6; Ludwig F2)
|
||||
|
||||
Sage sage-3652 approves console-bus.mjs wiring (null option + --send dispatch), confirms editor.user
|
||||
as the sole human login source and reserves CLEAR for T3/T4 after T2. Ordinary sync()'s separate
|
||||
post-rename fsync gap stays out of 288 per that ruling; no change to sync()'s existing rollback.
|
||||
|
||||
Honker 283 head 46bad7ea3 merged intact: mapping editor.user is optional, and if present uses the
|
||||
existing plain Gitea-user pattern. The omission at mapping level is valid; human writes refuse it.
|
||||
|
||||
- Ludwig F2 / Webby E6: confirmed the previous runtime used the fetch bot's mapping.user wrongly.
|
||||
Runtime now passes mapping.editor.user to humanCredential, never mapping.user. Missing user gives
|
||||
library-human-missing before Git. New real-runtime cases pin both save and revert with a separate
|
||||
fetch-bot login, positive pushes under her-gitea-login, and no command for an absent editor login.
|
||||
The broker's credential.agent is s.role, not args: added an exact assertion and strict-key refusal
|
||||
cases for caller-supplied agent and credential.
|
||||
- E1: replaced the business-id RegExp interpolation with a literal startsWith(business.id + '/')
|
||||
check and ID_PATTERN on only the instance suffix. New acme.co vs acme-co trailer test.
|
||||
- E2: new broker→bare case holds the seat in the fetch command. beforePush must refuse
|
||||
library-write-no-grant with all local refs and the remote unchanged and no notice. New mutant:
|
||||
remove `options.beforePush?.();` in write.mjs; the E2 test must fail.
|
||||
- E3: new broker cases for expired rotate_by, fire.stopped without fire.done, missing role.publish
|
||||
project, grant/identity Gitea-user mismatch, and missing identity name/email, for both agent verbs.
|
||||
- E4: the exceptional publication failure currently returns outcome-unknown before the broker has
|
||||
a successful result to put in a notice, so a remote agent commit can exist without a Console
|
||||
notice. There is no durable notice outbox/retry in this row. The trailer commit remains human
|
||||
revertible by id after sync; this is an explicit known failure-mode gap for the manifest, not
|
||||
a claim that every failed publication emits a notice. A new real-push test breaks state decoding
|
||||
after push admission, pins outcome-unknown, old served and no notice. It also found a local error
|
||||
propagation gap: errors before publishLibraryWrite's internal try now normalize to outcome-unknown
|
||||
in the writer once push has succeeded, so the caller is never told an ordinary refused-write code.
|
||||
- E5: 284 still has no build. Tools/8-or-10 snapshot and served-blob integration remain pending.
|
||||
librarySave is intentionally only an adapter option until C2-11b supplies its route/page.
|
||||
|
||||
All tests above are newly authored by row288 and remain unrun; no changes to baseline/dependency
|
||||
tests. RED/GREEN and mutants await CLEAR. Proposed fixes are not claimed verified. The new host-only
|
||||
library write module imports the existing pure ID_PATTERN to match the broker's accepted instance ids.
|
||||
|
||||
## 2026-10-23 mos-1520 ruling: post-push notice is required
|
||||
|
||||
Correction to the E4 interpretation above: Mos rejected the known-gap treatment. The requirement
|
||||
stays: a pushed agent commit has the exact notice even if local publication then fails. This is not
|
||||
an outbox or retry. The writer now calls the broker's trusted synchronous onPushed callback as soon
|
||||
as push returns pushed:true, before publication. The callback records the exact library-write notice
|
||||
and action.allowed in the normal Console-store transaction. Publication failure still answers
|
||||
outcome-unknown. The notice's Revert can refuse library-revert-not-agent until sync makes that commit
|
||||
reachable from served; README now says that explicitly and removes the rejected gap description.
|
||||
|
||||
The row's own real-push corruption test now pins the exact notice body with the pushed id, old
|
||||
served and outcome-unknown; after restoring readable state it pins Revert's refusal before sync and
|
||||
success after sync. The successful-write adapter fixture calls onPushed too, matching runtime's
|
||||
writer contract. No existing baseline/dependency test changed (mos-1520 explicitly authorizes this
|
||||
row's test correction without mos-1272).
|
||||
|
||||
Residual Console-store failure: if notice insertion/routing/its event transaction fails, the broker
|
||||
calls a separate host log sink with {event:library-notice-store-failed,code:outcome-unknown,business,
|
||||
instance,op,commit}. It logs no args, path content, credential, raw SQL or exception output. Runtime's
|
||||
new library.log adapter writes this metadata to stderr. The caller gets outcome-unknown even if
|
||||
recording action.refused in that same broken store fails. A new real-push test uses temporary SQLite
|
||||
abort triggers on notice insertion and refusal evidence, checks remote advanced/served unchanged,
|
||||
no message, the exact external log and outcome-unknown. It does not claim a notice can be stored in
|
||||
a failed Console store. If the host log sink itself also fails, no durable log can be promised; the
|
||||
caller still receives outcome-unknown. The runtime log hunk is the required residual-failure wiring.
|
||||
|
||||
Mutant added: move `options.onPushed?.({ project: context.project, path: change.path, commit });`
|
||||
from before `await publishLibraryWrite(...)` to immediately after that await inside the same try.
|
||||
Successful operations keep their notice, but the outcome-unknown publication path drops it. The
|
||||
C2-02 E4 real-push corruption test must fail on its exact notice assertion. Run on CLEAR and restore.
|
||||
|
||||
No Node/tests/build/mutants run. Static git diff --check clean. Della's first284 head0ed4fbab9 is
|
||||
explicitly incomplete and not a finished dependency; waiting for her complete-head pin before tools.
|
||||
|
||||
Webby webby-0253 (static, cef503cb9) closes E4 per mos-1520; no tests ran. N1: a failed notice insert throws from onPushed before publication, so served stays at the prior commit until sync; the store-failure test pins this safe direction. Only E5 (284 tools/snapshot integration) remains open in this early review.
|
||||
|
||||
## 2026-10-23 complete284 merge and final tool integration (unrun)
|
||||
|
||||
Della della-0037 supplied complete untested head47db04eba85ac3ceb7e81385603f97bb6f8c1a38,
|
||||
replacing the incomplete head. Merged intact as c0ca3894c. Its22423816cf28 and2164f3fbc25a
|
||||
lineage is retained. Three conflicts were in overlapping production wiring only:
|
||||
- broker.mjs: keep Della's LIBRARY_LOOKUPS object and #requestLibraryLookup byte-for-byte, and
|
||||
keep288 writer/notice methods; LIBRARY_VERBS contains both sets and request dispatch calls lookup.
|
||||
- runtime.mjs: keep lookup adapter plus288's metadata log and write adapters.
|
||||
- library/index.mjs: keep index/query exports,286's push/humanCredential exports and288's writer exports.
|
||||
No dependency test body was changed in the merge. These are overlapping consumer integrations,
|
||||
not rewrites of dependency commits. Every prerequisite still needs its own gate (mos-1351).
|
||||
|
||||
Added library_doc_write/library_doc_delete after the five lookups, only for a creative definition
|
||||
with a within-role library.write grant and library enabled. Closed schemas carry exactly
|
||||
{universe,path,base,content} and {universe,path,base}; base is string-or-null with 40-lowercase-hex
|
||||
pattern. No project, business, author, credential, commit or message. tools.action is library.write.
|
||||
New harness/tests/library-write-tools.test.mjs pins per-role eight/ten names, both schema invariants,
|
||||
no repo/commit/business/accept, exact required/verb/action, nullable base, and missing library/grant
|
||||
or noncreative exclusion. This satisfies sage-3678 and mos-1523's replacement coverage condition.
|
||||
|
||||
Publication's narrow helper now calls indexFor for the new served commit instead of only readLibrary,
|
||||
so the284 lookup index is rebuilt before publication. New broker integration uses library.get's
|
||||
served blob as write/delete base, independently checks served:path, reads the new text/blob immediately,
|
||||
preserves default-canon null, then sees null after delete. No change to284's query behavior.
|
||||
|
||||
### Authorized baseline-test migration, own commit (mos-1523 via sage-3677)
|
||||
|
||||
File: packages/harness/tests/library-tools.test.mjs. Numstat: +4/-4, exactly.
|
||||
The noncreative LOOKUPS assertion at :33 is unchanged. Every existing lookup schema assertion stays.
|
||||
The four old/new lines are:
|
||||
|
||||
```diff
|
||||
- ? ['web_search', 'web_fetch'] : []), ...LOOKUPS];
|
||||
+ ? ['web_search', 'web_fetch'] : []), ...LOOKUPS, 'library_doc_write', 'library_doc_delete'];
|
||||
-test('M3: per-role snapshots contain six names, or eight with the C2-08 web grant', t => {
|
||||
+test('M3: per-role snapshots contain eight names, or ten with the C2-08 web grant', t => {
|
||||
- for (const [i, tool] of tools.filter(t => t.name.startsWith('library_')).entries()) {
|
||||
+ for (const [i, tool] of tools.filter(t => LOOKUPS.includes(t.name)).entries()) {
|
||||
- assert.deepEqual(tools.map(t => t.name), ['send_message', ...LOOKUPS]);
|
||||
+ assert.deepEqual(tools.map(t => t.name), ['send_message', ...LOOKUPS, 'library_doc_write', 'library_doc_delete']);
|
||||
```
|
||||
|
||||
Additional schema mutant (mos-1523): add `commit: str("caller commit")` to writeBase in tools.mjs,
|
||||
thereby adding it to either/both write schemas. The new-file schema test must fail both its exact
|
||||
property set and forbidden-property assertions. Run on CLEAR and restore. No further assertion
|
||||
change in library-tools.test.mjs is authorized or made.
|
||||
|
||||
All code/tests/mutants remain unrun. No Node, build or syntax check before Sage's named CLEAR.
|
||||
Static diff --check is clean. Scope additions stay listed above, now including indexFor import and
|
||||
call in the previously approved narrow publication helper. E5 implementation is present; review and
|
||||
acceptance are pending evidence, not claimed complete.
|
||||
|
||||
## 2026-10-23 testing and landing boundary (sage-3682)
|
||||
|
||||
Latest complete dependency2845d587a552 (test/docs only over47db04eba) merged intact as f8f79ea2f.
|
||||
Own tool implementation cd2f50dae and separate mos-1523 migration29455c0c5 are pushed; Webby reads
|
||||
E5 and Dewey checks the migration. Static manifest-r1 covered21 own paths at f8f79ea2f.
|
||||
|
||||
Sage reserves first focused runs/RED-GREEN/mutants for T7, provisionally about22:25Z, unless he
|
||||
explicitly frees an earlier lane. No Node before his CLEAR. This is a schedule, not CLEAR. Full
|
||||
library/bus/harness and shell gates still need their own authorized slot; no gate result is claimed.
|
||||
|
||||
Before the gate, incorporate landed refactor after283/224/284 land while preserving every
|
||||
prerequisite. Under the standing add-commits/no-force-push rule, update this published branch by merge
|
||||
rather than rewriting its pushed history. 288 lands last in the chain; no landing is authorized here.
|
||||
|
||||
## 2026-10-23 E5 static closure and N2 wording
|
||||
|
||||
Dewey dewey-0948: the mos-1523 migration at29455c0c5 is static CLEAN, exactly+4/-4, with the
|
||||
noncreative assertion unchanged and new-file schema coverage meeting every condition. Webby
|
||||
webby-0263: E5 closed on29455c0c5/cd2f50dae (f8f79ea2f;98ca25ab1 only BUILD.md), nothing blocking.
|
||||
Both ran nothing; Webby did not read the scratch manifest, so no manifest verification is implied.
|
||||
|
||||
Webby N2: delete inherited the write base description "or null to create". Delete now overrides only
|
||||
that description: "The existing file's blob from library_get; a missing file cannot be deleted".
|
||||
The reviewed nullable-base schema/required keys/verbs remain unchanged. The row's new schema test
|
||||
pins create wording only on write and existing-file wording without create on delete. No further
|
||||
assertion change in the baseline library-tools.test.mjs. No Node/tests run; still await Sage CLEAR.
|
||||
|
||||
## 2026-10-23 dependency283 follow-up (honker-0195)
|
||||
|
||||
Merged origin/honker/row283-build b2a1f4092 intact, fast-forward lineage from46bad7ea3. It changes
|
||||
supporting.test's helper and Honker's BUILD receipts only; no production source changed. Honker
|
||||
reports library98/98, business231/231 and14/14 mutants killed on that dependency head. Those are
|
||||
his dependency receipts, not verification of288's combined branch. No288 Node/tests run; Sage's
|
||||
CLEAR is still required. The dependency's own gate/landing remains Sage's responsibility.
|
||||
|
||||
## 2026-10-23 new candidate after283 landing (sage-3727, honker-0200)
|
||||
|
||||
Created bubba/row288-r2 from the preserved published bubba/row2887c53adf10, then rebased with
|
||||
--rebase-merges --no-update-refs onto93694c5185d0a7f989d2fe78fb620e44599b744e. This supersedes
|
||||
the earlier merge-only refresh plan for this candidate: Sage explicitly requested a fresh branch,
|
||||
not a force-push. Both local and remote bubba/row288 remain7c53adf10, unchanged.
|
||||
|
||||
Git skipped12 duplicate283 commits already replayed upstream. Range-diff of
|
||||
45dca69f6..bubba/row288 against93694c518..bubba/row288-r2 (before this receipt) has35 equal
|
||||
commits,12 dropped,0 modified,0 added. Full output: scratch/bubba-row288/rebase-r2-range-diff.txt
|
||||
under /mnt/storage. The other prerequisite commits retain their patches; row216/224 identities are
|
||||
unchanged where Git could retain them. This is not new verification of any prerequisite.
|
||||
|
||||
Replayed merges had the same launcher-footer and broker/runtime/index conflicts as before. Reused
|
||||
the exact previously reviewed combined file bytes, keeping both launcher test bodies and all lookup
|
||||
and writer paths. Static comparison before this receipt: all21 own manifest paths byte-identical
|
||||
to7c53adf10; all16 other changed paths exactly match93694c518 upstream. These include newly landed
|
||||
226 and coordination/docs changes already on that base, not new288 edits. No baseline assertion or
|
||||
production change was introduced by conflict resolution.
|
||||
|
||||
Sage's base93694c518 is an ancestor of the candidate; old283 b2a1f4092 is not. Static diff --check
|
||||
clean. No Node, test, syntax, build, mutant or gate run. Named CLEAR/T7 still pending;288 lands last.
|
||||
|
||||
## 2026-10-24 static preflight before landed284 base (sage-4282)
|
||||
|
||||
No Node, npm, test, syntax, mutant, queue command or gate ran. Still on bubba/row288-r2,
|
||||
b44152ac6 plus this static follow-up; await Sage's landed284 base before a fresh candidate branch.
|
||||
Do not treat the earlier T7 estimate as CLEAR. Webby remains reviewer.
|
||||
|
||||
### Updated C2-02 brief and contract names (webby-0282 N1)
|
||||
|
||||
Read C2-02 on origin/refactor 95d4991d20a4ef291c824d37c886eaeaa9be7062; current brief blob 85e6fe2ecfc53ed615b7b61aade88ae0cbb222ef
|
||||
(the new ruling is identified by Sage as5e8a720b). Statically checked both the candidate and current
|
||||
refactor versions of all six creative contracts: roles/companion.md, research.md, ideas.md,
|
||||
proofreader.md, fact-checker.md and continuity.md. Every one names library_doc_write and
|
||||
library_doc_delete, exactly the two add() names in candidate harness/src/tools.mjs. No role contract
|
||||
change is needed or made. Exact-name evidence is tool-contract-audit.json in the scratch dir below.
|
||||
|
||||
### mos-1584 fixture audit
|
||||
|
||||
The new row288 harness tests reach businessDoc through resolvedFor, but their callbacks add only a
|
||||
creative role; they add no library business project. library:true is the typedTools option, not a
|
||||
businessDoc project. The broker/runtime/bare-library tests build minimal business objects directly,
|
||||
not businessDoc. The migrated baseline harness test likewise adds no library business project.
|
||||
There is therefore no new row288 businessDoc-with-library fixture requiring a chief-reference edit.
|
||||
The current and future landed-base imported fixtures must be rechecked after the pending rebase;
|
||||
any validating businessDoc fixture that adds a library project must retain/provide chief gitea.
|
||||
|
||||
Shortened only two row288-authored fixture credential values: write-fixture.mjs uses fx288-write,
|
||||
and the runtime test's two slots use fx288-human. Both are11 characters, below24. This corrects the
|
||||
25-character runtime value and avoids an edge at the older23-character write value. No assertion
|
||||
changed, no baseline test edit and no credential file was read. No live credential action occurred.
|
||||
|
||||
### mos-1612 whole-file run-list preparation (not run authorization)
|
||||
|
||||
Static lexer traversed real import/re-export/require and new-URL module-entry edges, skipping
|
||||
comments/quoted fixture code; reverse closures carry a chain witness for every selected test.
|
||||
Used the union of this older candidate and the current refactor source snapshots so newer importers
|
||||
are visible without prematurely rebasing. It selected270 whole test files. Required whole library,
|
||||
bus and harness suites add17 distinct files, making a proposed union of287 (not a narrow grep list):
|
||||
bus115, business2, cli58, harness16, library13, plugins1, processing-control1, seat5, tasks9, webui67.
|
||||
The brief also names all12 scripts/test-*.sh runners. No file/runner was executed.
|
||||
|
||||
packages/cli/tests/case1-accept.test.mjs is explicitly excluded under mos-1592/1612. The11
|
||||
nonliteral-import files have a manual expression audit; all already occur in the import-selected
|
||||
whole-file set. Generated programs and runtime plugin discovery are recorded static limitations,
|
||||
not asserted fully resolved by the lexer. Whole suites and the named script runners retain their
|
||||
brief-required coverage. Regenerate after landed284 rebase; report exact final sha/list before CLEAR.
|
||||
Each approved gate later needs own root/webui npm ci, required editor build, short scratch TMPDIR,
|
||||
whole files and gate skip receipts under the then-current rules. These are plans, not past evidence.
|
||||
|
||||
Artifacts: /mnt/storage/scratch/bubba-row288/static-2026-10-24/
|
||||
- import-run-list.py SHA256 ec18031c87bf6f96b3e794e2e56747bc5e1fe37580de7ce9444c60cd5f18ddc0
|
||||
- whole-files.txt SHA256 b2d6c11349268c347e51a64fe6bf9e7c4d560238a8fc5b5e10f600f1e6e8a39e
|
||||
- import-witnesses.json SHA256 01702657d34c4f2c0635f38741d0a5b57cfdda63d5e96897f188d03971afd5d9
|
||||
- dynamic-import-audit.txt SHA256 42b8721fde783c618e1347f2609ad6e4ae3144f563272f0bbbeb45c34f751018
|
||||
- brief-required-whole-files.txt SHA256 254828414cd9b1b0a785df5c23b7f2b9ea7564d1f85e648899801e46194d135f
|
||||
- proposed-whole-files.txt SHA256 bee666dce2c3f2d0bafe7e963fb443ad356b6ae8719ac3cb8ee413a329578035
|
||||
- brief-script-runners.txt SHA256 02cd25af486c163189977106e055575145bffba688c7836e2a508f5ec91e76d4
|
||||
- tool-contract-audit.json SHA256 bf07865d796b8b0aed85d84c015430ea03468c5331bcaae9eb80efafe1a182be
|
||||
- fixture-audit.json SHA256 436a69a6b8548d8426390e867ed4b0f83693a3116d825c26c6f8a3e788f477d6
|
||||
|
||||
## 2026-10-25 landed-base transplant (sage-4338), UNTESTED
|
||||
|
||||
Named base: 78b69e1e68b2b8a9dc4c02564381a0792c0c7549, including landed284 ce0b6ed41.
|
||||
New branch bubba/row288-r3-landed; published r2 e7c31a90d is untouched.
|
||||
The initial --rebase-merges replay tried to reapply already-landed285 and conflicted;
|
||||
aborted without resolving those conflicts. Transplanted only the twelve Bubba nonmerge
|
||||
commits, in order, onto the named base using cherry-pick. No dependency commits replayed.
|
||||
|
||||
Conflict resolutions (no test conflicts, no assertion changes):
|
||||
- BUILD-LOG.md and docs/SESSIONS.md: preserve the entire upstream side, append Bubba entries.
|
||||
- broker.mjs: retain landed LIBRARY_LOOKUPS and lookup dispatch, add writer verbs/methods;
|
||||
retain the complete landed library.status replyLimits branch after writer/save/revert dispatch.
|
||||
- server.mjs: retain landed refusal envelope including reason, combine project/library save detection.
|
||||
- library/index.mjs: retain every landed export, append the writer exports.
|
||||
- webui cli.mjs and console-bus.mjs: preserve landed libraryRevert and add librarySave alongside it.
|
||||
- runtime.mjs (notice follow-up): retain landed lookup adapter, add metadata-only log sink.
|
||||
All other changes merged automatically. No business role/vocabulary file or dependency packet changed.
|
||||
Baseline test migration remains packages/harness/tests/library-tools.test.mjs +4/-4 (mos-1523).
|
||||
Only additional own-test edit removes a trailing blank EOF line in write-fixture.mjs.
|
||||
|
||||
Line-reference recheck: the sole explicit source-line citation in this append-only packet,
|
||||
noncreative LOOKUPS :33, is still exactly line33 of library-tools.test.mjs and unchanged.
|
||||
Historical fired-chief path4308 described an older broker; current fire.stopped/fire.done are
|
||||
broker.mjs5126/5147 (firing detection863-865). Those historical receipts remain historical.
|
||||
Old head/brief identifiers and T7 times above are historical, not the present base or CLEAR.
|
||||
|
||||
Final import-list audit uses candidate + exact named-base import graphs, eleven changed source
|
||||
modules as roots, and all touched tests; no text grep selects a test. 272 whole files selected,
|
||||
plus17 brief-required library/bus/harness files =289 files. Package counts: bus116, business2,
|
||||
cli59, harness16, library13, plugins1, processing-control1, seat5, tasks9, webui67.
|
||||
All12 current scripts/test-*.sh runners retained. case1-accept.test.mjs excluded explicitly.
|
||||
The eleven nonliteral import expression sites were rechecked; whole parent tests already selected.
|
||||
Generated programs/runtime plugin discovery remain documented static limitations.
|
||||
Artifacts and chain witnesses: /mnt/storage/scratch/bubba-row288/static-r3/.
|
||||
No Node, npm, test, syntax check, build, mutant or gate run. Pending Sage CLEAR.
|
||||
|
||||
## 2026-10-25 first CLEAR and delete-path RED/GREEN (sage-4396/4398)
|
||||
|
||||
Own root npm ci rc0; own webui npm ci rc0; editor build rc0. Fresh TMPDIR from mktemp
|
||||
under /mnt/storage/scratch/tmp. All Node whole-file runs nice19, timeout600 kill-after10.
|
||||
Initial own write.test.mjs rc1,10 pass/2 fail: delete and revert-to-absent hit library-git.
|
||||
Isolated bare Git confirms update-index --force-remove rc128 (requires worktree), while
|
||||
--index-info rc0. First replacement and two delimiter-investigation reruns still rc1,
|
||||
10/2: runGit's stdin argument names an input FILE, not input text. No pre-existing test
|
||||
ran or was edited. Final correction writes a private NUL-delimited removal record in the
|
||||
already-cleaned staging dir and supplies that file to update-index -z --index-info.
|
||||
Whole write.test.mjs now rc0,12/12,0 skipped/cancelled. Source-only fix in owned write.mjs.
|
||||
Logs remain /mnt/storage/scratch/bubba-row288/gate-r3/ (write-fixed through write-fixed4).
|
||||
Sage4398 authorizes this fix and requires committed new candidate + driver restart from
|
||||
first whole file. Focused green is not the complete gate; remaining acceptance pending.
|
||||
|
||||
## 2026-10-25 new broker-fixture RED/GREEN, driver restart pending
|
||||
|
||||
Candidate7932c979a driver first library file rc0,12/12; second own bus library-write rc1,
|
||||
0/14 (fixture FK and undefined dataRoot). Own fixture lacked gitea_users parent; insert
|
||||
with valid16-hex nonce, consistent with landed credential tests. First attempted fixture
|
||||
nonce was invalid and INSERT OR IGNORE hid it; corrected nonce, no product/schema changes.
|
||||
Every bare.dataRoot reference was undefined: use bare.context.dataRoot. After these fixes,
|
||||
whole file rc1,13/14, last assertion assumed lines were strings; query returns [line,text].
|
||||
Corrected this row's new assertion to exact [1,'new served text']; whole file rc0,14/14,
|
||||
0 skipped/cancelled. Intermediate logs bus-fixture-fixed{,2,3} under gate-r4 retained.
|
||||
Only owned new bus/tests/library-write.test.mjs changes (+14/-12); no pre288 test changed
|
||||
or failed. Commit this fixture correction, then restart all289 whole files from the start.
|
||||
|
||||
## 2026-10-25 creative typed-tool fixture RED/GREEN
|
||||
|
||||
Candidate4fb11e74a restart: library write12/12 rc0; bus write14/14 rc0; own harness
|
||||
library-write-tools rc1,0/3 before assertions: new creative role fixture omitted the
|
||||
Gitea credential reference required by landed285. Added the same fixture reference copy
|
||||
as landed library-tools.test.mjs (no credential file read); no assertion changes. Whole
|
||||
own harness file now3/3 rc0. Only new owned test+BUILD changed. No pre288 test failed.
|
||||
Commit and restart driver from first whole file again; gate still not claimed green.
|
||||
|
||||
## 2026-10-25 completed candidate gate (sage-4396), SLOT RETURNED
|
||||
|
||||
Tested candidate2848d832e (three additive fix commits after requested8a47c462a).
|
||||
Driver restarted from first whole file after EACH fix. Positive gate-r6: all289 whole
|
||||
files rc0,2604 pass/0 fail/0 cancelled/1 deliberate FED R7 skip. Full required suites:
|
||||
library168/168 (13 files), bus1035/1035 (116), harness180/180 (16). Other selected:
|
||||
business25/25, cli619 pass/1 skip, plugins8/8, processing-control7/7, seat69/69,
|
||||
tasks95/95, webui398/398. Case1 acceptance excluded. Final import graph regenerated on
|
||||
2848d832e in static-r6: whole-files.txt byte-identical to static-r3 selection.
|
||||
|
||||
All12 scripts rc0: test-auth,conductor,config,discord,extension-package,
|
||||
federation-acceptance,foundation,land-work,push-check,queue,release,task.
|
||||
FED R7 dedicated runner covers ordinary-suite skip. Queue wrapper28/0 and node156/156;
|
||||
canonical-root verify/render check deliberately skipped in this linked worktree.
|
||||
Root/webui own npm ci and editor build rc0 before runs. All tests nice19, sequential,
|
||||
whole-file timeout600/kill-after10; scripts timeout1800/kill-after10. Fresh mktemp TMPDIR
|
||||
/mnt/storage/scratch/tmp/ for each restarted driver; no /tmp test root.
|
||||
|
||||
Six mutants killed (each rc1), restored positive whole files each rc0:
|
||||
- M1 supporting checks removed: broker M12 refusal assertion.
|
||||
- M2 forced push refspec: unrelated-blob retry/new-parent and guard assertions.
|
||||
- M3 forged owner token: missing expected rejection in exact-token test.
|
||||
- M4 beforePush grant recheck removed: held-during-fetch expected rejection.
|
||||
- M5 notice after publication: E4 exact notice and failed-store assertions.
|
||||
- M6 caller commit in schema: exact property-set schema assertion.
|
||||
Source SHA256 restored after every mutant; git status clean afterward.
|
||||
Captured logs across all four run attempts scanned for both fixture credentials:0 hits.
|
||||
No pre288 test failed; no pre288 assertion changed beyond approved mos-1523 +4/-4.
|
||||
|
||||
Per-run rc and log hashes: gate-r6-results.tsv; counts: gate-r6-summary.json in this
|
||||
packet. Full logs, mutant diffs, drivers and JSONL receipts under
|
||||
/mnt/storage/scratch/bubba-row288/gate-r6/. Earlier RED/intermediate logs retained in r3-r5.
|
||||
Slot-returned report sent to Sage as bubba-0034; no further tests after slot return.
|
||||
|
||||
Review notes to carry into request (webby-0296): N1 baseline library-tools.test.mjs73
|
||||
still says tool names unchanged while the pin gains writer names; retained to honor the
|
||||
narrow +4/-4 permission. N2 librarySave is dispatch plumbing only; C2-11b owns the route
|
||||
that consumes it. N3 sync comment omission is cosmetic and unchanged from reviewed r2.
|
||||
Commit-count correction: r3 initial head8a47c462a had13 commits, twelve transplants plus
|
||||
receipt (BUILD +36, SESSIONS +2, fixture EOF -1). Current tested head adds three fix commits.
|
||||
Formal manifest review/gate acceptance and landing remain Sage/Webby stages, not claimed.
|
||||
@@ -0,0 +1,308 @@
|
||||
kind name rc restored_rc log_sha256
|
||||
whole-file packages/library/tests/write.test.mjs 0 e0f6a09b70316beebbf33966b35bfd05b4a12b587744dadc9239bd28de1b976c
|
||||
whole-file packages/bus/tests/library-write.test.mjs 0 e0e9e609b20ea0564a7611046026cbdfd007832e0eb13507798e958d8d106ef0
|
||||
whole-file packages/harness/tests/library-write-tools.test.mjs 0 c8a3a493d1d2abce785e94fdda5b1732e8d4a5e6eb19767af1b7e8b5e560cf3b
|
||||
whole-file packages/webui/tests/library-write-options.test.mjs 0 2491eb6a01a0b4b2127bcb6a87a5dc778d079b7d1ea445509a1dffc49dea9051
|
||||
whole-file packages/bus/tests/access-revoke.test.mjs 0 a1b317bb687e8c369440f5481b4ef309615afb5a5922fa306980d2024019e544
|
||||
whole-file packages/bus/tests/account-migration.test.mjs 0 e697b58a1bb9b37c609b441fe786e557c7f0279e7a31eea791483ce4567db318
|
||||
whole-file packages/bus/tests/address.test.mjs 0 977f755d29a8e75578d0bef48ec40b7045736014b2e53e15a03cdb6dd39696d8
|
||||
whole-file packages/bus/tests/agents.test.mjs 0 3e8116bad1ab5594e627ba3e9f5403414885959935bcd9e6f75f1af4c625d4b0
|
||||
whole-file packages/bus/tests/approval-binding.test.mjs 0 d2fbee516b57857f3a6562427d2a08bd7fdb772aacabd33ebd9d2990f01ec7fa
|
||||
whole-file packages/bus/tests/broker.test.mjs 0 400e7f0616bc7432d8cb2a9efbae3c84d5b44ef59ce7f8fbebd4c802e2a9f44e
|
||||
whole-file packages/bus/tests/business.test.mjs 0 f42502884a158ed1d190b37b3884e19d44547ececf068f91765b8ad15faffba3
|
||||
whole-file packages/bus/tests/channel-proof.test.mjs 0 3984a7e2da2a60e60d0309f719a45a3ef04bca767bac34e00a760e19e941d732
|
||||
whole-file packages/bus/tests/chief-of-staff.test.mjs 0 8dd57acf0101d4c92ada92f4c33f68061d4e2cb430179a8ab99a0b7fc9a19997
|
||||
whole-file packages/bus/tests/citation-availability.test.mjs 0 d34d84d44b5949024ce6b32708c68e10b00a84600bb03598bf9538b358750455
|
||||
whole-file packages/bus/tests/citation-guard.test.mjs 0 acab66e65fd41f1fbfd9524f80456553fc033d8bfc18a9bec7a813bd0d4552d8
|
||||
whole-file packages/bus/tests/citation-runtime.test.mjs 0 ce33310d333c7a0af7451c3a3b610f5178961b80b6b63a839909b67657fd161e
|
||||
whole-file packages/bus/tests/comment-cursors.test.mjs 0 cd4de90a070faf2bdb776a96a6ba43aa4854f6f5f1b2b7459740bd7bdc0ffb7e
|
||||
whole-file packages/bus/tests/comment-migration.test.mjs 0 773659855873805b5bd950101d950945213658fde42d8012211e742ba9ddcc09
|
||||
whole-file packages/bus/tests/console-decide.test.mjs 0 275fc0a24a03f2f27210a65e2bd21ce615ec9b6b400c4e4177ddb97a9e8e85be
|
||||
whole-file packages/bus/tests/creative.test.mjs 0 eb950d5680ff7e4e09a9c11b1c169d4a740011ea72bb75921d35571ef5373df2
|
||||
whole-file packages/bus/tests/credential-claim.test.mjs 0 b2d40e4d2a353f4154af4974d5779f7b20c652a8110da3183f95959ae2540941
|
||||
whole-file packages/bus/tests/credential-status.test.mjs 0 ed367e3d123c9629b7d3d22f6fa60f48c74e71b36982c54ad2b3e8419f515431
|
||||
whole-file packages/bus/tests/credential-store.test.mjs 0 0fd528ba27757393973cba5b21da573bc681659416962bf7d3a3dff3018414af
|
||||
whole-file packages/bus/tests/credential-verbs.test.mjs 0 154b123af420abf08c6ab8b7bc6439052e7ab65c772abb0caac5e9b9cd3608e9
|
||||
whole-file packages/bus/tests/credentials.test.mjs 0 6474b45c076f474a6881d0dddd0cd3b4cd385d52ac4c55ef1a9f8c10e0028277
|
||||
whole-file packages/bus/tests/csuite-messaging.test.mjs 0 c0229df65240be8ad014d83def48dbd64a952b701f46e40ff01efdebb5644d6a
|
||||
whole-file packages/bus/tests/daily-quota-runtime.test.mjs 0 62d686aa1852088af569cd3b921194db6873311f2ba04e55b15a93900b1615cd
|
||||
whole-file packages/bus/tests/decision-close.test.mjs 0 9599744860a4e6e330ce50b8e93c478546edce5545a9171bed068743a266ae5c
|
||||
whole-file packages/bus/tests/decision-route.test.mjs 0 2b2a9570dab83b1d454f56c1311cb41103a14bcff49e207cbb4ac5d4c73d6f0f
|
||||
whole-file packages/bus/tests/decisions.test.mjs 0 a753df7d939155adf13c7177926728b2760cf61770117dd1655f29fe2c88a26f
|
||||
whole-file packages/bus/tests/end-launch.test.mjs 0 ff7a27c35e35be424cbefa799e219ffc073c494214642b4ce56ceed6b359335e
|
||||
whole-file packages/bus/tests/fire-revokes-grants.test.mjs 0 8e6bee9421480af8a1326d65903f258d5ea0a79df6188bf18301f32a5260547e
|
||||
whole-file packages/bus/tests/gitea-grant-docker.test.mjs 0 8b639ee86d1a7d2ef267e1d36264f4ce1b5beed2c8ce084c39c0de1d699ca8a0
|
||||
whole-file packages/bus/tests/gitea-grant.test.mjs 0 8fa39b3e58cfac4ec97f02ff876fde1a40a1894c826306c0b77d45c73586f753
|
||||
whole-file packages/bus/tests/gitea-migration.test.mjs 0 689fb43a38718f5f5d00ffb3b6d998d5d454854d5e77a936b56d7c4192ee12e9
|
||||
whole-file packages/bus/tests/grant-enroll.test.mjs 0 a8be59931d37a5c787c2087d29d766d6b701bbe34f8343134148292cd29d345c
|
||||
whole-file packages/bus/tests/grant-executor.test.mjs 0 0a7f5be29b28140c9f8e7f67c6a897eb455677fba4459952a83982f2b23c87e0
|
||||
whole-file packages/bus/tests/grant-host.test.mjs 0 54c4ed722116914221e9320fd41723e32aad59284e98d8e177130b0f4c4fe6c4
|
||||
whole-file packages/bus/tests/grant-migration.test.mjs 0 7cfaa3f24d1dc070d7bdb8de76b08fabcdb6877d3367c59f7601e08f683d658b
|
||||
whole-file packages/bus/tests/grant-scope-runtime.test.mjs 0 f8bccf5324f1e10282dfb4f4fd0f63c76c8e9481ae6cc0845a1d9028e3d9633e
|
||||
whole-file packages/bus/tests/grant-scope.test.mjs 0 e30a8ced066ad214a6f70dd4a39c75bacf296032a3201e4605fa3c8df23e0159
|
||||
whole-file packages/bus/tests/hard-waits.test.mjs 0 78d92a14781286d3ca11989786a8698a2b796ad1db78769fd5b3c7653ad7639e
|
||||
whole-file packages/bus/tests/host-run.test.mjs 0 51d231611940444d2c2a37fbb7b14267c705c1b5e453b02367d0a6def214dc93
|
||||
whole-file packages/bus/tests/host-tools.test.mjs 0 3bc5b5413661edd2026bca1721055ec7dce8d115918cc10c8f334e527176d23b
|
||||
whole-file packages/bus/tests/human.test.mjs 0 5c8f3fd402ae4e130029b3203ed41b3cb45b8781417d31dec1f47ebddd516349
|
||||
whole-file packages/bus/tests/instances-verb.test.mjs 0 940ca3884930f539272a2c39ca7e1c7fa3e51b3a4ea6fdcfb21719c652152257
|
||||
whole-file packages/bus/tests/interpreter.test.mjs 0 d0c6190a428005b7750b59e665968657b242b322af05ca7bdd4cd7e815b7f4ab
|
||||
whole-file packages/bus/tests/land.test.mjs 0 e9ecd1d8011ab6789fc3254bb069e76631b68b20ad65ae844ee12e0dc3b97cb0
|
||||
whole-file packages/bus/tests/launch-notice.test.mjs 0 750a54d4c40559c7fb5414071bace8056d6bfa71dfdbec4de12c94c90299c3c5
|
||||
whole-file packages/bus/tests/launcher-released.test.mjs 0 4d59486ca3077a4b2f51141ab79df635feb6b7bca2d6c0cfe5839a6a93715de6
|
||||
whole-file packages/bus/tests/library-credential.test.mjs 0 db7e2dc4afe23f74c49795e4dcd3d28414435519029a837641cbb35bd3f4fcef
|
||||
whole-file packages/bus/tests/library-lookups.test.mjs 0 fa18b5d4c829ec35ebbf519c4346a2ac38cff7538d58ea663acbbdd7930a6015
|
||||
whole-file packages/bus/tests/library-sync.test.mjs 0 ee0ab1ad2549d3e6ae6d8123fa99d697d9be6c629e09131da82f0ab6d58a60e2
|
||||
whole-file packages/bus/tests/library.test.mjs 0 62b663034a5de8d6d1abfaf3f94327d7aae435e3350874b424eefdb28bca1bd1
|
||||
whole-file packages/bus/tests/lifecycle-revoke.test.mjs 0 c0810c743b5108773b6cfbc2d108fa09ea274c437433355277ce2b1295fbf692
|
||||
whole-file packages/bus/tests/lifecycle-verbs.test.mjs 0 926bcfb20ab58a4ecbe85b4f8feb915f72adcdc6d6aee84f814187d0ac998efd
|
||||
whole-file packages/bus/tests/limit-checks.test.mjs 0 919d745dc570172716b1e06d0ea1e8f16ae7b05dbadba77fe92dbbcc8eb4cba7
|
||||
whole-file packages/bus/tests/limit-stop.test.mjs 0 2fc1ebee4e4d4862a100ed9ba97ace1e6d3fb3c608abea63b1ff634a04d5649a
|
||||
whole-file packages/bus/tests/limits-runtime.test.mjs 0 5845176b73f98a499d92096ce0d22a3987ada664a529bdbb26230c2d00b9e65c
|
||||
whole-file packages/bus/tests/limits.test.mjs 0 333f6757d9d00eda74e7d4641af660c9a0ce3d978e6a8550139d3a461c1d5866
|
||||
whole-file packages/bus/tests/message-citation.test.mjs 0 38422c1d6faf076c750c3fccb7e927e38fe955cc28778511acccf5704df5b191
|
||||
whole-file packages/bus/tests/message-filter.test.mjs 0 2ba52728df27756ac19ba65fe0705e7cf7a2a786a3b4df77a4eb4eec8072604c
|
||||
whole-file packages/bus/tests/messages.test.mjs 0 b87de4ab04682d59a2faa7b3fc64751247454e628e9c1ebd4c07b1df2acbf11c
|
||||
whole-file packages/bus/tests/messaging.test.mjs 0 831b48a9de594e17e8fc47f16ab8c2d722e8d57068a7d39deeaf4c0fa8d136bb
|
||||
whole-file packages/bus/tests/model-proxy.test.mjs 0 99c5128733ae5bb16489171513c158ec1e34dfce1d07c60f6bccb0f84ab7afb9
|
||||
whole-file packages/bus/tests/name-migration.test.mjs 0 bb8f92a17f6a022c7ec63161cccc0bb49a26c2a591fc369375cbebfa93a277a2
|
||||
whole-file packages/bus/tests/names.test.mjs 0 0fcf60cc8c7dee3e85548c759f03e1792de92b59131bb9fe4282a3b07859366c
|
||||
whole-file packages/bus/tests/peer-transport.test.mjs 0 3cb547003e50695ec2a25c950819b304f15203302281c00758a12c9b89b60f40
|
||||
whole-file packages/bus/tests/peercred.test.mjs 0 e75c700a68ba0c1013c8f36178d6554f0321dc331b2198fd5ffc1a684df76c3e
|
||||
whole-file packages/bus/tests/plugins-verb.test.mjs 0 359fef350befd1d743bcca89a96ebd0757346fd7c0622ba364039ba827620d26
|
||||
whole-file packages/bus/tests/principal.test.mjs 0 732724e6bb2bb953e7dbbbae5c23cc0a1c33a7b7b202f5cb8b46be9363bcb392
|
||||
whole-file packages/bus/tests/process.test.mjs 0 9c5883ea2ca858131317fa9be66d2a51a5d86dbe03c515b0544f4db56b2456a1
|
||||
whole-file packages/bus/tests/processing-runtime.test.mjs 0 bb6b94ac654d069abd6e484031893e649e239d933c135b226d1cb2fbd5a94ca9
|
||||
whole-file packages/bus/tests/processing-timeout.test.mjs 0 323036aa7f32f7aa324f61595531a29f3d5ef0ae2c24aec029842f2783d6be76
|
||||
whole-file packages/bus/tests/processing-web.test.mjs 0 dbd1543498fbc8d0625990436ae668b3a382eedeecd098b929c13d2bb61939ff
|
||||
whole-file packages/bus/tests/processing.test.mjs 0 529ec93e767444970ff03acb1ba719b45033daac239256c9cbd587c39f926a71
|
||||
whole-file packages/bus/tests/project-await.test.mjs 0 e948d721ef06ce95f909880942f7e7ebd73b2f42563e1faedacc7b403b8ebedf
|
||||
whole-file packages/bus/tests/project-files.test.mjs 0 3691b04705c17d4ef7218bc15771560aca5586c00ba0fd3fdf2a80f63d8dc659
|
||||
whole-file packages/bus/tests/project-runtime.test.mjs 0 9d8d6cedeb2044f19e548dda0e32cae1e997b75a6d89072f7367b6e0ce17a44f
|
||||
whole-file packages/bus/tests/publish.test.mjs 0 87f7a382580432646ca89d1228377719cb177bfdbb4f74ad617f6a70dbfb4b33
|
||||
whole-file packages/bus/tests/reload.test.mjs 0 25aa45aca6ba24d72e9e5ad66dfc8c049375c8df121dd748dde09af52f16f853
|
||||
whole-file packages/bus/tests/replies.test.mjs 0 0f05dc2194896540a1488310107e31b7e779cc02c381c201d0c95f69ab39fc51
|
||||
whole-file packages/bus/tests/requirements.test.mjs 0 29569dc30659dbecf359117cc9a59ff37927c60f5514b8ceb8d3796c2c3e53d1
|
||||
whole-file packages/bus/tests/review.test.mjs 0 a8daea8602be9429a8044ad636a438c18eee8ec77b6fa272dce606116d01de8c
|
||||
whole-file packages/bus/tests/revoke-window.test.mjs 0 1435c543c9367204f4ae6d25c2cb97145e762cb18e78ea90a15eb1672a220353
|
||||
whole-file packages/bus/tests/room-check.test.mjs 0 bbe5ac7abfb7e1b30f6fc8f9a247cd28aca82a8783753434339b790dd9857e1c
|
||||
whole-file packages/bus/tests/rooms.test.mjs 0 506fad116ab1d9bb2520fe691adc91ca26e64faa96ced35c193cb11da4e07ccb
|
||||
whole-file packages/bus/tests/routes-verb.test.mjs 0 c09fef3cb022bf7713cde4e1c512feefe67090eff05451fda9f55272140c916e
|
||||
whole-file packages/bus/tests/schema.test.mjs 0 2a1a668dc408aaebe6f604ec5c3b8858211edc52930e1a552173cecedf806d72
|
||||
whole-file packages/bus/tests/session-clear.test.mjs 0 1fce03f1d2a41f5ba294e9d406980a1d3251bc9a5dd4a02897413d6be3d9681c
|
||||
whole-file packages/bus/tests/session-compactions.test.mjs 0 745510273a9eaaf61de4c6453c5dcc70e4a6020c0bcec69673621eefa593dab7
|
||||
whole-file packages/bus/tests/sessions.test.mjs 0 d6c0337e0981ecb9aae2e912c27145bbd831e196828b2d59029666ceae5f605c
|
||||
whole-file packages/bus/tests/single-use.test.mjs 0 6f5cf7d47716ce94eccb069f9b7e5625c3587737921231649fc74e8bfd3aad3c
|
||||
whole-file packages/bus/tests/spec-migration.test.mjs 0 ede4ca62a772261bc868f4dfcb4861c9c3670110ba2afd39cc44183398c2eafd
|
||||
whole-file packages/bus/tests/spec-review.test.mjs 0 951aa2a54cfe4bf0960dd3d2180874cb8183ff9b0aee7b7251b05df11385e1a5
|
||||
whole-file packages/bus/tests/spec-socket.test.mjs 0 65735a2557e19b3d63a3ce7198c80fb5059f1974acd0708a89f192857b43b095
|
||||
whole-file packages/bus/tests/spec.test.mjs 0 488ab443b41b06b6ae4434f5b2ee8904169aa08ab3527f3ae4c3f438e54f9909
|
||||
whole-file packages/bus/tests/staff-request.test.mjs 0 edeb4ceffae3af33e330e1fac5d74c4fb1861d52ec5ac5005689791ab74da555
|
||||
whole-file packages/bus/tests/state.test.mjs 0 3b5f916c494563fc7c18f89450e0cecbbdad8e24680b373e897253b9ea6064fe
|
||||
whole-file packages/bus/tests/store.test.mjs 0 39d0a4033b89eff9b29ea6dcf6e56696a1d20a41e0e14b4bc8efbfae7962ecf4
|
||||
whole-file packages/bus/tests/task-page.test.mjs 0 6446cfc1a43cb4206b4130f316251d3d9a820f929a6efc59dc74e7cbe1fbd258
|
||||
whole-file packages/bus/tests/task-read.test.mjs 0 37134ced43ad5ada4291ad5e3474dcf7063892443b06d54a7456cd4cddb9cf2f
|
||||
whole-file packages/bus/tests/tasks.test.mjs 0 9d91ee510a7447545c2cf93ae6779544a05e01bb70346e595b91a4800fa42d7b
|
||||
whole-file packages/bus/tests/tester-threads.test.mjs 0 ff843610e02f20c5e1ab19de27b131ae7cbc3fd87b16d85e68336b5e914ffd77
|
||||
whole-file packages/bus/tests/tester-writes.test.mjs 0 517c41e666d2c59b7ffb6ddb22898ef58b816bb26eaa47e1ce7508087b607df5
|
||||
whole-file packages/bus/tests/tester.test.mjs 0 19eff6ddb3aca05ad5c006ac15e7860311f56941055850e86b8075cd39eff0d1
|
||||
whole-file packages/bus/tests/thread-claims.test.mjs 0 ee146e984bb3ae82ad60f90c83035053ff2966ffea91015b6c3c4f93b7e019bd
|
||||
whole-file packages/bus/tests/thread-migration.test.mjs 0 1dc2ae4e920d40a6fc2b1033f43ac0a2f766bfb86f07a596a983b9c1bccf87d5
|
||||
whole-file packages/bus/tests/thread-shares.test.mjs 0 3f99a82e9e4a43fa788ebb231c5e712969809fa83b6ca66f04b8cad13f411235
|
||||
whole-file packages/bus/tests/threads.test.mjs 0 2e5ba2be69a34c71f41a819c7a5e8615fcd085f847f9aa6d16095b9c8d742552
|
||||
whole-file packages/bus/tests/transport.test.mjs 0 c9d28b9acfc0c2213838df0d960506156ef6fffec24a0cee7336324f105fac0e
|
||||
whole-file packages/bus/tests/usage-broker.test.mjs 0 26fc080c6ffcb7c64cca39a07f302b59c21d1425ca3c90120eb3d3bb751c49da
|
||||
whole-file packages/bus/tests/usage-migration.test.mjs 0 cfc65c4233aedb325293170e782ccb83fcfdd85cc4afed1e913e91785fa4b7cf
|
||||
whole-file packages/bus/tests/usage-runtime.test.mjs 0 faa5fce30cbd7002137acfca3cde68b49b74321fb27b8a190736b35601430866
|
||||
whole-file packages/bus/tests/usage.test.mjs 0 d36d21e0b3f5c9621b50e8b71ff6d8daa341f5954f3612b7cc93cb6c734215c0
|
||||
whole-file packages/bus/tests/user-principal.test.mjs 0 1b56d881de2975ec4e3bc27fa15bc495ae0600709457da579337f0c9c98651e5
|
||||
whole-file packages/bus/tests/viewer.test.mjs 0 942ba4fd96c3bb4c527eb129e69ddb5cb7660915e8458cfee47661d268db0e24
|
||||
whole-file packages/bus/tests/web.test.mjs 0 048e3ef2840934f95d7e00de6502b19342215cfe5d00d1b8ada7c1bc6e87cfa1
|
||||
whole-file packages/bus/tests/workflow-roles.test.mjs 0 1bed1351ec3c50ddce65f9b8108f93a5c4061ef5cf36f3d09706d85b13e085ed
|
||||
whole-file packages/business/tests/case1-business-file.test.mjs 0 4481abbce73eae119fbedc5edf65120fa494931913aac6f0273b04263c251cc9
|
||||
whole-file packages/business/tests/launch-settings.test.mjs 0 540f5170ddc7e79a8abd9584c4586eade3ecc1f7544489060db99552c75e4685
|
||||
whole-file packages/cli/tests/access-boot.test.mjs 0 e41e1f1949d3568f8845234d573282c18a185578389d6947140cf540d7f99dc8
|
||||
whole-file packages/cli/tests/access.test.mjs 0 9e2a813092ad4b60cd89443424eb4142f217265e52ff7b769b7f16d247d5a367
|
||||
whole-file packages/cli/tests/app.test.mjs 0 5f5244ba0061dc4ffd1565c4abd3168d7cb2e6398625b69c841a54544b9a35a7
|
||||
whole-file packages/cli/tests/boot-status.test.mjs 0 ca090577e20ea46315cb377668e309571380e2323c867a8b90d0537022e681b7
|
||||
whole-file packages/cli/tests/case1-hostcheck.test.mjs 0 403e1127da5637904e8fe1c1eed5a7865c5bd5ee1c5d0fe684837a7370c41de4
|
||||
whole-file packages/cli/tests/case2-install.test.mjs 0 df8edefe13297ced929d09f01eec5c7ecf38c355d9871368cb19f2f392ae057a
|
||||
whole-file packages/cli/tests/case2-persona-import.test.mjs 0 5dc7a93c03567ec0e85e43bcafe07109881c30b2e83b4c4eab4631a155daece0
|
||||
whole-file packages/cli/tests/case2-unit.test.mjs 0 39b349dfd3125e74b780ce28e9ec24465a6a8c2bb9e431c819864f4eff1fdb77
|
||||
whole-file packages/cli/tests/channel-cli.test.mjs 0 9fd9c30aa604f77b7a069912cb987293af36f2f138cce850f8d26946f5ce42cd
|
||||
whole-file packages/cli/tests/channel-recovery.test.mjs 0 52c6bd980cd54ef22b9e6c78260a1e389e8177526340129ac64fbafa7f2e01ab
|
||||
whole-file packages/cli/tests/cli.test.mjs 0 59327fd89e3fb8f927210df6a2c62afe4522e59f59129b1967be2a238c498f26
|
||||
whole-file packages/cli/tests/compactions-launch.test.mjs 0 c9006b3039a6616be1816f5ce85bf68ba4fb54dadf10447e52fc92f7ed57bbe5
|
||||
whole-file packages/cli/tests/config.test.mjs 0 35e39fb0551e23f5738d0c30d66bd233f2290040885c511e526f0f0468b31cad
|
||||
whole-file packages/cli/tests/credentials.test.mjs 0 2b55cde2318a034ec4152e712863ac54f07f1b4db752c6282507d7f621bde67d
|
||||
whole-file packages/cli/tests/federation-acceptance.test.mjs 0 cca971a93a5dfd20e603b45cc2158b58ea0638e8bb7a33e6e0c5e12178ffa3df
|
||||
whole-file packages/cli/tests/follow.test.mjs 0 26a3ad0dbc124aff48a5f4908e3d8b78cb871e5560c0e389de457f58aea2dfe3
|
||||
whole-file packages/cli/tests/git-socket.test.mjs 0 10c21c34a79ebd5f89f6ed9182a492e9f42f0512a68f758c0566a763a84abeb9
|
||||
whole-file packages/cli/tests/grants-scope.test.mjs 0 37967ca8e82a89a0e88ed872fa562b5c094282cc759853b6dc31c292204bef3c
|
||||
whole-file packages/cli/tests/grants.test.mjs 0 687af745932625ea1d439a8139c760f14b4a327bf9b15c5b5b1cdbc7ea6be09f
|
||||
whole-file packages/cli/tests/host.test.mjs 0 50010e97e44392f28006bbd4127146b8aa1712ff4a016ca10864923701213dce
|
||||
whole-file packages/cli/tests/hosts.test.mjs 0 d5d507c3968c45b2aa25067d6bbb1eedd69785c0aa1bb51177c63b6ad64fcb53
|
||||
whole-file packages/cli/tests/idle-stop-race.test.mjs 0 8e6171855003b0f40921863430dc89cf20f8bbf14a9ce3c115f90486f08232b5
|
||||
whole-file packages/cli/tests/instances.test.mjs 0 c6b4d59443baa4af029af76a846a8093669a7593490f21408a2c40ad027acccf
|
||||
whole-file packages/cli/tests/launch-account.test.mjs 0 6cb63446fc8dbb6217e6d925caabb37a28bcf1ea5c27cf8d515d67c7842553b8
|
||||
whole-file packages/cli/tests/launch-limit-checks.test.mjs 0 fae971d715a90d1dbbeb3c054932a02d253e0a0d036d3b0db4e0c9edcd5ad7ef
|
||||
whole-file packages/cli/tests/launch-limits.test.mjs 0 d3f4b2c57e776bf6bbcafba6dbd9c4728918b0d81fc606dcd7d2dffefdad4001
|
||||
whole-file packages/cli/tests/launch-routes.test.mjs 0 e2bec020f96238079908c03537366c64800de0913a83f3a2877606d0266548d6
|
||||
whole-file packages/cli/tests/launcher.test.mjs 0 beafda99d34a532f3a1d6f96565b7a780e03924939b6c30b01a8169cceaa792a
|
||||
whole-file packages/cli/tests/lifecycle.test.mjs 0 014a2cea6d3f2a9f21f7fc16dc68b34740b03726612851047b877f8c1194ebe0
|
||||
whole-file packages/cli/tests/limits-tokens.test.mjs 0 6fdf9e6946dfb579cde42ec500d8cb8473a9d8318f43caecee7561a5e40fdec1
|
||||
whole-file packages/cli/tests/limits.test.mjs 0 bc2f13959d087ccaf6977c4cf1294e6fcee21f968677ab169580ce79643add18
|
||||
whole-file packages/cli/tests/mark-read.test.mjs 0 64864c54ab910488d55136b88f351771489cf744ba1c21e5c479121694dd88ab
|
||||
whole-file packages/cli/tests/model-proxy.test.mjs 0 119a5d02870a4062992efba7f44ee9dfe69ecf8a7076f121aa24a8c7edf24cf0
|
||||
whole-file packages/cli/tests/names.test.mjs 0 c2160aeb0c68855f59b5d97dc3b6101b7800028df708f5f42430ae5854eb23e2
|
||||
whole-file packages/cli/tests/notifier.test.mjs 0 18e62f48de18cc4d57916a556adeb3befb13921d5d7b036ef5a7f2bfa460cdc1
|
||||
whole-file packages/cli/tests/optional-tracker.test.mjs 0 e5adddfee06b2f1cbd2ec50caf84b5acbc58d4ff4ec4c1bc539a4535a3991827
|
||||
whole-file packages/cli/tests/other-tree.test.mjs 0 2dae57af2b8d508f2792ce37191aebf0cd28ad75f7621d4ab9f8d6daa60b748f
|
||||
whole-file packages/cli/tests/processing-cli.test.mjs 0 44b2854ba03810cdda3ccf0c6de883f953089d532bfcdc8f4bdd3faffac80fe9
|
||||
whole-file packages/cli/tests/project-name-repo.test.mjs 0 0468b9ecde6189d1398eef2e7e8043e870aa867a8a9d22b20b2ded26dc1543b5
|
||||
whole-file packages/cli/tests/project.test.mjs 0 79e648106151ca220d9535b16c416dfdcffffaed41e3c211e4325c151f0775f7
|
||||
whole-file packages/cli/tests/publish-socket.test.mjs 0 59a02b1fcc284407b06d0d2df8e6e1e5323059daa2e47c690f30a718d1e7c682
|
||||
whole-file packages/cli/tests/reply-limits.test.mjs 0 554a353d61891c3cbe8da01062025dcb0756ba4f4e7ab3fb8d7b56ceccf64437
|
||||
whole-file packages/cli/tests/revoke-window.test.mjs 0 743fdb0135e507dea5b968265907e5ac9228284b130feb0057d91e70ad561c02
|
||||
whole-file packages/cli/tests/session-clear.test.mjs 0 5abbd894dee9cfcb3464a40e5045f9062402d5ffe567972113715cc9e24c6ce3
|
||||
whole-file packages/cli/tests/session-cli.test.mjs 0 c469aed70c1ae408e176189f23a849f3acf0bfc7dc3ba1132c101a931359a462
|
||||
whole-file packages/cli/tests/session-launch.test.mjs 0 cde78f9dcee9afa243692fb0493697104425b61589bd7be6c4cbdb0f4bfbd5e5
|
||||
whole-file packages/cli/tests/stack-copy.test.mjs 0 3b92b3772c060378cd2ec0c1bf7fe6662721721a11d99d83c9debe3e3aa929c2
|
||||
whole-file packages/cli/tests/stack-instances.test.mjs 0 2839e390f564a6e58cd514ce4061ec0b741ed07b4e2b755a337439737601d97e
|
||||
whole-file packages/cli/tests/tester-http.test.mjs 0 bd9c598c84dd0be3b9873176c63e418ccc004265153be68e0cbe51c40b1ff91f
|
||||
whole-file packages/cli/tests/tester-reads.test.mjs 0 50a71ffd9093c4d51d8ad07bc9edbbd76306922f290d28194d656cd78f8528c5
|
||||
whole-file packages/cli/tests/tester-threads.test.mjs 0 cd3dd0020472c269d61934ddb32d77f6dc297090d63e819288f4038fe9edf096
|
||||
whole-file packages/cli/tests/tester.test.mjs 0 437498258b4ea5c2f2bfa8fe66114106ac10944911bd40bca944f2078b34e45f
|
||||
whole-file packages/cli/tests/thread-cli.test.mjs 0 c29897119f279434bec0248c12beb5ce97f6214b8b0bec995061000f047b1600
|
||||
whole-file packages/cli/tests/thread-launch.test.mjs 0 459ef35e2b4f9cb87151577f252282a716ddbd4985f26e9e26c01b08af13a7f2
|
||||
whole-file packages/cli/tests/thread-revoke.test.mjs 0 b8a72080a9624ce4bbc1b23b8903be1b74a8a6ced35b0b60f503d92a22d1028c
|
||||
whole-file packages/cli/tests/trackers-boot.test.mjs 0 f83d72dac4eec7369d62b495745e1fdeeb4ff1ba2e1efb376c9b583da1765a1d
|
||||
whole-file packages/cli/tests/transport.test.mjs 0 e0ae6c26b5669d4b955deffe941a3c4314be12297e72d6c099de750170688d9c
|
||||
whole-file packages/cli/tests/usage.test.mjs 0 639ebccda23e0e094d62cb0e7e5c80c403b74b284ac5189fb0d92e63387c92f8
|
||||
whole-file packages/cli/tests/verbs.test.mjs 0 43eefd242a987efe42d145d7863372a2b0a397031baec12ecd4c9e0644daff12
|
||||
whole-file packages/harness/tests/bundle.test.mjs 0 75fd5a9a6fa0b46078952ce216348e39ce481f6fc00b89f85da9ff729d5ba100
|
||||
whole-file packages/harness/tests/citation-runner.test.mjs 0 933021f114882a6c58f4071446a07d49ebc9f99c062ffe1fedc1f13b799db389
|
||||
whole-file packages/harness/tests/claude-gate.test.mjs 0 f66053597c8209c5175810e33d8b878ad74eb314f4057f5b4b0b1ac625272329
|
||||
whole-file packages/harness/tests/claude-session.test.mjs 0 9c4d8ffc00efd0da75d143bca1cd33f30f3e762e20086270d941170e80c962ed
|
||||
whole-file packages/harness/tests/creative.test.mjs 0 3433184ced3427613d645807f0757896baf52d214c3dd6a4a12c3951bbdc50ce
|
||||
whole-file packages/harness/tests/gate.test.mjs 0 991670779d9cf6f733d0060c6f1a59d122ab8e99f7e0a5f7183c9e974f1b1aaf
|
||||
whole-file packages/harness/tests/git-tools.test.mjs 0 2837565ec16cd8c4d032311675a1468bc4bc756ee0696d0e57ea35ffd56eea44
|
||||
whole-file packages/harness/tests/library-tools.test.mjs 0 05a8a5d8e8c4e7b3435d4a9b3f6292816356a99f7156ded2ef7873e1c84f8de7
|
||||
whole-file packages/harness/tests/mcp-server.test.mjs 0 6ce4c6c39b8a1ed70fbeaca410781a62d69a6412333e03640296048b799a09b9
|
||||
whole-file packages/harness/tests/pi-session.test.mjs 0 570376f798b43e82bbc1972f1d50d3915e94a8e4ca00e50076ab390df9830d7d
|
||||
whole-file packages/harness/tests/raise-domain.test.mjs 0 aa6be675614d9324c3ddc01ef40347b235adbccfe6cd73081096ae9e9742d754
|
||||
whole-file packages/harness/tests/runner.test.mjs 0 d3f8c752897e359e10a8e9d507c10ef3760a5ae37999f6f143cb222c42568437
|
||||
whole-file packages/harness/tests/skills.test.mjs 0 2f34800387f4d7f8b8d5f6758cd197f77d11034011bf7f4f79f4d74c582e3f0f
|
||||
whole-file packages/harness/tests/spec-tools.test.mjs 0 29cd1f7094266963a995f636d9575337246a9868d42a48f02231a99251261f47
|
||||
whole-file packages/harness/tests/tools.test.mjs 0 f27638f1fa38fd2d15a352f6c21f7980688d48d580f5a59dab1520fd7c30b30c
|
||||
whole-file packages/library/tests/guard.test.mjs 0 6529c2b48c252eeaf9c19487507a03cf46d952e7ffb3a41e640a54590e312df0
|
||||
whole-file packages/library/tests/index-build.test.mjs 0 4747cb8c842cdc424db727cb3b0e36a6d35f4e9c5c31c1a3bf61314897c2cf6d
|
||||
whole-file packages/library/tests/links.test.mjs 0 9b89f9275f19035c5f079b6920b248466048626c634153eba2fec165f9f6388f
|
||||
whole-file packages/library/tests/mapping.test.mjs 0 9110b98f955dd71588357858b913194ccf9e04dca9aa7c4559600cbe9b9e8e71
|
||||
whole-file packages/library/tests/mirror.test.mjs 0 cc2326da9796dbbbf5b3faa121180a7f00c43816a232a96faece7b58570f1227
|
||||
whole-file packages/library/tests/query.test.mjs 0 f1209bc132a63cba9a7312cbed7e3098126e05c792356648f1d9ec497a11aa82
|
||||
whole-file packages/library/tests/reader.test.mjs 0 3e17f34697ae65524cf7908dcc9ca1b24025bbc92989b2ba05f6e75c0f839c3b
|
||||
whole-file packages/library/tests/render.test.mjs 0 5e68091a309a07e5502b9cc500c7cdfabaa9e246bb77b0c04a0dd86bb9bfcde1
|
||||
whole-file packages/library/tests/scope.test.mjs 0 f55dec854adcd311d614b66959c5655cb3419d7ae7b06fe1f648b43cdecef32e
|
||||
whole-file packages/library/tests/supporting.test.mjs 0 2f50bbe9c942360a5becf5bb984ed5aedb12628d4578ea85c6e433ad0ad73130
|
||||
whole-file packages/library/tests/survey.test.mjs 0 72108eb8a07b990b93ca62c5fa4b55f1f89f3254ca1cf59d3de8ad194dcc3a0d
|
||||
whole-file packages/library/tests/sync.test.mjs 0 a68c69492149b6e9dc7dbf24fc434ebfd3243b2db2c7654b00107b997e99ead0
|
||||
whole-file packages/plugins/tests/harness-compactions.test.mjs 0 9eb9f67c06ae17b33198fbcdb443a0c73ca9cc0cd157cae2359a0647d2db3658
|
||||
whole-file packages/processing-control/tests/task.test.mjs 0 8db6adda6b5d5f7437e9e4cdf961134493409d7227fde02c0b0a76aeb21186f0
|
||||
whole-file packages/seat/tests/compactions-budget.test.mjs 0 1849994f45bfcccfb4ce18915887c740947a98041aa7bd66590ae7a62fa7078e
|
||||
whole-file packages/seat/tests/compactions-ledger.test.mjs 0 af689c631c9d62dee11f4400112fd10df82567c1f49d9f0d423b9d19d6f860d7
|
||||
whole-file packages/seat/tests/compactions.test.mjs 0 cf5e74e1211560ca47a2c13e8002635097f486e2ca06698795bc38263d889f3c
|
||||
whole-file packages/seat/tests/sandbox.test.mjs 0 c09d89b8e1b22db25578f62cd4a1d3b9f441d652178fb675800efb31de33a577
|
||||
whole-file packages/seat/tests/session.test.mjs 0 82171646932cddfc68f0e90ca3355f9595d32c5d228c1fdb855c06cb1d9c2feb
|
||||
whole-file packages/tasks/tests/adapter.test.mjs 0 8843067c21942ae6165a7feaced2382fd9d02d0c62bd30772191393cb5d962b8
|
||||
whole-file packages/tasks/tests/comment-restart.test.mjs 0 25aa6e953cf19ed3ae368581035b9d883ed924b41d593240e2b4ab5ba39f2982
|
||||
whole-file packages/tasks/tests/fields.test.mjs 0 e4eafd6b17a46262dddc548263da993bc3a672adca1cadcc698b6d8374f11e2d
|
||||
whole-file packages/tasks/tests/processing.test.mjs 0 0b89866cc076c43c37534dba926d492d8d334d08a6886325e3d4ac6ddbc88897
|
||||
whole-file packages/tasks/tests/shapes.test.mjs 0 0b8cc660a07e048d007993d726ae5b8e1f343cb571c18ee0284c3ba0c4b41602
|
||||
whole-file packages/tasks/tests/startup.test.mjs 0 5b44d779ab4ec86922dd35ce7c78096de02c0a0f2363cc6c2a5ee07816efc2ad
|
||||
whole-file packages/tasks/tests/sync.test.mjs 0 513ce9f6e64a3fa0179f8cc9b27db671267a43cdc44ab93f38d21982dd2b9631
|
||||
whole-file packages/tasks/tests/tree.test.mjs 0 d3c8f91f59150259b3226e9306c98efbaf2aa17b5b74cb9b376a103620f8aefe
|
||||
whole-file packages/tasks/tests/verbs.test.mjs 0 47b075d01ef38a5d0228902a6f9bcfec0b78b1a958cd071e8114139af73bca53
|
||||
whole-file packages/webui/tests/agents-browser.test.mjs 0 d3ae822399f58d2c4dfd90e054811a60663a66f9d3fbe69a76aa4ce7dc7260d0
|
||||
whole-file packages/webui/tests/agents.test.mjs 0 be18c8fed41173900617d3b646fac1ce14c07c208e7f24d6567f61f55c5c4dc2
|
||||
whole-file packages/webui/tests/assistant.test.mjs 0 056e4c2a37b7b395741ed1ba33546031a6e4ca53db843bd877b93bed764e02ad
|
||||
whole-file packages/webui/tests/board-held.test.mjs 0 01a24d222092ad46c24ea94f684a018b787dfc8d26a0f7d7fda5d9df7192e695
|
||||
whole-file packages/webui/tests/browser-edge.test.mjs 0 4d10b4fcefbc64cdf12bbfbd9b47f74810b3219f70624c35490cb23d66d3c4a1
|
||||
whole-file packages/webui/tests/browser.test.mjs 0 d8c31b7cc16d93961d65959fa9653eaba906f81da3a34e19b3da7cc922f840c3
|
||||
whole-file packages/webui/tests/bus-browser.test.mjs 0 ab1538aaabfdb02c336da61675a0d1d7f367483bc500a380e7f0ece050ab8609
|
||||
whole-file packages/webui/tests/bus.test.mjs 0 fd7acdf189fef230af80c32c4c972d9bfb1564069d7c4ed649c3a49bb6636e1e
|
||||
whole-file packages/webui/tests/channel-chat.test.mjs 0 678b8e3c9152662487fee1c52c8abcae95029b0672d5bbe26a824bbdb4a7ee0b
|
||||
whole-file packages/webui/tests/chat-browser.test.mjs 0 ec29dd03ac7950dfa90e31f72ae74e2112718f1c02ce4dce8055194a328b5f5b
|
||||
whole-file packages/webui/tests/chat-instances.test.mjs 0 72c88b1eb11397bcff524af72cfe826a276b97b58e33465c6e3334e2746e7783
|
||||
whole-file packages/webui/tests/chat-replies-browser.test.mjs 0 bea9b1f9333bc913b2f01c29a792ea4b2fdbed3ebc64b42548e06f0bd75c9fc5
|
||||
whole-file packages/webui/tests/chat-sessions-access.test.mjs 0 f2de41c7d1f6e246a8a13dc33e8662932964facc5b93d8f85ed5fca760c87fd8
|
||||
whole-file packages/webui/tests/chat.test.mjs 0 4c6564d3565b01d68f7d16937aafddbef2ec3e04744fa9d1544ac0500a386ff7
|
||||
whole-file packages/webui/tests/conversation.test.mjs 0 1c3f8811256ad82d7771a06878c3e70dc3d6f2beab82366b837780a7873ff6f1
|
||||
whole-file packages/webui/tests/decide-browser.test.mjs 0 c53e1b962df99686f9739e258b2b86fe6325544ab02509833f9b1176f1303845
|
||||
whole-file packages/webui/tests/decide-sso.test.mjs 0 f96f8085015a96e11aca2d94bfb9e75318c0c0dfa9659049ec7b7e31ad7bc6de
|
||||
whole-file packages/webui/tests/decision-http.test.mjs 0 c89b7af200edb82f9986479199f359cf6a06c870f1bcf22b538cdf3b167f1258
|
||||
whole-file packages/webui/tests/discord.test.mjs 0 1c3e6d64a370adbc6c7cb24391989e9c00d97330d393943402ff5c6a99c5f9fc
|
||||
whole-file packages/webui/tests/docs-browser.test.mjs 0 f39d989ccdc561175643583b27bc19ee76fa25dec5947baa206b0c38b41c1e2a
|
||||
whole-file packages/webui/tests/editor-browser.test.mjs 0 c807f81d596b3a2e3d2ea55e560f29e181a0354ce48676c6760844855acb0dd3
|
||||
whole-file packages/webui/tests/editor.test.mjs 0 6953a85fe791e9087d14c3bf5075de2c4a651e1a502937da3226065c529ffd2b
|
||||
whole-file packages/webui/tests/granted-user.test.mjs 0 ae3baaa0d2a1ed1ad3e9625a46a8071222dfdef4c2a70ae3ba5bf159b85dec0b
|
||||
whole-file packages/webui/tests/history-return-flow.test.mjs 0 8f9ceeb41a64ec2849383a78f4997f7383c86a01eb6cf1d16747c2eb6c7448d4
|
||||
whole-file packages/webui/tests/instances-browser.test.mjs 0 33b9cd4be552f89809e75f72c54f73dbe6459a5c53401a79a4d887e5a658c442
|
||||
whole-file packages/webui/tests/instances.test.mjs 0 904abfc06358c99b0f646c3e728027cd7bba2b27c98c767c7da95016e531277c
|
||||
whole-file packages/webui/tests/library-cli.test.mjs 0 dda07bd1b0d65058723c4d5e50f94fefb1ee103c33c8f61c13841cfedfd53f41
|
||||
whole-file packages/webui/tests/library-routes.test.mjs 0 5cd1f4c6ea99cce38ce59eee085ab31f9aa8bba39cebbaf52b355c4d8fec3098
|
||||
whole-file packages/webui/tests/library-status.test.mjs 0 0b4fb5a07a3d35fb2237401b460ddd3832a2f6fa357f6fbb06b8463251b77faa
|
||||
whole-file packages/webui/tests/limits-browser.test.mjs 0 1a55050c92be5b93e447878feafe59e017b205d114413e6de000faf784df1dad
|
||||
whole-file packages/webui/tests/limits-route.test.mjs 0 b74fe734b0ccbdde716bb52bc15791f80a0b251514a6b01d6ba59164b9639123
|
||||
whole-file packages/webui/tests/messages.test.mjs 0 19bce52278091c8d37ca48c1f6f72bc400cd9a9b3038ae9f31e35444fdab05b8
|
||||
whole-file packages/webui/tests/pinned-copy.test.mjs 0 a8c99249fda05c5be773c8d2231e2dd1a3f3b7d10a40a7ec3e6ca6c6cfb4c85a
|
||||
whole-file packages/webui/tests/preview.test.mjs 0 7f7fbb11dd5915b3351b5086ec9b0f0bed6837a32a2e5902b707fcb54f1bf1c3
|
||||
whole-file packages/webui/tests/processing-browser.test.mjs 0 ed5970f1e2b1fa393c80db5e5551523e77ad0ee107dfa686a76c5d923b7e2d8f
|
||||
whole-file packages/webui/tests/processing.test.mjs 0 ac7a3a0ff51bb15c04e228186d771dd75ef515e09156cd080a8bea881e7cdd8d
|
||||
whole-file packages/webui/tests/project-file.test.mjs 0 8bf04994dec5c6d6670bbae055802759588c27b04010582732cbbf32c2921487
|
||||
whole-file packages/webui/tests/project-name-repo.test.mjs 0 ca773d7109f132bd099c43003a8269edfa96b2ba3a9e01e3694fa6bd12b9143e
|
||||
whole-file packages/webui/tests/proof-cli.test.mjs 0 5095f1ec66a3fc153467baef2291073e27aba17fdc2757e0a3cf2a03057a6547
|
||||
whole-file packages/webui/tests/proof.test.mjs 0 cdb948d261f52232c6e31e25baaf234bd0af3b6d90360a41a6142d2f34a8a797
|
||||
whole-file packages/webui/tests/reads.test.mjs 0 ef69c9c1dc9a55d526a704297aa4e2802cdca8f0e1fd70ff79efad2114a071a6
|
||||
whole-file packages/webui/tests/relaunch.test.mjs 0 e9f075560291cb446dabcaeb86c8800b5ea4ca0396c6a999ddbff88b245cefd1
|
||||
whole-file packages/webui/tests/return-flow.test.mjs 0 bf809ee139c15e9b6b8fe7f6e0f2ce00d0860aa460f145242fc732eafe8da8de
|
||||
whole-file packages/webui/tests/room-browser.test.mjs 0 23286bf46f341220f9e89cc1d229f46a579edb2eae5d688fc768f99ee343c858
|
||||
whole-file packages/webui/tests/room-http.test.mjs 0 a921e1991386cb5e184475cf47a012b3020b575968268e9f3c0b396e244a1819
|
||||
whole-file packages/webui/tests/routes-browser.test.mjs 0 df1283a6e1c5970b404b314b6b76dbf412e5318d76f296ec00164c932166b020
|
||||
whole-file packages/webui/tests/routes.test.mjs 0 055aa274fa66910a4ff1518fea49fbbbbbc5360dc16e825a460538f312a0781d
|
||||
whole-file packages/webui/tests/send-browser.test.mjs 0 da1547383522dbfd74a571b5d7625a310ae88f8adddff0c2bd33cb59df3ac137
|
||||
whole-file packages/webui/tests/send.test.mjs 0 f273e78388935b5fbc18a974f50cb85789436d51c47a4d8dccaf4af4742c2392
|
||||
whole-file packages/webui/tests/serve.test.mjs 0 1971344643d9cf6b4440d95c9ede42093235883ffab04fc4b614c1b7e7078f22
|
||||
whole-file packages/webui/tests/settings-form.test.mjs 0 80bd949d1666f2fe1f4caa051e2c34e742bab0e4adbe5584bd534d9bad8bef53
|
||||
whole-file packages/webui/tests/settings-write.test.mjs 0 7090474fff40910edb93c032feef248adc40ad21666d09c3610cf10279164f99
|
||||
whole-file packages/webui/tests/shell.test.mjs 0 844287f7797201473f1fb8d97ff2355ff3ff505006723678db78f26d3e5c2a40
|
||||
whole-file packages/webui/tests/sso-browser.test.mjs 0 1f70c36ce7d054ad435e52d63083c8fe19cd71e70c723518de5e58b69a426a0a
|
||||
whole-file packages/webui/tests/sso.test.mjs 0 6d7b3061bdc6e22744634ddc5612ff781e5c952a120a346c29eead66a3736438
|
||||
whole-file packages/webui/tests/tester-browser.test.mjs 0 425c54a84e5bf04ca91d8dc57ee83c14aff14a38159243f10f1c9114dd563967
|
||||
whole-file packages/webui/tests/tester-cli.test.mjs 0 5eb7296792f16f24d4486f39101007114b3db81c6ab53ab969e925caaffaa7d9
|
||||
whole-file packages/webui/tests/tester-listener.test.mjs 0 43927869c50a0e223d121e2dea2d10346a053a39fc8157c9fc4813b7c3b17e1f
|
||||
whole-file packages/webui/tests/tester-socket.test.mjs 0 c05962be7f87084404c53e4f7ed2caffff3d69e98e69a3c9f3c53b150cc804e5
|
||||
whole-file packages/webui/tests/thread-claims-browser.test.mjs 0 cbb2aeca0de2c64dd4c8d063d3e45140738d8f4064d38effeaec245ed7181b42
|
||||
whole-file packages/webui/tests/thread-claims.test.mjs 0 164a6c927abd819218be6386e123b234dd9955b122bace699e0a12201797791c
|
||||
whole-file packages/webui/tests/thread-sharing.test.mjs 0 ad64ab207702483aed6f16d13393e5f3c5a1ab0a0c2dcdf32717e8148a45078c
|
||||
whole-file packages/webui/tests/usage-browser.test.mjs 0 14be967dcae157cbb0a7b9ea4e0bc67337941d91a11d789baeb3d3c8353ac0e9
|
||||
whole-file packages/webui/tests/usage.test.mjs 0 4e20eee53c3eb79dc2930ca054977d06da88dba45b706a96ed94abaea6ec3e93
|
||||
whole-file packages/webui/tests/user-option-swap.test.mjs 0 bd43c257ae31cad2ca6c7a0b3ee49c6e323737d236fb7ff2a2105fd5e59b9767
|
||||
whole-file packages/webui/tests/views.test.mjs 0 4226ea02d33b9de8f2eaf9f93b0a551cbd9e6100560a8811eed90aa8de76974c
|
||||
script scripts/test-auth.sh 0 03999aab66446a4192ea9855f5cc1bd406080d0dfa5250b45b30e118556a0d5a
|
||||
script scripts/test-conductor.sh 0 79b7a689ed96792d42edd1d14796e0293e84e2e339b49476e457ad89e1c4a186
|
||||
script scripts/test-config.sh 0 2c3c87fb193e82fa17428933eb69cb2b34a589e8338f7a94aaa8256729fdf08f
|
||||
script scripts/test-discord.sh 0 5c6918b4415e8eabc5df31ac5ad696ec2afdfe3d498d69eae64f228a3807eddb
|
||||
script scripts/test-extension-package.sh 0 1f672a73e507cc57a204f7d3806ddcb211a0a8fb3ffccefc12bbc41e299f65ac
|
||||
script scripts/test-federation-acceptance.sh 0 febb0148bc1316bba3189aacf34d7c78f5f5db5d97a35aa4528dfadbec8bfbd0
|
||||
script scripts/test-foundation.sh 0 3de063333ce60ef1ca1986ecdd8ac30187a2316b7ca8e3ae4b4e6c8bd435caee
|
||||
script scripts/test-land-work.sh 0 ec90beab6246420982f872a4f45a7173e7a06f4aa263630d6ec4a4865176cde0
|
||||
script scripts/test-push-check.sh 0 0304c110dc5cf269fd8a4bea9759e5d48895a51c9d411814ce39ca924073da79
|
||||
script scripts/test-queue.sh 0 f678da9550eb6daa5268465b3a07beefd661e506ed38b8e11bf0a1b4a68cc781
|
||||
script scripts/test-release.sh 0 ad0f2a6b271275003783d3ffab5520f167ea79e26683710ce92a71600037360c
|
||||
script scripts/test-task.sh 0 ad511e52af4bc0a843eabbb3efcedd88c89468ccee43be9022e23880d7901f16
|
||||
mutant M1-supporting 1 0 10581fdab579db4d63322eee3369a1a740266e00a7dcbaa95e7651a7e0447ab8
|
||||
mutant M2-force-push 1 0 b137e8362ae3e26a090a82e8c41c3e87beeaac43183cdd6db67aba55d5e372c9
|
||||
mutant M3-forged-token 1 0 bda603c18b7b2bf468a21e139f0cff0c15c22e19c9d41cf8c50f9c3ae0bd128d
|
||||
mutant M4-grant-before-push 1 0 a642ef8e57a98c1488bb787da241292e6ed5446ca4117940fb0ddb9800c6a105
|
||||
mutant M5-notice-after-publication 1 0 5645138c2cef5d1999948744783ff3904a78e9cefd7421674386a2d0c5fdc45a
|
||||
mutant M6-commit-schema 1 0 7201f53cb976b2db1994c4e25f7f6e8b58eea323c060dbddbfa0372464adb686
|
||||
|
@@ -0,0 +1,102 @@
|
||||
{
|
||||
"total": {
|
||||
"tests": 2605,
|
||||
"pass": 2604,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 1,
|
||||
"todo": 0
|
||||
},
|
||||
"packages": {
|
||||
"library": {
|
||||
"files": 13,
|
||||
"tests": 168,
|
||||
"pass": 168,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"todo": 0
|
||||
},
|
||||
"bus": {
|
||||
"files": 116,
|
||||
"tests": 1035,
|
||||
"pass": 1035,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"todo": 0
|
||||
},
|
||||
"harness": {
|
||||
"files": 16,
|
||||
"tests": 180,
|
||||
"pass": 180,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"todo": 0
|
||||
},
|
||||
"webui": {
|
||||
"files": 67,
|
||||
"tests": 398,
|
||||
"pass": 398,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"todo": 0
|
||||
},
|
||||
"business": {
|
||||
"files": 2,
|
||||
"tests": 25,
|
||||
"pass": 25,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"todo": 0
|
||||
},
|
||||
"cli": {
|
||||
"files": 59,
|
||||
"tests": 620,
|
||||
"pass": 619,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 1,
|
||||
"todo": 0
|
||||
},
|
||||
"plugins": {
|
||||
"files": 1,
|
||||
"tests": 8,
|
||||
"pass": 8,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"todo": 0
|
||||
},
|
||||
"processing-control": {
|
||||
"files": 1,
|
||||
"tests": 7,
|
||||
"pass": 7,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"todo": 0
|
||||
},
|
||||
"seat": {
|
||||
"files": 5,
|
||||
"tests": 69,
|
||||
"pass": 69,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"todo": 0
|
||||
},
|
||||
"tasks": {
|
||||
"files": 9,
|
||||
"tests": 95,
|
||||
"pass": 95,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"todo": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1694,3 +1694,9 @@ are never rewritten or removed; corrections are new entries.
|
||||
2026-10-24 | magica | row329 R2 #1827 | ASCII management-key fix and docs verified: package66/66,bus readers261/261,CLI readers726/726,config55/55; no required failures/skips, slot returned. Preparing authorized ff review candidate; old tests unchanged.
|
||||
2026-10-24 | Sage 1ef1e4f8 | row329 landing #1827 (mos-1634) | Gate GREEN 23:29:14Z unchanged, landed 61ebf2c34 pushed 23:31:27Z; #1827 closed c31650; queue note 4063, done 4064 (b24d917ec); DEFERRED N2/R2-N1 to 331, N1 to P7; Vikunja 631 filed, 237 c1109; 284 and 157 must rebase.
|
||||
2026-10-25 | Sage 1ef1e4f8 | row284 landing #1789 | Gate GREEN 00:09:04Z unchanged, landed ce0b6ed41 pushed 00:09:52Z; #1789 closed c31655; queue note 4071, done 4072 (7c8dc2033); DEFERRED Webby N1 to the next library read-path row; row 332 PROXY_CODEX_API ruled openai-responses (sage-4331), P3 holds for row 331.
|
||||
2026-10-23 | Bubba (Codex gpt-6.1-sol, 3d6672b5) | Row 288 / #1793 C2-02 in bubba/row288; mos-1499, sage-3613 | Started; 283/285/286 merged intact, static build pending Sage CLEAR; queue ownership remains with Sage.
|
||||
2026-10-24 | Bubba (3d6672b5) | Row288 static preflight, sage-4282 | Creative contract/tool names match; short fixture tokens; provisional import-based whole-file run list excludes Case1; no Node/test run, awaiting landed284 base and CLEAR.
|
||||
|
||||
2026-10-25 | Bubba | row288 sage-4338 landed-base transplant and import audit | New bubba/row288-r3-landed on78b69e1e6; no test conflicts/assertion changes;289 whole files +12 runners proposed; UNTESTED pending CLEAR.
|
||||
|
||||
2026-10-25 | Bubba | row288 gate sage-4396 | Tested2848d832e:289 files/12 scripts rc0,6 mutants killed with restored controls rc0; own reds recorded/fixed; slot returned bubba-0034, review/landing pending.
|
||||
+95
-11
@@ -110,7 +110,7 @@ const LIBRARY_LOOKUPS = Object.freeze({
|
||||
'library.manuscript': { method: 'manuscript', fields: ['universe', 'book', 'scope', 'chapter', 'from', 'to'], required: ['universe', 'book'] },
|
||||
'library.universe': { method: 'universeInfo', fields: ['universe'], required: ['universe'] },
|
||||
});
|
||||
export const LIBRARY_VERBS = Object.freeze(['library.file', 'library.status', 'library.sync', ...Object.keys(LIBRARY_LOOKUPS)]);
|
||||
export const LIBRARY_VERBS = Object.freeze(['library.file', 'library.status', 'library.sync', 'library.file.save', 'library.revert', 'library.doc.write', 'library.doc.delete', ...Object.keys(LIBRARY_LOOKUPS)]);
|
||||
export const PROJECT_SAVE_VERB = 'project.file.save';
|
||||
export const PROJECT_SAVE_REQUEST_MAX = 1024 * 1024 + 64 * 1024;
|
||||
export const WEB_VERBS =Object.freeze(['web.search', 'web.fetch']);
|
||||
@@ -5411,18 +5411,99 @@ export class Broker {
|
||||
}
|
||||
// C2-04 M4: only a verified human reads library text or status. Host Git
|
||||
// functions stay injected, preserving this module's sandbox import closure.
|
||||
// C2-02: all authority and credential choice stays on the host. Neither an agent's tool
|
||||
// arguments nor the human's save may supply a credential, identity or commit message.
|
||||
#libraryWriter(s, project) {
|
||||
if (s.tester) fail('tester-refused');
|
||||
const b = this.#business(s.business), role = b.roles?.[s.role];
|
||||
if (s.human || s.reader || !CREATIVE_DEFINITIONS.includes(role?.definition)
|
||||
|| !role.authority?.withinRole?.includes('library.write')) fail('library-write-refused');
|
||||
this.#agent(s);
|
||||
const live = this.#store.get('SELECT user,projects,rotate_by FROM gitea_grants WHERE business=? AND agent=?', s.business, s.role);
|
||||
if (!live || !this.#giteaKeys || this.#giteaKeys.held(s.business, s.role)
|
||||
|| this.#firing(s.business, s.role) || live.rotate_by < this.#now().slice(0, 10)
|
||||
|| !role.publish?.includes(project)) fail('library-write-no-grant');
|
||||
let projects;
|
||||
try { projects = JSON.parse(live.projects); } catch { fail('library-write-no-grant'); }
|
||||
if (!Array.isArray(projects) || !projects.includes(project) || live.user !== role.identity?.gitea?.user)
|
||||
fail('library-write-no-grant');
|
||||
if (!role.identity?.name || !role.identity?.email) fail('library-write-refused');
|
||||
return { identity: { name: role.identity.name, email: role.identity.email },
|
||||
agent: `${s.business}/${s.role}`, credential: { kind: 'seat', agent: s.role, user: live.user } };
|
||||
}
|
||||
#libraryWriteNotice(s, op, result) {
|
||||
const mid = randomUUID();
|
||||
this.#store.run('INSERT INTO messages(id,at,business,from_role,from_run,to_role,class,in_reply_to,decision,corrects,body) VALUES(?,?,?,?,?,?,?,?,?,?,?)',
|
||||
mid, this.#now(), s.business, s.role, s.run, 'human', 'INFO', null, null, null,
|
||||
JSON.stringify({ notice: 'library-write', op, path: result.path, commit: result.commit }));
|
||||
this.#route(s.business, mid, 'human', s.session);
|
||||
}
|
||||
async #requestLibrary(s, request) {
|
||||
const { verb, args } = request;
|
||||
if (Object.hasOwn(LIBRARY_LOOKUPS, verb)) return this.#requestLibraryLookup(s, request);
|
||||
try {
|
||||
this.#human(s);
|
||||
const fields = verb === 'library.file' ? ['project', 'path'] : verb === 'library.sync' ? ['project', 'accept'] : ['project'];
|
||||
keys(args, fields, verb === 'library.sync' ? ['project'] : fields);
|
||||
if (!this.#library?.readFile || !this.#library?.status) fail('project-not-configured');
|
||||
if (verb === 'library.sync' && !this.#library.sync) fail('project-not-configured');
|
||||
const context = { business: this.#business(s.business), project: args.project };
|
||||
const agent = verb === 'library.doc.write' || verb === 'library.doc.delete';
|
||||
const b = this.#business(s.business);
|
||||
let project = args.project, writer;
|
||||
if (agent) {
|
||||
// The caller gate precedes argument checks, including human and tester refusals.
|
||||
if (s.tester) fail('tester-refused');
|
||||
if (s.human || s.reader || !CREATIVE_DEFINITIONS.includes(b.roles?.[s.role]?.definition)
|
||||
|| !b.roles[s.role].authority?.withinRole?.includes('library.write')) fail('library-write-refused');
|
||||
this.#agent(s);
|
||||
const fields = ['universe', 'path', 'base', ...(verb === 'library.doc.write' ? ['content'] : [])];
|
||||
keys(args, fields, fields);
|
||||
if (typeof args.universe !== 'string') fail('invalid-request');
|
||||
const matches = Object.entries(b.projects ?? {}).filter(([, e]) => e.kind === 'library'
|
||||
&& Object.hasOwn(e.universes ?? {}, args.universe));
|
||||
if (matches.length !== 1) fail('project-not-configured');
|
||||
project = matches[0][0];
|
||||
writer = this.#libraryWriter(s, project);
|
||||
} else {
|
||||
this.#human(s);
|
||||
const fields = verb === 'library.file' ? ['project', 'path'] : verb === 'library.sync' ? ['project', 'accept']
|
||||
: verb === 'library.file.save' ? ['project', 'path', 'base', 'content']
|
||||
: verb === 'library.revert' ? ['project', 'commit'] : ['project'];
|
||||
keys(args, fields, verb === 'library.sync' ? ['project'] : fields);
|
||||
}
|
||||
if (typeof project !== 'string' || b.projects?.[project]?.kind !== 'library') fail('project-not-configured');
|
||||
if (verb === 'library.revert' && (typeof args.commit !== 'string' || !/^[a-f0-9]{40}$/.test(args.commit))) fail('invalid-request');
|
||||
if (agent || verb === 'library.file.save') {
|
||||
if (args.base !== null && (typeof args.base !== 'string' || !/^[a-f0-9]{40}$/.test(args.base))) fail('invalid-request');
|
||||
if (verb !== 'library.doc.delete' && (typeof args.content !== 'string' || args.content.includes('\0')
|
||||
|| Buffer.byteLength(args.content, 'utf8') > 1024 * 1024
|
||||
|| Buffer.from(args.content, 'utf8').toString('utf8') !== args.content)) fail('invalid-request');
|
||||
}
|
||||
const context = { business: b, project };
|
||||
let result;
|
||||
if (verb === 'library.file') result = await this.#library.readFile({ ...context, path: args.path });
|
||||
const method = agent ? 'writeDoc' : verb === 'library.file.save' ? 'saveFile'
|
||||
: verb === 'library.revert' ? 'revert' : verb === 'library.file' ? 'readFile' : verb === 'library.status' ? 'status' : 'sync';
|
||||
if (typeof this.#library?.[method] !== 'function') fail('project-not-configured');
|
||||
if (agent) {
|
||||
const op = verb === 'library.doc.delete' ? 'delete' : 'write';
|
||||
result = await this.#library.writeDoc({ ...context, ...args }, { ...writer, op,
|
||||
beforePush: () => { this.#processingWork(); this.#libraryWriter(s, project); },
|
||||
onPushed: saved => {
|
||||
try {
|
||||
this.#store.transaction(() => {
|
||||
this.#libraryWriteNotice(s, op, saved);
|
||||
this.#event(s, 'action.allowed', { action: 'library.write', class: 'within-role', project, path: saved.path, commit: saved.commit });
|
||||
});
|
||||
} catch {
|
||||
// A separate metadata-only sink survives a Console store failure. Never log args,
|
||||
// file text, credentials or raw SQL/exception output.
|
||||
try { this.#library.log?.({ event: 'library-notice-store-failed', code: 'outcome-unknown',
|
||||
business: s.business, instance: s.role, op, commit: saved.commit }); } catch {}
|
||||
fail('outcome-unknown');
|
||||
}
|
||||
} });
|
||||
} else if (verb === 'library.file.save') {
|
||||
this.#processingWork();
|
||||
result = await this.#library.saveFile({ ...context, path: args.path, base: args.base, content: args.content });
|
||||
} else if (verb === 'library.revert') {
|
||||
this.#processingWork();
|
||||
result = await this.#library.revert({ ...context, commit: args.commit });
|
||||
} else if (verb === 'library.file') result = await this.#library.readFile({ ...context, path: args.path });
|
||||
else if (verb === 'library.status') {
|
||||
result = await this.#library.status(context);
|
||||
// C2-06: the reply limits in force, read from the file now, and the keys that fell back.
|
||||
@@ -5432,16 +5513,19 @@ export class Broker {
|
||||
}
|
||||
}
|
||||
else {
|
||||
// A fetch that did not move served still answers with the code, so the refusal is recorded.
|
||||
const { state, code } = await this.#library.sync(context, { accept: args.accept ?? null });
|
||||
if (code !== null) fail(code);
|
||||
result = { project: args.project, ...state };
|
||||
result = { project, ...state };
|
||||
}
|
||||
this.#secretCheck(result);
|
||||
return result;
|
||||
} catch (error) {
|
||||
const Failure = this.#library?.LibraryError;
|
||||
throw this.#refused(s, Failure && error instanceof Failure ? new BusError(error.code) : error, request);
|
||||
const mapped = Failure && error instanceof Failure ? new BusError(error.code) : error;
|
||||
const refused = this.#refused(s, mapped, request);
|
||||
// Once a push succeeded the remote effect is uncertain to the caller even if the store
|
||||
// cannot record refusal evidence. Do not replace that with an ordinary storage refusal.
|
||||
throw mapped instanceof BusError && mapped.code === 'outcome-unknown' ? mapped : refused;
|
||||
}
|
||||
}
|
||||
// C2-06: the business's library project, the first in file order, or null.
|
||||
|
||||
@@ -17,7 +17,7 @@ export class Client {
|
||||
verb,
|
||||
args,
|
||||
}) + '\n';
|
||||
if (Buffer.byteLength(request) > (verb === PROJECT_SAVE_VERB ? PROJECT_SAVE_REQUEST_MAX : 65536))
|
||||
if (Buffer.byteLength(request) > ([PROJECT_SAVE_VERB, 'library.file.save'].includes(verb) ? PROJECT_SAVE_REQUEST_MAX : 65536))
|
||||
return Promise.reject(new BusError('request-too-large'));
|
||||
return new Promise((resolve, reject) => {
|
||||
const socket = connect(this.path);
|
||||
|
||||
@@ -281,6 +281,12 @@ export async function startBroker({
|
||||
credential: fetchCredential(business),
|
||||
...(librarySync ? { command: librarySync.command } : {}),
|
||||
});
|
||||
const humanWriteOptions = context => {
|
||||
const mapping = libraryModule.checkMapping(context.business.projects[context.project]);
|
||||
if (!mapping.editor) throw new libraryModule.LibraryError('library-mapping-invalid', 'editor is required');
|
||||
return { credential: libraryModule.humanCredential(context, { user: mapping.editor.user, configDir: slotDir }),
|
||||
...(librarySync ? { command: librarySync.command } : {}) };
|
||||
};
|
||||
// Row 194 (#1692, D3): after every write that commits, the broker takes the business as a restart
|
||||
// would load it. The credential references, the project roots the Credentials store protects
|
||||
// and the tracker bots are fixed at start (the Credentials store, the tasks adapter), so a file
|
||||
@@ -356,9 +362,18 @@ export async function startBroker({
|
||||
library: {
|
||||
lookup: createLibraryLookup({ dataRoot, sync: context =>
|
||||
processing.work(() => libraryModule.sync(context, syncOptions(context.business))) }),
|
||||
log: entry => process.stderr.write(`library: ${JSON.stringify(entry)}\n`),
|
||||
readFile: context => libraryModule.readLibraryFile({ ...context, dataRoot }),
|
||||
status: context => libraryModule.libraryStatus({ ...context, dataRoot }),
|
||||
sync: (context, { accept }) => processing.work(() => libraryModule.sync({ ...context, dataRoot }, { ...syncOptions(context.business), accept })),
|
||||
// C2-02: trusted references only. The askpass helper alone opens a credential file.
|
||||
writeDoc: (context, options) => processing.work(() => libraryModule.writeLibraryDoc({ ...context, dataRoot }, {
|
||||
...options, credential: { ...options.credential,
|
||||
file: storeRoot ? giteaReference(storeRoot, context.business.id, options.credential.agent).file : null },
|
||||
...(librarySync ? { command: librarySync.command } : {}),
|
||||
})),
|
||||
saveFile: context => processing.work(() => libraryModule.saveLibraryFile({ ...context, dataRoot }, humanWriteOptions(context))),
|
||||
revert: context => processing.work(() => libraryModule.revertLibraryCommit({ ...context, dataRoot }, humanWriteOptions(context))),
|
||||
LibraryError: libraryModule.LibraryError,
|
||||
// C2-06: the citation guard, the reply limits read from the business file at each check (none
|
||||
// configured reads as unreadable, so requireCitation is true), and thread.messages' `last`.
|
||||
|
||||
@@ -6,7 +6,9 @@ import { createPeerReader } from './peercred.mjs';
|
||||
const LIMIT = 65536;
|
||||
// Row 148: a document save may carry up to 1 MiB of text. Only a request that names that verb near
|
||||
// its start (the proof object comes first) may pass LIMIT; the parsed verb is checked again below.
|
||||
const SAVE_VERB_AT_START = new RegExp(`"verb":"${PROJECT_SAVE_VERB.replaceAll('.', '\\.')}"`);
|
||||
const PROJECT_SAVE_AT_START = new RegExp(`"verb":"${PROJECT_SAVE_VERB.replaceAll('.', '\\.')}"`);
|
||||
const LIBRARY_SAVE_AT_START = /"verb":"library\.file\.save"/;
|
||||
const SAVE_VERB_AT_START = { test: text => PROJECT_SAVE_AT_START.test(text) || LIBRARY_SAVE_AT_START.test(text) };
|
||||
// The refusal envelope. C2-06 (sage-3599): `reason` is added only when the error carries one, which
|
||||
// only the citation guard's refusals do, so every other envelope is byte-identical to before.
|
||||
const refusal = (e, other) => e instanceof BusError
|
||||
@@ -66,7 +68,7 @@ export async function serve({ broker, path, authenticateHuman = null, authentica
|
||||
try {
|
||||
if (end !== input.length - 1) throw new BusError('invalid-envelope');
|
||||
const r = JSON.parse(input.subarray(0, end).toString('utf8'));
|
||||
if (input.length > LIMIT && r?.verb !== PROJECT_SAVE_VERB) throw new BusError('request-too-large');
|
||||
if (input.length > LIMIT && ![PROJECT_SAVE_VERB, 'library.file.save'].includes(r?.verb)) throw new BusError('request-too-large');
|
||||
if (
|
||||
!r ||
|
||||
Array.isArray(r) ||
|
||||
|
||||
@@ -0,0 +1,334 @@
|
||||
// Row 288: broker authority, exact notice and strict Console revert API. Fixture grant rows only.
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { mkdtempSync, rmSync, mkdirSync, writeFileSync, readFileSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
import { connect } from 'node:net';
|
||||
import { startBroker, createLibraryLookup } from '../src/runtime.mjs';
|
||||
import { Store } from '../src/store.mjs';
|
||||
import { Broker } from '../src/broker.mjs';
|
||||
import { Client } from '../src/client.mjs';
|
||||
import { serve } from '../src/server.mjs';
|
||||
import { mappingEntry } from '../../library/tests/helpers.mjs';
|
||||
import { LibraryError } from '../../library/src/problems.mjs';
|
||||
import { grantDecision } from './host-fixtures.mjs';
|
||||
import { world as bareWorld } from '../../library/tests/write-fixture.mjs';
|
||||
import { sync } from '../../library/src/sync.mjs';
|
||||
import { SYNC_STATE_FILE, servedCommit } from '../../library/src/mirror.mjs';
|
||||
import { writeLibraryDoc, saveLibraryFile, revertLibraryCommit } from '../../library/src/write.mjs';
|
||||
import { git } from '../../library/tests/helpers.mjs';
|
||||
|
||||
const SHA = 'a'.repeat(40), NEXT = 'b'.repeat(40), PATH = 'Book One/Notes/Agent.md';
|
||||
function world(t, options = {}) {
|
||||
const root = mkdtempSync(join(tmpdir(), 'bus-library-write-')), store = new Store(root);
|
||||
t.after(() => { store.close(); rmSync(root, { recursive: true, force: true }); });
|
||||
const projects = options.projects ?? { books: mappingEntry() }; projects.books.universes.harbor.supporting = ['Book One/Notes/'];
|
||||
const authority = { withinRole: ['message.send', 'library.write'], crossRole: [] };
|
||||
const roles = { bean: { definition: 'companion', authority, identity: { name: 'Bean', email: '[email protected]', gitea: { user: 'bean', url: 'https://git.example.invalid' } }, publish: ['books'] },
|
||||
chief: { definition: 'chief-of-staff', authority }, quill: { definition: 'proofreader', authority: { withinRole: ['message.send'], crossRole: [] } } };
|
||||
options.changeRole?.(roles.bean);
|
||||
const business = { id: 'acme', human: 'jason', vars: { 'bus.tester': true }, projects, roles,
|
||||
arbiters: { technical: 'chief', delivery: 'chief' }, definitions: { companion: {}, 'chief-of-staff': {}, proofreader: {} } };
|
||||
const seen = [], held = new Set(), logs = [];
|
||||
const result = { project: 'books', path: PATH, commit: NEXT };
|
||||
const library = { LibraryError, log: entry => logs.push(entry), readFile: async () => ({}), status: async () => ({}),
|
||||
writeDoc: async (context, options) => { seen.push({ context, options }); options.beforePush(); options.onPushed(result); return result; },
|
||||
saveFile: async context => { seen.push({ context }); return result; },
|
||||
revert: async context => { seen.push({ context }); return { ...result, reverted: context.commit }; } };
|
||||
const broker = new Broker({ store, businesses: { acme: business }, library, giteaKeys: { held: (b, a) => held.has(a) } });
|
||||
const caps = { human: broker.bindHuman({ business: 'acme', human: 'jason', via: 'cli', outsideAgent: true }),
|
||||
tester: broker.bindTester({ business: 'acme' }) };
|
||||
const p = broker.request(caps.human, { verb: 'project.create', args: { name: 'Library chat', repo: 'books' } }).id;
|
||||
for (const role of ['bean', 'quill', 'chief']) {
|
||||
const thread = role === 'chief' ? null : broker.request(caps.human,
|
||||
{ verb: 'thread.create', args: { project: p, name: role, instance: role } }).id;
|
||||
caps[role] = broker.bindLaunch({ business: 'acme', role, run: role + '-run', harness: 'pi', ...(thread ? { thread } : {}) });
|
||||
broker.request(caps[role], { verb: 'role.claim' });
|
||||
}
|
||||
const grant = (project = 'books') => {
|
||||
const decision = grantDecision(store, { business: 'acme', grant: { method: 'gitea', agent: 'bean', target: { projects: [project] }, rotateBy: '2099-01-01' } });
|
||||
store.run('INSERT OR IGNORE INTO gitea_users(business,agent,user,id,nonce,decision) VALUES(?,?,?,?,?,?)',
|
||||
'acme', 'bean', 'bean', 7, '0123456789abcdef', decision);
|
||||
store.run('INSERT OR REPLACE INTO gitea_grants(business,agent,user,token_id,token_name,projects,rotate_by,decision) VALUES(?,?,?,?,?,?,?,?)',
|
||||
'acme', 'bean', 'bean', 7, 'fixture', JSON.stringify([project]), '2099-01-01', decision);
|
||||
};
|
||||
grant();
|
||||
const call = (who, verb, args = {}) => Promise.resolve().then(() => broker.request(caps[who], { verb, args }));
|
||||
return { root, store, broker, caps, call, seen, held, business, grant, library, logs };
|
||||
}
|
||||
const args = () => ({ universe: 'harbor', path: PATH, base: SHA, content: 'fixture\n' });
|
||||
|
||||
test('C2-02: agent verbs require creative holder, action and live unheld project grant', async t => {
|
||||
const f = world(t);
|
||||
for (const verb of ['library.doc.write', 'library.doc.delete']) {
|
||||
const a = args(); if (verb.endsWith('delete')) delete a.content;
|
||||
for (const [who, code] of [['human', 'library-write-refused'], ['tester', 'tester-refused'], ['chief', 'library-write-refused'], ['quill', 'library-write-refused']])
|
||||
await assert.rejects(f.call(who, verb, a), { code });
|
||||
f.held.add('bean'); await assert.rejects(f.call('bean', verb, a), { code: 'library-write-no-grant' }); f.held.clear();
|
||||
f.store.run('DELETE FROM gitea_grants');
|
||||
await assert.rejects(f.call('bean', verb, a), { code: 'library-write-no-grant' });
|
||||
f.grant('other'); await assert.rejects(f.call('bean', verb, a), { code: 'library-write-no-grant' });
|
||||
f.store.run('DELETE FROM gitea_grants'); f.grant();
|
||||
}
|
||||
assert.equal(f.seen.length, 0);
|
||||
});
|
||||
|
||||
test('C2-02: writing instance supplies neither ident nor notice text; broker inserts exact notice', async t => {
|
||||
const f = world(t);
|
||||
const result = await f.call('bean', 'library.doc.write', args());
|
||||
assert.equal(result.commit, NEXT);
|
||||
const m = f.store.get("SELECT * FROM messages WHERE from_role='bean' AND to_role='human'");
|
||||
assert.equal(m.body, JSON.stringify({ notice: 'library-write', op: 'write', path: PATH, commit: NEXT }));
|
||||
assert.equal(m.from_run, 'bean-run');
|
||||
assert.ok(f.store.get('SELECT 1 FROM message_threads WHERE message=?', m.id));
|
||||
assert.equal(f.seen[0].options.identity.name, 'Bean');
|
||||
assert.equal(f.seen[0].options.credential.user, 'bean');
|
||||
assert.equal(f.seen[0].options.credential.agent, 'bean');
|
||||
for (const extra of [{ author: 'other' }, { agent: 'chief' }, { credential: { agent: 'chief' } }])
|
||||
await assert.rejects(f.call('bean', 'library.doc.write', { ...args(), ...extra }), { code: 'invalid-request' });
|
||||
});
|
||||
|
||||
test('C2-02 A2: strict human revert args/result and agent human-required refusal', async t => {
|
||||
const f = world(t);
|
||||
for (const who of ['bean', 'quill', 'chief', 'tester'])
|
||||
for (const verb of ['library.revert', 'library.file.save'])
|
||||
await assert.rejects(f.call(who, verb, {}), { code: 'human-required' });
|
||||
for (const a of [{ commit: SHA }, { project: 'books' }, { project: 'books', commit: 'A'.repeat(40) },
|
||||
{ project: 'books', commit: SHA, editor: {} }, { project: 'books', commit: SHA + 'a' }])
|
||||
await assert.rejects(f.call('human', 'library.revert', a), { code: 'invalid-request' });
|
||||
assert.deepEqual(await f.call('human', 'library.revert', { project: 'books', commit: SHA }),
|
||||
{ project: 'books', path: PATH, reverted: SHA, commit: NEXT });
|
||||
await assert.rejects(f.call('human', 'library.revert', { project: 'missing', commit: SHA }), { code: 'project-not-configured' });
|
||||
});
|
||||
|
||||
test('C2-02: human revert independent of revoked seat and LibraryError codes survive', async t => {
|
||||
const f = world(t); f.store.run('DELETE FROM gitea_grants'); f.held.add('bean');
|
||||
assert.equal((await f.call('human', 'library.revert', { project: 'books', commit: SHA })).reverted, SHA);
|
||||
for (const code of ['library-revert-not-agent', 'stale-base', 'library-mapping-invalid', 'library-human-missing']) {
|
||||
f.library.revert = async () => { throw new LibraryError(code, 'fixture'); };
|
||||
await assert.rejects(f.call('human', 'library.revert', { project: 'books', commit: SHA }), { code });
|
||||
}
|
||||
});
|
||||
|
||||
test('C2-02 transport: human library.file.save exceeds 64 KiB; agent write remains bounded', async t => {
|
||||
const f = world(t), path = join(f.root, 'bus.sock');
|
||||
const server = await serve({ broker: f.broker, path, authenticateHuman: () => f.broker.bindHuman({ business: 'acme', human: 'jason', via: 'cli', outsideAgent: true }) });
|
||||
t.after(() => server.close());
|
||||
const human = new Client({ path, human: { fixture: true } }), agent = new Client({ path, cap: f.caps.bean });
|
||||
const content = 'x'.repeat(70000);
|
||||
assert.equal((await human.call('library.file.save', { project: 'books', path: PATH, base: SHA, content })).commit, NEXT);
|
||||
assert.equal(f.seen.at(-1).context.content, content);
|
||||
await assert.rejects(agent.call('library.doc.write', { ...args(), content }), { code: 'request-too-large' });
|
||||
await assert.rejects(human.call('library.file.save', { project: 'books', path: PATH, base: SHA, content: 'x'.repeat(2 * 1024 * 1024) }), { code: 'request-too-large' });
|
||||
});
|
||||
|
||||
|
||||
test('C2-02 M12 through broker: every refusal preserves remote, served and all mirror refs', async t => {
|
||||
const bare = await bareWorld(t), f = world(t, { projects: bare.context.business.projects });
|
||||
f.library.writeDoc = (context, options) => writeLibraryDoc({ ...context, dataRoot: bare.context.dataRoot },
|
||||
{ ...options, credential: { ...options.credential, file: bare.file }, command: bare.command });
|
||||
for (const path of ['Book One/Chapters/Chapter 01.md', 'Inbox Note.md', 'Book One/Playground/What If.md',
|
||||
'Book One/Notes/X.canvas', 'Book One/Notes/X.base', 'Book One/Notes/X.png', 'Second World/Chapters/01.md',
|
||||
'/Book One/Notes/X.md', 'Book One/Notes/../X.md', 'Book One/./Notes/X.md', 'Book One//Notes/X.md',
|
||||
'Book One/Notes/X\\Y.md', 'Book One/Notes/X\nY.md', 'Book One/Notes/Link.md', 'Book One/Notes/Submodule.md',
|
||||
'Book One/Notes/agent.md', `Book One/Notes/${'é'.repeat(512)}.md`]) {
|
||||
for (const verb of ['library.doc.write', 'library.doc.delete']) {
|
||||
const before = bare.refs(), head = bare.remoteHead(), a = { ...args(), path, base: null };
|
||||
if (verb.endsWith('delete')) delete a.content;
|
||||
await assert.rejects(f.call('bean', verb, a), { code: 'library-write-story-path' }, path);
|
||||
assert.equal(bare.refs(), before); assert.equal(bare.remoteHead(), head);
|
||||
}
|
||||
}
|
||||
assert.equal(f.store.get("SELECT count(*) n FROM messages WHERE from_role='bean'").n, 0);
|
||||
});
|
||||
|
||||
test('C2-02 broker + bare: exact write/delete notices, then human revert with revoked seat grant', async t => {
|
||||
const bare = await bareWorld(t), f = world(t, { projects: bare.context.business.projects });
|
||||
f.library.writeDoc = (context, options) => writeLibraryDoc({ ...context, dataRoot: bare.context.dataRoot },
|
||||
{ ...options, credential: { ...options.credential, file: bare.file }, command: bare.command });
|
||||
f.library.revert = context => revertLibraryCommit({ ...context, dataRoot: bare.context.dataRoot },
|
||||
{ credential: { ...bare.credential, kind: 'human' }, command: bare.command });
|
||||
f.library.saveFile = context => saveLibraryFile({ ...context, dataRoot: bare.context.dataRoot },
|
||||
{ credential: { ...bare.credential, kind: 'human' }, command: bare.command });
|
||||
const base = git(bare.mirror, 'rev-parse', `served:${PATH}`);
|
||||
const saved = await f.call('bean', 'library.doc.write', { ...args(), base });
|
||||
assert.equal(saved.commit, bare.remoteHead());
|
||||
f.store.run('DELETE FROM gitea_grants'); f.held.add('bean');
|
||||
const reverted = await f.call('human', 'library.revert', { project: 'books', commit: saved.commit });
|
||||
assert.equal(reverted.reverted, saved.commit);
|
||||
assert.equal(git(bare.remote, 'show', `main:${PATH}`), 'before');
|
||||
assert.equal(git(bare.remote, 'show', '-s', '--format=%an <%ae>', reverted.commit), 'Human Fixture <[email protected]>');
|
||||
f.held.clear(); f.grant();
|
||||
const deleted = await f.call('bean', 'library.doc.delete', { universe: 'harbor', path: PATH, base });
|
||||
const messages = f.store.all("SELECT body FROM messages WHERE from_role='bean' AND to_role='human' ORDER BY seq");
|
||||
assert.deepEqual(messages.map(m => JSON.parse(m.body)), [
|
||||
{ notice: 'library-write', op: 'write', path: PATH, commit: saved.commit },
|
||||
{ notice: 'library-write', op: 'delete', path: PATH, commit: deleted.commit }]);
|
||||
assert.equal(git(bare.remote, 'ls-tree', '-r', deleted.commit, '--', PATH), '');
|
||||
});
|
||||
|
||||
|
||||
test('C2-02 socket: a save name nested in proof cannot enlarge an agent envelope', async t => {
|
||||
const f = world(t), path = join(f.root, 'raw-bus.sock');
|
||||
const server = await serve({ broker: f.broker, path, authenticateHuman: () => f.caps.human });
|
||||
t.after(() => server.close());
|
||||
const request = JSON.stringify({ human: { verb: 'library.file.save' }, verb: 'library.doc.write',
|
||||
args: { ...args(), content: 'x'.repeat(70000) } }) + '\n';
|
||||
const response = await new Promise((resolve, reject) => {
|
||||
const socket = connect(path); let result = '';
|
||||
socket.on('error', reject); socket.on('connect', () => socket.write(request));
|
||||
socket.on('data', bytes => { result += bytes; if (result.includes('\n')) { socket.destroy(); resolve(JSON.parse(result)); } });
|
||||
});
|
||||
assert.deepEqual(response, { ok: false, error: 'request-too-large' });
|
||||
assert.equal(f.seen.length, 0);
|
||||
});
|
||||
|
||||
|
||||
test('C2-02 E2: held grant during fetch refuses at beforePush and preserves every ref', async t => {
|
||||
const bare = await bareWorld(t), f = world(t, { projects: bare.context.business.projects });
|
||||
const before = bare.refs(), head = bare.remoteHead();
|
||||
const command = (kind, url, options) => {
|
||||
if (kind === 'fetch') f.held.add('bean');
|
||||
return bare.command(kind, url, options);
|
||||
};
|
||||
f.library.writeDoc = (context, options) => writeLibraryDoc({ ...context, dataRoot: bare.context.dataRoot },
|
||||
{ ...options, credential: { ...options.credential, file: bare.file }, command });
|
||||
await assert.rejects(f.call('bean', 'library.doc.write', { ...args(), base: git(bare.mirror, 'rev-parse', `served:${PATH}`) }),
|
||||
{ code: 'library-write-no-grant' });
|
||||
assert.equal(bare.refs(), before); assert.equal(bare.remoteHead(), head);
|
||||
assert.equal(f.store.get("SELECT count(*) n FROM messages WHERE from_role='bean'").n, 0);
|
||||
});
|
||||
|
||||
test('C2-02 E3: expired, firing, missing publish, wrong grant user and missing ident refuse', async t => {
|
||||
const cases = [
|
||||
{ name: 'expired', prepare: f => f.store.run("UPDATE gitea_grants SET rotate_by='2000-01-01'"), code: 'library-write-no-grant' },
|
||||
{ name: 'firing', prepare: f => f.store.run("INSERT INTO events(id,at,business,kind,actor_role,actor_run,subject,body) VALUES('fire-bean',?,'acme','action.allowed','chief','run','bean',?)",
|
||||
new Date().toISOString(), JSON.stringify({ action: 'fire.stopped' })), code: 'library-write-no-grant' },
|
||||
{ name: 'publish', changeRole: r => { r.publish = []; }, code: 'library-write-no-grant' },
|
||||
{ name: 'grant user', prepare: f => f.store.run("UPDATE gitea_grants SET user='other-seat'"), code: 'library-write-no-grant' },
|
||||
{ name: 'identity name', changeRole: r => { delete r.identity.name; }, code: 'library-write-refused' },
|
||||
{ name: 'identity email', changeRole: r => { delete r.identity.email; }, code: 'library-write-refused' },
|
||||
];
|
||||
for (const c of cases) {
|
||||
const f = world(t, { changeRole: c.changeRole }); c.prepare?.(f);
|
||||
for (const verb of ['library.doc.write', 'library.doc.delete']) {
|
||||
const a = args(); if (verb.endsWith('delete')) delete a.content;
|
||||
await assert.rejects(f.call('bean', verb, a), { code: c.code }, c.name);
|
||||
}
|
||||
assert.equal(f.seen.length, 0);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
test('C2-02 F2/E6 runtime: human saves/reverts use editor.user; no login never uses the fetch bot', { timeout: 30000 }, async t => {
|
||||
for (const user of [undefined, 'her-gitea-login']) {
|
||||
const bare = await bareWorld(t);
|
||||
const agent = await writeLibraryDoc(bare.args(), bare.opts);
|
||||
bare.entry.user = 'fetch-bot';
|
||||
if (user) bare.entry.editor.user = user;
|
||||
const configDir = join(bare.root, 'config'), slots = join(configDir, 'secrets', 'acme');
|
||||
mkdirSync(slots, { recursive: true, mode: 0o700 });
|
||||
for (const slot of ['library-human', 'library-gitea'])
|
||||
writeFileSync(join(slots, slot), 'fx288-human\n', { mode: 0o600 });
|
||||
const pushes = [];
|
||||
const command = (kind, url, options) => { if (kind === 'push') pushes.push(options.user); return bare.command(kind, url, options); };
|
||||
const initial = (await bare.state()).lastFetch;
|
||||
const business = { ...bare.context.business, human: 'jason', roles: { coder: { authority: { withinRole: [], crossRole: [] } } },
|
||||
arbiters: { technical: 'coder', delivery: 'coder' } };
|
||||
const runtime = await startBroker({ dataRoot: bare.context.dataRoot, businesses: { acme: business },
|
||||
librarySync: { configDir, command, intervalMs: 60000 } });
|
||||
try {
|
||||
const human = runtime.broker.bindHuman({ business: 'acme', human: 'jason', via: 'cli', outsideAgent: true });
|
||||
const call = (verb, args) => Promise.resolve().then(() => runtime.broker.request(human, { verb, args }));
|
||||
// Let the boot fetch finish before inspecting only the human operation's command count.
|
||||
const deadline = Date.now() + 10000;
|
||||
while (true) {
|
||||
const state = await call('library.status', { project: 'books' });
|
||||
if (state.lastFetch !== initial) break;
|
||||
if (Date.now() > deadline) throw Error('boot fetch did not complete');
|
||||
await new Promise(resolve => setTimeout(resolve, 10));
|
||||
}
|
||||
const path = 'Book One/Chapters/Chapter 01.md';
|
||||
const saves = [
|
||||
['library.file.save', { project: 'books', path, base: git(bare.mirror, 'rev-parse', `served:${path}`), content: 'her edit\n' }],
|
||||
['library.revert', { project: 'books', commit: agent.commit }],
|
||||
];
|
||||
const before = bare.commands.length;
|
||||
for (const [verb, args] of saves) {
|
||||
if (user) assert.match((await call(verb, args)).commit, /^[a-f0-9]{40}$/);
|
||||
else await assert.rejects(call(verb, args), { code: 'library-human-missing' });
|
||||
}
|
||||
assert.deepEqual(pushes, user ? [user, user] : []);
|
||||
if (!user) assert.equal(bare.commands.length, before);
|
||||
} finally { await runtime.close(); }
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
test('C2-02 E4: post-push publication failure stores the exact notice and returns outcome-unknown', async t => {
|
||||
const bare = await bareWorld(t), f = world(t, { projects: bare.context.business.projects });
|
||||
const state = join(bare.mirror, SYNC_STATE_FILE), original = readFileSync(state);
|
||||
const previous = await servedCommit(bare.mirror), head = bare.remoteHead();
|
||||
const command = (kind, url, options) => {
|
||||
// Break only state decoding after push admission; the real remote push still executes.
|
||||
if (kind === 'push') writeFileSync(state, 'invalid state\n');
|
||||
return bare.command(kind, url, options);
|
||||
};
|
||||
f.library.writeDoc = (context, options) => writeLibraryDoc({ ...context, dataRoot: bare.context.dataRoot },
|
||||
{ ...options, credential: { ...options.credential, file: bare.file }, command });
|
||||
try {
|
||||
await assert.rejects(f.call('bean', 'library.doc.write', { ...args(), base: git(bare.mirror, 'rev-parse', `served:${PATH}`) }),
|
||||
{ code: 'outcome-unknown' });
|
||||
assert.notEqual(bare.remoteHead(), head);
|
||||
assert.equal(git(bare.mirror, 'rev-parse', 'served'), previous);
|
||||
const messages = f.store.all("SELECT body FROM messages WHERE from_role='bean' AND to_role='human'");
|
||||
assert.deepEqual(messages.map(m => JSON.parse(m.body)), [
|
||||
{ notice: 'library-write', op: 'write', path: PATH, commit: bare.remoteHead() }]);
|
||||
} finally { writeFileSync(state, original); }
|
||||
const commit = bare.remoteHead();
|
||||
const human = { credential: { ...bare.credential, kind: 'human' }, command: bare.command };
|
||||
await assert.rejects(revertLibraryCommit({ ...bare.context, commit }, human), { code: 'library-revert-not-agent' });
|
||||
await sync(bare.context, { credential: bare.credential, command: bare.command });
|
||||
assert.equal((await revertLibraryCommit({ ...bare.context, commit }, human)).reverted, commit);
|
||||
});
|
||||
|
||||
|
||||
test('C2-02 mos-1520: failed notice insert logs metadata and returns outcome-unknown even if refusal evidence fails', async t => {
|
||||
const bare = await bareWorld(t), f = world(t, { projects: bare.context.business.projects });
|
||||
const previous = await servedCommit(bare.mirror), head = bare.remoteHead();
|
||||
f.library.writeDoc = (context, options) => writeLibraryDoc({ ...context, dataRoot: bare.context.dataRoot },
|
||||
{ ...options, credential: { ...options.credential, file: bare.file }, command: bare.command });
|
||||
f.store.run(`CREATE TEMP TRIGGER fail_library_notice BEFORE INSERT ON messages
|
||||
WHEN NEW.from_role='bean' BEGIN SELECT RAISE(ABORT, 'fixture store failure'); END;`);
|
||||
f.store.run(`CREATE TEMP TRIGGER fail_refusal_log BEFORE INSERT ON events
|
||||
WHEN NEW.kind='action.refused' BEGIN SELECT RAISE(ABORT, 'fixture evidence failure'); END;`);
|
||||
await assert.rejects(f.call('bean', 'library.doc.write', { ...args(), base: git(bare.mirror, 'rev-parse', `served:${PATH}`) }),
|
||||
{ code: 'outcome-unknown' });
|
||||
assert.notEqual(bare.remoteHead(), head);
|
||||
assert.equal(await servedCommit(bare.mirror), previous);
|
||||
assert.equal(f.store.get("SELECT count(*) n FROM messages WHERE from_role='bean'").n, 0);
|
||||
assert.deepEqual(f.logs, [{ event: 'library-notice-store-failed', code: 'outcome-unknown', business: 'acme',
|
||||
instance: 'bean', op: 'write', commit: bare.remoteHead() }]);
|
||||
});
|
||||
|
||||
|
||||
test('C2-02 + C2-05c: broker get blob feeds write/delete and new served lookups immediately', async t => {
|
||||
const bare = await bareWorld(t), f = world(t, { projects: bare.context.business.projects });
|
||||
f.library.lookup = createLibraryLookup({ dataRoot: bare.context.dataRoot,
|
||||
sync: context => sync({ ...context, dataRoot: bare.context.dataRoot }, { credential: bare.credential, command: bare.command }) });
|
||||
f.library.writeDoc = (context, options) => writeLibraryDoc({ ...context, dataRoot: bare.context.dataRoot },
|
||||
{ ...options, credential: { ...options.credential, file: bare.file }, command: bare.command });
|
||||
const query = { universe: 'harbor', path: PATH, scope: 'supporting' };
|
||||
const before = await f.call('bean', 'library.get', query);
|
||||
assert.equal(before.blob, git(bare.mirror, 'rev-parse', `served:${PATH}`));
|
||||
const saved = await f.call('bean', 'library.doc.write', { ...args(), base: before.blob, content: 'new served text\n' });
|
||||
const after = await f.call('bean', 'library.get', query);
|
||||
assert.equal(after.commit, saved.commit);
|
||||
assert.equal(after.blob, git(bare.mirror, 'rev-parse', `${saved.commit}:${PATH}`));
|
||||
assert.notEqual(after.blob, before.blob);
|
||||
assert.deepEqual(after.lines[0], [1, 'new served text']);
|
||||
assert.equal(await f.call('bean', 'library.get', { universe: 'harbor', path: PATH }), null);
|
||||
await f.call('bean', 'library.doc.delete', { universe: 'harbor', path: PATH, base: after.blob });
|
||||
assert.equal(await f.call('bean', 'library.get', query), null);
|
||||
});
|
||||
@@ -249,6 +249,21 @@ export function typedTools({ resolved, arbiter = false, tracker = false, project
|
||||
...base, book, scope: manuscriptScope, chapter: str("Chapter filename without extension"), from: line, to: line,
|
||||
}, ["universe", "book"]));
|
||||
add("library_universe", "library.universe", null, "Read a universe's mapping at the served commit: books, types, playground prefixes, and hybrid sources/constraints/notes or draft metadata.", obj(base, ["universe"]));
|
||||
// C2-02: write tools carry only the note/base/content. The broker supplies the writer,
|
||||
// credential and fixed commit message; a creative within-role grant is required.
|
||||
if (CREATIVE_DEFINITIONS.includes(resolved.definition) && resolved.limits.authority.withinRole.includes("library.write")) {
|
||||
const writeBase = {
|
||||
universe,
|
||||
path: str("A .md path under this universe's human-marked supporting folder"),
|
||||
base: { type: ["string", "null"], pattern: "^[a-f0-9]{40}$", description: "The blob from library_get, or null to create; never the commit id" },
|
||||
};
|
||||
add("library_doc_write", "library.doc.write", "library.write", "Create or edit one supporting .md note using the blob from library_get. One commit by your identity, with a fixed human notice. A stale base refuses; agent requests retain the 64 KiB envelope limit.", obj({
|
||||
...writeBase, content: str("UTF-8 note text without NUL; at most 1 MiB and bounded by the agent socket envelope"),
|
||||
}, ["universe", "path", "base", "content"]));
|
||||
add("library_doc_delete", "library.doc.delete", "library.write", "Delete one existing regular supporting .md note using the blob from library_get. One commit and a fixed human notice. A stale base or nonregular/missing target refuses.", obj({ ...writeBase,
|
||||
base: { ...writeBase.base, description: "The existing file's blob from library_get; a missing file cannot be deleted" },
|
||||
}, ["universe", "path", "base"]));
|
||||
}
|
||||
}
|
||||
add("read_spec", "spec.read", null, "Read a project's committed north-star spec and its file history. commit is an optional full reachable commit ID. The file commit, rather than HEAD, is what a decision cites. confirmed says the file commit was authored by the business's human.", obj({
|
||||
project: str("Project ID from the business file"),
|
||||
|
||||
@@ -14,9 +14,9 @@ const role = (t, definition, harness = 'pi') => resolvedFor(t, 'bean', d => {
|
||||
d.roles.bean = { definition, credentials: { gitea: { ...d.roles.coder.credentials.gitea } }, vars: { harness, model: 'claude-sonnet-5-5', thinking: 'high' } };
|
||||
});
|
||||
const expected = definition => ['send_message', ...(['companion', 'research'].includes(definition)
|
||||
? ['web_search', 'web_fetch'] : []), ...LOOKUPS];
|
||||
? ['web_search', 'web_fetch'] : []), ...LOOKUPS, 'library_doc_write', 'library_doc_delete'];
|
||||
|
||||
test('M3: per-role snapshots contain six names, or eight with the C2-08 web grant', t => {
|
||||
test('M3: per-role snapshots contain eight names, or ten with the C2-08 web grant', t => {
|
||||
for (const definition of CREATIVE) {
|
||||
const tools = typedTools({ resolved: role(t, definition), library: true, tracker: true, arbiter: true });
|
||||
assert.deepEqual(tools.map(t => t.name), expected(definition), definition);
|
||||
@@ -38,7 +38,7 @@ test('every noncreative role gets lookups when a library is configured, not othe
|
||||
test('lookup schemas require a universe even for get, include paging, and cannot select repo or commit', t => {
|
||||
const tools = typedTools({ resolved: role(t, 'ideas'), library: true });
|
||||
const required = [ ['universe', 'query'], ['universe', 'path'], ['universe', 'text'], ['universe', 'book'], ['universe'] ];
|
||||
for (const [i, tool] of tools.filter(t => t.name.startsWith('library_')).entries()) {
|
||||
for (const [i, tool] of tools.filter(t => LOOKUPS.includes(t.name)).entries()) {
|
||||
assert.deepEqual(tool.parameters.required, required[i]);
|
||||
assert.equal(tool.verb, LOOKUPS[i].replace('_', '.'));
|
||||
assert.equal(tool.action, null);
|
||||
@@ -72,7 +72,7 @@ test('bundle flag reaches both harness tool manifests and creative prompt withou
|
||||
|
||||
test('C2-05c scope schemas expand only find/search/get, keeping manuscript and tool names unchanged', t => {
|
||||
const tools = typedTools({ resolved: role(t, 'ideas'), library: true });
|
||||
assert.deepEqual(tools.map(t => t.name), ['send_message', ...LOOKUPS]);
|
||||
assert.deepEqual(tools.map(t => t.name), ['send_message', ...LOOKUPS, 'library_doc_write', 'library_doc_delete']);
|
||||
for (const name of ['library_find', 'library_search', 'library_get']) {
|
||||
const tool = tools.find(t => t.name === name);
|
||||
assert.deepEqual(tool.parameters.properties.scope.enum, ['canon', 'playground', 'supporting']);
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
// Row 288 M3: creative write tools and independent closed-schema coverage.
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { typedTools, untypedActions } from '../src/tools.mjs';
|
||||
import { resolvedFor } from './helpers.mjs';
|
||||
|
||||
const LOOKUPS = ['library_find', 'library_get', 'library_search', 'library_manuscript', 'library_universe'];
|
||||
const WRITES = ['library_doc_write', 'library_doc_delete'];
|
||||
const CREATIVE = ['companion', 'research', 'ideas', 'proofreader', 'fact-checker', 'continuity'];
|
||||
const role = (t, definition) => resolvedFor(t, 'bean', doc => {
|
||||
doc.roles.bean = { definition, credentials: { gitea: { ...doc.roles.coder.credentials.gitea } }, vars: { harness: 'pi', model: 'claude-sonnet-5-5', thinking: 'high' } };
|
||||
});
|
||||
|
||||
test('C2-02 M3: each creative role has eight tools, or ten with its web grant', t => {
|
||||
for (const definition of CREATIVE) {
|
||||
const resolved = role(t, definition), tools = typedTools({ resolved, library: true, tracker: true, arbiter: true });
|
||||
const expected = ['send_message', ...(['companion', 'research'].includes(definition) ? ['web_search', 'web_fetch'] : []), ...LOOKUPS, ...WRITES];
|
||||
assert.deepEqual(tools.map(tool => tool.name), expected, definition);
|
||||
assert.equal(tools.length, ['companion', 'research'].includes(definition) ? 10 : 8);
|
||||
assert.equal(untypedActions(resolved, tools).includes('library.write'), false);
|
||||
}
|
||||
});
|
||||
|
||||
test('C2-02 schemas: both write tools require universe/path/base, close keys, map verb and action', t => {
|
||||
const tools = typedTools({ resolved: role(t, 'ideas'), library: true });
|
||||
for (const [name, verb, required] of [
|
||||
['library_doc_write', 'library.doc.write', ['universe', 'path', 'base', 'content']],
|
||||
['library_doc_delete', 'library.doc.delete', ['universe', 'path', 'base']],
|
||||
]) {
|
||||
const tool = tools.find(tool => tool.name === name);
|
||||
assert.ok(tool, name); assert.equal(tool.verb, verb); assert.equal(tool.action, 'library.write');
|
||||
assert.equal(tool.parameters.type, 'object'); assert.equal(tool.parameters.additionalProperties, false);
|
||||
assert.deepEqual(tool.parameters.required, required);
|
||||
assert.deepEqual(Object.keys(tool.parameters.properties).sort(), [...required].sort());
|
||||
assert.equal(tool.parameters.properties.universe.type, 'string');
|
||||
assert.equal(tool.parameters.properties.path.type, 'string');
|
||||
assert.deepEqual(tool.parameters.properties.base.type, ['string', 'null']);
|
||||
assert.equal(tool.parameters.properties.base.pattern, '^[a-f0-9]{40}$');
|
||||
if (name === 'library_doc_write') {
|
||||
assert.equal(tool.parameters.properties.content.type, 'string');
|
||||
assert.match(tool.parameters.properties.base.description, /null to create/);
|
||||
} else {
|
||||
assert.match(tool.parameters.properties.base.description, /existing file/);
|
||||
assert.doesNotMatch(tool.parameters.properties.base.description, /create/);
|
||||
}
|
||||
for (const field of ['repo', 'commit', 'business', 'accept', 'project', 'identity', 'author', 'credential', 'agent', 'message'])
|
||||
assert.equal(Object.hasOwn(tool.parameters.properties, field), false, `${name} ${field}`);
|
||||
}
|
||||
});
|
||||
|
||||
test('C2-02 tools: no library, noncreative or no within-role write grant exposes neither write tool', t => {
|
||||
const creative = role(t, 'ideas');
|
||||
assert.equal(typedTools({ resolved: creative }).some(tool => WRITES.includes(tool.name)), false);
|
||||
for (const instance of ['coder', 'reviewer', 'pm', 'chief'])
|
||||
assert.equal(typedTools({ resolved: resolvedFor(t, instance), library: true }).some(tool => WRITES.includes(tool.name)), false);
|
||||
const narrowed = structuredClone(creative);
|
||||
narrowed.limits.authority.withinRole = narrowed.limits.authority.withinRole.filter(action => action !== 'library.write');
|
||||
narrowed.limits.authority.crossRole.push('library.write');
|
||||
assert.deepEqual(typedTools({ resolved: narrowed, library: true }).map(tool => tool.name), ['send_message', ...LOOKUPS]);
|
||||
});
|
||||
@@ -276,3 +276,60 @@ node --test packages/library/tests/
|
||||
|
||||
Fixtures are made-up repos built per test in a temp dir. A marker string stands in for her text, and
|
||||
the survey tests check that it never appears in output.
|
||||
|
||||
### C2-02 supporting writes and human saves
|
||||
|
||||
The host exposes `library.doc.write {universe,path,base,content}` and
|
||||
`library.doc.delete {universe,path,base}` only to a creative session holding
|
||||
`library.write` and a live, unheld Gitea grant for the library project. The
|
||||
broker chooses the project from the universe mapping, the author/committer
|
||||
from the instance identity, and the credential from its grant store. `base`
|
||||
is the served-tree blob id, or `null` to create. Supporting-path checks run
|
||||
before remote effects and against each fetched tip; deletes require an
|
||||
existing `100644` blob. Content is UTF-8 without NUL and at most 1 MiB;
|
||||
agent requests still have the ordinary 64 KiB socket envelope limit.
|
||||
|
||||
Each operation is one plumbing commit with one parent, a fixed
|
||||
`Mosaic: write <path>` or `Mosaic: delete <path>` message, and the trailer
|
||||
`Mosaic-Agent: <business>/<instance>`. Pushes are fast-forward only. A moved
|
||||
remote tip retries once when the target blob still equals `base`; otherwise
|
||||
it refuses `stale-base`. A failed push leaves no queued work or local ref
|
||||
change. A successful push updates served/tracking, rebuilds the index and
|
||||
records `lastPush: {commit,at}`. The broker inserts the exact fixed notice
|
||||
`{notice:"library-write",op,path,commit}` from the writer to the human in
|
||||
that thread, independently of the agent-message citation guard.
|
||||
|
||||
`library.file.save {project,path,base,content}` and
|
||||
`library.revert {project,commit}` are human-only. Project is the business
|
||||
project key. A save reaches any mapped `.md` file under the structural path
|
||||
rules, uses the mapping's `editor` as author and committer, and pushes with
|
||||
slot `library-human`. Revert accepts only a reachable, one-parent,
|
||||
one-path commit with this business's agent trailer, and restores its
|
||||
parent's blob with the agent commit's blob as `base`. The result is
|
||||
`{project,path,reverted,commit}`. Revoking/firing the original writer does
|
||||
not prevent a human revert. Missing editor refuses
|
||||
`library-mapping-invalid`; missing/unsafe human slot refuses
|
||||
`library-human-missing`, before Git. An agent calling either human verb
|
||||
gets `human-required`. Only `library.file.save` joins the larger Console
|
||||
save envelope exception; agent writes retain 64 KiB.
|
||||
|
||||
The write guard excludes concurrent sync and admits a race fetch only for
|
||||
its opaque owner token. The library flock spans fetch/build/push as the
|
||||
brief requires, so readers can receive `library-sync-busy` after the
|
||||
existing five-second lock wait while network Git runs (MVP tradeoff,
|
||||
sage-3628). Publication errors return `outcome-unknown`: served is restored
|
||||
to match old state unless the new state was already published. The remote
|
||||
commit remains; callers must inspect status rather than retry blindly.
|
||||
|
||||
The human push login is `editor.user` (optional in the mapping, required
|
||||
for save/revert), distinct from `user`, which names the fetch bot.
|
||||
An absent editor login refuses `library-human-missing`; no fallback uses
|
||||
the bot's login. Once an agent push succeeds, the broker stores the exact
|
||||
write notice with that pushed commit id before local publication. If
|
||||
publication then fails, the agent receives `outcome-unknown` and the
|
||||
notice remains. Revert can refuse `library-revert-not-agent` until a sync
|
||||
makes the commit reachable from served. No notice outbox or retry is
|
||||
needed. If the Console store itself refuses the notice insert, the caller
|
||||
still receives `outcome-unknown` and a separate metadata-only host log
|
||||
records `library-notice-store-failed` with the business, instance, operation
|
||||
and pushed commit id; it contains no file text or credential.
|
||||
@@ -13,3 +13,5 @@ export { buildIndex, indexFor, clearIndexCache, indexCacheSize } from './index-b
|
||||
export { sees, find, get, search, manuscript, universeInfo, QUERY_REPLY_CAP, LOOKUP_LIMIT, MANUSCRIPT_LINES } from './query.mjs';
|
||||
export { REPLY_LIMITS, resolveReplyLimits, checkReplyLimits } from './limits.mjs';
|
||||
export { GUARD_CODES, FIXED_TEXTS, WEB_CITE_DEFINITIONS, REASON_MAX, GuardError, parseReply, checkReply, libraryReader, guardReply, lastCommits } from './guard.mjs';
|
||||
|
||||
export { writeLibraryDoc, saveLibraryFile, revertLibraryCommit } from './write.mjs';
|
||||
@@ -221,7 +221,9 @@ async function readStateHeld(mirror, { branch } = {}) {
|
||||
finally { if (fd !== undefined) closeSync(fd); }
|
||||
// C2-03b adds `credential`, { kind, user } or null. It is optional, so a state from before it reads.
|
||||
const listed = ['version', 'branch', 'served', 'lastFetch', 'lastError', 'diverged', 'problems'];
|
||||
if (!keys(value, Object.hasOwn(value ?? {}, 'credential') ? [...listed, 'credential'] : listed)
|
||||
const expected = [...listed, ...['credential', 'lastPush'].filter(key => Object.hasOwn(value ?? {}, key))];
|
||||
if (!keys(value, expected)
|
||||
|| !optional(value.lastPush ?? null, v => keys(v, ['commit', 'at']) && SHA.test(v.commit) && utc(v.at))
|
||||
|| !optional(value.credential ?? null, v => keys(v, ['kind', 'user']) && ['slot', 'chief', 'seat', 'human'].includes(v.kind)
|
||||
&& typeof v.user === 'string' && /^[A-Za-z0-9_.-]{1,100}$/.test(v.user))
|
||||
|| value.version !== 1 || value.branch !== branch || value.served !== served
|
||||
|
||||
@@ -14,6 +14,7 @@ import { SAFE, git } from '../../project/src/git.mjs';
|
||||
import { checkMapping } from './mapping.mjs';
|
||||
import { LibraryError } from './problems.mjs';
|
||||
import { readLibrary } from './reader.mjs';
|
||||
import { indexFor } from './index-build.mjs';
|
||||
import { SYNC_STATE_FILE, ensureMirror, withLibraryLock, servedCommit } from './mirror.mjs';
|
||||
|
||||
export const ASKPASS = fileURLToPath(new URL('../bin/askpass', import.meta.url));
|
||||
@@ -32,6 +33,15 @@ const fail = (code, message = code) => { throw new LibraryError(code, message);
|
||||
// One sync per mirror in this process. The library lock covers publication only, so a second
|
||||
// fetch into the same mirror is refused here rather than racing the first.
|
||||
const running = new Set();
|
||||
// C2-02: opaque identity lets only the owning write re-enter push's race fetch.
|
||||
const writers = new Map();
|
||||
export async function withLibraryWriteGuard(mirror, operation) {
|
||||
if (running.has(mirror)) fail('library-sync-busy');
|
||||
const token = Object.freeze({});
|
||||
running.add(mirror); writers.set(mirror, token);
|
||||
try { return await operation(token); }
|
||||
finally { writers.delete(mirror); running.delete(mirror); }
|
||||
}
|
||||
|
||||
// The one command builder for her remote. `kind` is 'fetch' or 'push'. A push sends `commit` to the
|
||||
// branch with no '+', so the remote takes a fast-forward only (D6). `slot` is the file the helper reads.
|
||||
@@ -210,7 +220,7 @@ export async function sync(context, { slot, credential = null, accept = null, co
|
||||
// mirror runs. A hook refusal ([remote rejected]) or any other failure
|
||||
// refuses library-push-failed, and git's output is never kept. It moves no local ref: C2-02 moves the
|
||||
// remote-tracking and served refs after a push.
|
||||
export async function push(context, { commit, credential, retried = false, command = syncCommand, limits = GIT_LIMITS } = {}) {
|
||||
export async function push(context, { commit, credential, retried = false, writeToken = null, command = syncCommand, limits = GIT_LIMITS } = {}) {
|
||||
const mirror = await ensureMirror(context);
|
||||
const mapping = checkMapping(context.business.projects[context.project]);
|
||||
const { branch } = mapping;
|
||||
@@ -233,8 +243,9 @@ export async function push(context, { commit, credential, retried = false, comma
|
||||
if (retried) fail('stale-base', 'her branch moved again during the write');
|
||||
// The race fetch writes the mirror, so it takes sync's guard (dewey-0864 R1). A held guard refuses
|
||||
// library-sync-busy and the caller tries the write again.
|
||||
if (running.has(mirror)) fail('library-sync-busy', 'a sync of this library is running');
|
||||
running.add(mirror);
|
||||
const owned = writeToken !== null && writers.get(mirror) === writeToken;
|
||||
if (running.has(mirror) && !owned) fail('library-sync-busy', 'a sync of this library is running');
|
||||
if (!owned) running.add(mirror);
|
||||
try {
|
||||
const fetch = command('fetch', mapping.remote, opts);
|
||||
if (!(await runGit(fetch.argv, fetch.env, limits)).ok) fail('library-push-failed', 'fetch after a moved tip failed');
|
||||
@@ -243,5 +254,42 @@ export async function push(context, { commit, credential, retried = false, comma
|
||||
catch { fail('library-push-failed', 'fetch after a moved tip found no branch'); }
|
||||
if (!SHA.test(tip)) fail('library-push-failed', 'fetch after a moved tip found no branch');
|
||||
return { pushed: false, raced: true, tip };
|
||||
} finally { running.delete(mirror); }
|
||||
} finally { if (!owned) running.delete(mirror); }
|
||||
}
|
||||
|
||||
// Called under the publication lock by C2-02; the raw state writer stays private.
|
||||
export async function publishLibraryWrite(mirror, { mapping, commit, previous, readSyncState, now = () => new Date() }) {
|
||||
if (!SHA.test(commit) || (previous !== null && !SHA.test(previous))) fail('library-state-invalid');
|
||||
const state = await readSyncState({ branch: mapping.branch });
|
||||
if (state.diverged !== null) fail('library-diverged');
|
||||
if (state.served !== previous) fail('library-state-invalid');
|
||||
const read = await indexFor({ repo: mirror, commit, mapping });
|
||||
const at = now().toISOString();
|
||||
const next = { ...state, served: commit, lastPush: { commit, at }, lastError: null,
|
||||
problems: read.problems.map(p => ({ ...p })) };
|
||||
const tracking = `refs/remotes/library/${mapping.branch}`;
|
||||
// The remote push already succeeded. A local publication failure is outcome-unknown to the
|
||||
// caller; it must never encourage an automatic second push.
|
||||
let moved = false;
|
||||
try {
|
||||
await git(mirror, ['update-ref', 'refs/heads/served', commit, previous ?? ZERO]);
|
||||
moved = true;
|
||||
await git(mirror, ['update-ref', tracking, commit]);
|
||||
writeState(mirror, next);
|
||||
} catch {
|
||||
if (moved) {
|
||||
// A rename may have succeeded before directory fsync failed. Keep the new ref when the
|
||||
// validated state already matches it; otherwise restore served by compare-and-swap.
|
||||
let published = false;
|
||||
try { published = (await readSyncState({ branch: mapping.branch })).served === commit; } catch {}
|
||||
if (!published) {
|
||||
try {
|
||||
await git(mirror, ['update-ref', ...(previous === null
|
||||
? ['-d', 'refs/heads/served', commit] : ['refs/heads/served', previous, commit])]);
|
||||
} catch { fail('outcome-unknown'); }
|
||||
}
|
||||
}
|
||||
fail('outcome-unknown');
|
||||
}
|
||||
return next;
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
// C2-02: one-path plumbing commits on the private bare mirror. No checkout and no pending push.
|
||||
import { lstatSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
||||
import { dirname, join } from 'node:path';
|
||||
import { SAFE, git, gitBytes } from '../../project/src/git.mjs';
|
||||
import { gitEnv, runGit, GIT_LIMITS } from '../../seat/src/checkout.mjs';
|
||||
import { ID_PATTERN } from '../../business/src/vocabulary.mjs';
|
||||
import { checkMapping } from './mapping.mjs';
|
||||
import { classify } from './scope.mjs';
|
||||
import { supportingPath } from './supporting.mjs';
|
||||
import { LibraryError } from './problems.mjs';
|
||||
import { listTree, MAX_BLOB } from './reader.mjs';
|
||||
import { mirrorPath, ensureMirror, withLibraryLock } from './mirror.mjs';
|
||||
import { syncCommand, push, withLibraryWriteGuard, publishLibraryWrite } from './sync.mjs';
|
||||
|
||||
const SHA = /^[a-f0-9]{40}$/;
|
||||
const fail = code => { throw new LibraryError(code, code); };
|
||||
const ident = identity => ({ GIT_AUTHOR_NAME: identity.name, GIT_AUTHOR_EMAIL: identity.email,
|
||||
GIT_COMMITTER_NAME: identity.name, GIT_COMMITTER_EMAIL: identity.email });
|
||||
|
||||
function mappingOf(context) {
|
||||
if (!Object.hasOwn(context.business?.projects ?? {}, context.project)) fail('project-not-configured');
|
||||
return checkMapping(context.business.projects[context.project]);
|
||||
}
|
||||
function contentOf(value) {
|
||||
if (typeof value !== 'string' || value.includes('\0') || Buffer.byteLength(value, 'utf8') > MAX_BLOB
|
||||
|| Buffer.from(value, 'utf8').toString('utf8') !== value) fail('invalid-request');
|
||||
return value;
|
||||
}
|
||||
function credentialOf(credential, human) {
|
||||
const code = human ? 'library-human-missing' : 'library-write-no-grant';
|
||||
if (!credential || credential.kind !== (human ? 'human' : 'seat')
|
||||
|| typeof credential.user !== 'string' || !/^[A-Za-z0-9_.-]{1,100}$/.test(credential.user)
|
||||
|| typeof credential.file !== 'string') fail(code);
|
||||
try {
|
||||
const st = lstatSync(credential.file);
|
||||
if (!st.isFile() || st.uid !== process.getuid() || st.nlink !== 1 || (st.mode & 0o777) !== 0o600 || st.size > 16384) fail(code);
|
||||
} catch { fail(code); }
|
||||
return credential;
|
||||
}
|
||||
// Her save uses the same structural and folded-tree checks; only the supporting rule changes.
|
||||
function pathOf(mapping, universe, path, tree, human) {
|
||||
if (!human) return supportingPath(mapping, universe, path, tree);
|
||||
if (typeof path !== 'string' || classify(mapping, path).scope === 'unmapped') fail('library-write-story-path');
|
||||
const place = classify(mapping, path);
|
||||
const universes = Object.fromEntries(Object.keys(mapping.universes).map(id => [id,
|
||||
{ ...mapping.universes[id], supporting: id === place.universe ? [path.slice(0, path.lastIndexOf('/') + 1)] : [] }]));
|
||||
return supportingPath({ ...mapping, universes }, place.universe, path, tree);
|
||||
}
|
||||
async function ref(mirror, name) {
|
||||
const rows = await git(mirror, ['for-each-ref', '--format=%(refname) %(objectname)', name]);
|
||||
const line = rows.split('\n').find(row => row.startsWith(name + ' '));
|
||||
return line ? line.slice(name.length + 1) : null;
|
||||
}
|
||||
async function ancestor(mirror, older, newer) {
|
||||
const result = await runGit([...SAFE, '-C', mirror, 'merge-base', '--is-ancestor', older, newer], gitEnv());
|
||||
if (result.ok) return true;
|
||||
if (result.why === 'exit 1') return false;
|
||||
fail('library-git');
|
||||
}
|
||||
async function fetchTip(mirror, mapping, credential, command) {
|
||||
const c = command('fetch', mapping.remote, { mirror, branch: mapping.branch, user: credential.user, slot: credential.file });
|
||||
if (!(await runGit(c.argv, c.env)).ok) fail('library-push-failed');
|
||||
const tip = await ref(mirror, `refs/remotes/library/${mapping.branch}`);
|
||||
if (!tip || !SHA.test(tip)) fail('library-push-failed');
|
||||
return tip;
|
||||
}
|
||||
|
||||
async function plumbing(mirror, tip, path, content, identity, message, stage) {
|
||||
const env = { ...gitEnv(), ...ident(identity), GIT_INDEX_FILE: join(stage, 'index'), GIT_CEILING_DIRECTORIES: dirname(mirror) };
|
||||
const run = async (args, input = null) => {
|
||||
const result = await runGit([...SAFE, '-c', 'core.createObject=rename', '-C', mirror, ...args], env, GIT_LIMITS, { stdin: input });
|
||||
if (!result.ok) fail('library-git');
|
||||
return result.out.trimEnd();
|
||||
};
|
||||
rmSync(env.GIT_INDEX_FILE, { force: true });
|
||||
await run(['read-tree', tip]);
|
||||
// --force-remove requires a worktree. Feed a NUL-delimited removal to the bare index.
|
||||
if (content === null) {
|
||||
const removal = join(stage, 'removal');
|
||||
writeFileSync(removal, `0 ${'0'.repeat(40)}\t${path}\0`, { mode: 0o600 });
|
||||
await run(['update-index', '-z', '--index-info'], removal);
|
||||
}
|
||||
else {
|
||||
const file = join(stage, 'content'); writeFileSync(file, content, { mode: 0o600 });
|
||||
const blob = await run(['hash-object', '-w', '--no-filters', file]);
|
||||
// --cacheinfo keeps the path a separate argv item; no pathspec expansion or filters.
|
||||
await run(['update-index', '--add', '--cacheinfo', '100644', blob, path]);
|
||||
}
|
||||
const tree = await run(['write-tree']);
|
||||
return run(['commit-tree', tree, '-p', tip, '-m', message]);
|
||||
}
|
||||
|
||||
async function revertChange(mirror, served, context, mapping) {
|
||||
const commit = context.commit;
|
||||
const refuse = () => fail('library-revert-not-agent');
|
||||
if (!served || !SHA.test(commit)) refuse();
|
||||
let text, parents;
|
||||
try {
|
||||
if (!await ancestor(mirror, commit, served)) refuse();
|
||||
text = await git(mirror, ['show', '-s', '--format=%(trailers:key=Mosaic-Agent,valueonly)', commit]);
|
||||
parents = (await git(mirror, ['show', '-s', '--format=%P', commit])).split(' ');
|
||||
} catch { refuse(); }
|
||||
if (parents.length !== 1 || !SHA.test(parents[0])
|
||||
|| !text.startsWith(context.business.id + '/')
|
||||
|| !ID_PATTERN.test(text.slice(context.business.id.length + 1))) refuse();
|
||||
const before = await listTree(mirror, parents[0]), after = await listTree(mirror, commit);
|
||||
const old = new Map(before.map(e => [e.path, e])), current = new Map(after.map(e => [e.path, e]));
|
||||
const changed = [...new Set([...old.keys(), ...current.keys()])].filter(path =>
|
||||
old.get(path)?.oid !== current.get(path)?.oid || old.get(path)?.mode !== current.get(path)?.mode);
|
||||
if (changed.length !== 1) refuse();
|
||||
const path = changed[0], was = old.get(path), made = current.get(path);
|
||||
if ((was && (was.mode !== '100644' || was.type !== 'blob' || was.size > MAX_BLOB))
|
||||
|| (made && (made.mode !== '100644' || made.type !== 'blob'))) refuse();
|
||||
pathOf(mapping, null, path, after, true);
|
||||
const bytes = was ? await gitBytes(mirror, ['cat-file', 'blob', was.oid], MAX_BLOB) : null;
|
||||
let content = null;
|
||||
if (bytes !== null) {
|
||||
try { content = new TextDecoder('utf-8', { fatal: true, ignoreBOM: true }).decode(bytes); } catch { refuse(); }
|
||||
contentOf(content);
|
||||
}
|
||||
return { path, base: made?.oid ?? null, content, reverted: commit };
|
||||
}
|
||||
|
||||
async function write(context, options, human, revert = false) {
|
||||
const mapping = mappingOf(context);
|
||||
if (human && !mapping.editor) fail('library-mapping-invalid');
|
||||
const credential = credentialOf(options.credential, human);
|
||||
const identity = human ? mapping.editor : options.identity;
|
||||
if (!identity || typeof identity.name !== 'string' || typeof identity.email !== 'string') fail('library-write-refused');
|
||||
if (!human && (typeof options.agent !== 'string' || !options.agent.startsWith(context.business.id + '/'))) fail('library-write-refused');
|
||||
const op = options.op ?? 'write';
|
||||
if (!['write', 'delete'].includes(op)) fail('invalid-request');
|
||||
let change = revert ? null : { path: context.path, base: context.base, content: op === 'delete' ? null : contentOf(context.content) };
|
||||
if (change) {
|
||||
if (change.base !== null && (typeof change.base !== 'string' || !SHA.test(change.base))) fail('invalid-request');
|
||||
pathOf(mapping, context.universe, change.path, [], human);
|
||||
}
|
||||
const mirror = mirrorPath(context);
|
||||
return withLibraryWriteGuard(mirror, writeToken => withLibraryLock(mirror, async lock => {
|
||||
await ensureMirror(context);
|
||||
const state = await lock.readSyncState({ branch: mapping.branch });
|
||||
if (state.diverged !== null) fail('library-diverged');
|
||||
const served = state.served;
|
||||
if (!served) fail('library-commit');
|
||||
if (revert) change = await revertChange(mirror, served, context, mapping);
|
||||
// Check the served tree before any remote effect, then recheck against every fetched tip.
|
||||
pathOf(mapping, context.universe, change.path, await listTree(mirror, served), human);
|
||||
const tracking = `refs/remotes/library/${mapping.branch}`, previous = await ref(mirror, tracking);
|
||||
let pushed = false;
|
||||
const stage = mkdtempSync(join(dirname(mirror), '.library-write-'));
|
||||
try {
|
||||
let tip = await fetchTip(mirror, mapping, credential, options.command ?? syncCommand);
|
||||
for (let attempt = 0; attempt < 2; attempt++) {
|
||||
if (!await ancestor(mirror, served, tip)) fail('library-diverged');
|
||||
const tree = await listTree(mirror, tip);
|
||||
pathOf(mapping, context.universe, change.path, tree, human);
|
||||
const entry = tree.find(e => e.path === change.path);
|
||||
if ((!human && op === 'delete' && (!entry || entry.mode !== '100644' || entry.type !== 'blob'))
|
||||
|| (entry && (entry.mode !== '100644' || entry.type !== 'blob'))
|
||||
|| tree.some(e => e.path.startsWith(change.path + '/') || change.path.startsWith(e.path + '/')))
|
||||
fail('library-write-story-path');
|
||||
if ((entry?.oid ?? null) !== change.base) fail('stale-base');
|
||||
const action = change.content === null ? 'delete' : 'write';
|
||||
const message = `Mosaic: ${action} ${change.path}` + (human ? '' : `\n\nMosaic-Agent: ${options.agent}`);
|
||||
const commit = await plumbing(mirror, tip, change.path, change.content, identity, message, stage);
|
||||
// Broker rechecks live grants/holder immediately before each push.
|
||||
options.beforePush?.();
|
||||
const result = await push(context, { commit, credential, retried: attempt === 1, writeToken, command: options.command ?? syncCommand });
|
||||
if (!result.pushed) { tip = result.tip; continue; }
|
||||
pushed = true;
|
||||
try {
|
||||
// The broker records the pushed commit even if publication fails next (mos-1520).
|
||||
options.onPushed?.({ project: context.project, path: change.path, commit });
|
||||
await publishLibraryWrite(mirror, { mapping, commit, previous: served, readSyncState: lock.readSyncState, now: options.now });
|
||||
} catch { fail('outcome-unknown'); }
|
||||
return { project: context.project, path: change.path, ...(revert ? { reverted: change.reverted } : {}), commit };
|
||||
}
|
||||
fail('stale-base');
|
||||
} finally {
|
||||
rmSync(stage, { recursive: true, force: true });
|
||||
if (!pushed) {
|
||||
if (previous === null) await git(mirror, ['update-ref', '-d', tracking]);
|
||||
else await git(mirror, ['update-ref', tracking, previous]);
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
export const writeLibraryDoc = (context, options = {}) => write(context, options, false);
|
||||
export const saveLibraryFile = (context, options = {}) => write(context, options, true);
|
||||
export const revertLibraryCommit = (context, options = {}) => write(context, options, true, true);
|
||||
@@ -0,0 +1,55 @@
|
||||
// Shared scratch bare-repository fixture for row 288 library and broker tests.
|
||||
import assert from 'node:assert/strict';
|
||||
import { mkdirSync, writeFileSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import { repo, git, mappingEntry, BASE } from './helpers.mjs';
|
||||
import { sync, syncCommand } from '../src/sync.mjs';
|
||||
import { mirrorPath, readSyncState } from '../src/mirror.mjs';
|
||||
|
||||
export const PATH = 'Book One/Notes/Agent.md';
|
||||
export const IDENTITY = { name: 'Bean Fixture', email: '[email protected]' };
|
||||
export const EDITOR = { name: 'Human Fixture', email: '[email protected]' };
|
||||
|
||||
export async function world(t, { business = 'acme' } = {}) {
|
||||
const f = repo(t, { ...BASE, [PATH]: 'before\n',
|
||||
'Book One/Notes/Executable.md': { text: 'exec\n', mode: 0o755 },
|
||||
'Book One/Notes/Link.md': { link: 'Agent.md' } });
|
||||
git(f.repo, 'update-index', '--add', '--cacheinfo', '160000', f.commit, 'Book One/Notes/Submodule.md');
|
||||
git(f.repo, 'commit', '-qm', 'gitlink fixture');
|
||||
const remote = join(f.root, 'remote.git');
|
||||
git(f.root, 'clone', '-q', '--bare', '--no-local', f.repo, remote);
|
||||
const dataRoot = join(f.root, 'data'); mkdirSync(dataRoot, { mode: 0o700 });
|
||||
const entry = { ...mappingEntry(), user: 'human-fixture', editor: { ...EDITOR } };
|
||||
entry.universes.harbor.supporting = ['Book One/Notes/'];
|
||||
const context = { business: { id: business, projects: { books: entry } }, project: 'books', dataRoot };
|
||||
const file = join(f.root, 'fixture-slot'); writeFileSync(file, 'fx288-write\n', { mode: 0o600 });
|
||||
const credential = { kind: 'seat', user: 'bean-fixture', file };
|
||||
// Keep the production refspec exactly: a '+' mutant on push would overwrite the racing remote.
|
||||
let beforePush = null, reject = false;
|
||||
const commands = [];
|
||||
const command = (kind, url, opts) => {
|
||||
commands.push(kind);
|
||||
if (kind === 'push' && beforePush) { const fn = beforePush; beforePush = null; fn(); }
|
||||
const c = syncCommand(kind, url, opts);
|
||||
const argv = [...c.argv];
|
||||
argv.splice(0, 0, '-c', 'protocol.file.allow=always');
|
||||
const at = argv.indexOf(url);
|
||||
assert.ok(at >= 0);
|
||||
argv[at] = kind === 'push' && reject ? join(f.root, 'missing.git') : remote;
|
||||
return { argv, env: c.env };
|
||||
};
|
||||
await sync(context, { credential, command });
|
||||
const mirror = mirrorPath(context);
|
||||
const refs = () => git(mirror, 'for-each-ref', '--format=%(refname) %(objectname)');
|
||||
const remoteHead = () => git(remote, 'rev-parse', 'main');
|
||||
const state = () => readSyncState(mirror, { branch: 'main' });
|
||||
const external = (path, content) => {
|
||||
writeFileSync(join(f.repo, path), content); git(f.repo, 'add', '-A'); git(f.repo, 'commit', '-qm', 'external');
|
||||
git(f.repo, 'push', '-q', remote, 'HEAD:main');
|
||||
};
|
||||
const args = (extra = {}) => ({ ...context, universe: 'harbor', path: PATH,
|
||||
base: git(mirror, 'rev-parse', `served:${PATH}`), content: 'after\n', ...extra });
|
||||
const opts = { credential, identity: IDENTITY, agent: `${business}/bean`, command };
|
||||
return { ...f, remote, context, entry, file, credential, command, mirror, refs, remoteHead, state, external, args, opts,
|
||||
race: fn => { beforePush = fn; }, reject: () => { reject = true; }, commands };
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
// Row 288: real bare-repository plumbing and remote effects. All text and credentials are fixtures.
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { writeFileSync, existsSync, renameSync, mkdirSync, rmSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import { git } from './helpers.mjs';
|
||||
import { sync, push, withLibraryWriteGuard } from '../src/sync.mjs';
|
||||
import { servedCommit, withLibraryLock, readSyncState, SYNC_STATE_FILE } from '../src/mirror.mjs';
|
||||
import { publishLibraryWrite } from '../src/sync.mjs';
|
||||
import { checkMapping } from '../src/mapping.mjs';
|
||||
import { writeLibraryDoc, saveLibraryFile, revertLibraryCommit } from '../src/write.mjs';
|
||||
import { world, PATH, EDITOR } from './write-fixture.mjs';
|
||||
|
||||
test('C2-02: write/delete each publish one parent, seat ident, fixed trailer, index and lastPush', async t => {
|
||||
const f = await world(t), parent = f.remoteHead();
|
||||
const saved = await writeLibraryDoc(f.args(), f.opts);
|
||||
assert.deepEqual(saved, { project: 'books', path: PATH, commit: f.remoteHead() });
|
||||
assert.equal(git(f.remote, 'show', `${saved.commit}:${PATH}`), 'after');
|
||||
assert.equal(git(f.remote, 'show', '-s', '--format=%P', saved.commit), parent);
|
||||
assert.equal(git(f.remote, 'show', '-s', '--format=%an <%ae>|%cn <%ce>', saved.commit),
|
||||
'Bean Fixture <[email protected]>|Bean Fixture <[email protected]>');
|
||||
assert.equal(git(f.remote, 'show', '-s', '--format=%B', saved.commit), `Mosaic: write ${PATH}\n\nMosaic-Agent: acme/bean`);
|
||||
assert.equal(await servedCommit(f.mirror), saved.commit);
|
||||
assert.equal((await f.state()).lastPush.commit, saved.commit);
|
||||
assert.equal(existsSync(join(f.mirror, 'index')), false);
|
||||
const gone = await writeLibraryDoc(f.args({ content: undefined }), { ...f.opts, op: 'delete' });
|
||||
assert.equal(git(f.remote, 'show', '-s', '--format=%P', gone.commit), saved.commit);
|
||||
assert.equal(git(f.remote, 'ls-tree', '-r', gone.commit, '--', PATH), '');
|
||||
assert.equal(git(f.remote, 'show', '-s', '--format=%B', gone.commit), `Mosaic: delete ${PATH}\n\nMosaic-Agent: acme/bean`);
|
||||
});
|
||||
|
||||
test('C2-02 M12: each path refusal leaves every ref and remote unchanged', async t => {
|
||||
const f = await world(t);
|
||||
const paths = ['Book One/Chapters/Chapter 01.md', 'Book One/Playground/What If.md', 'Inbox Note.md',
|
||||
'Book One/Notes/X.canvas', 'Book One/Notes/X.base', 'Book One/Notes/X.png', 'Second World/Chapters/01.md',
|
||||
'/Book One/Notes/X.md', 'Book One/Notes/../X.md', 'Book One/./Notes/X.md', 'Book One//Notes/X.md',
|
||||
'Book One/Notes/X\\Y.md', 'Book One/Notes/X\nY.md', 'Book One/Notes/Link.md',
|
||||
'Book One/Notes/agent.md', `Book One/Notes/${'é'.repeat(512)}.md`];
|
||||
for (const path of paths) {
|
||||
const before = f.refs(), head = f.remoteHead();
|
||||
await assert.rejects(writeLibraryDoc(f.args({ path, base: null }), f.opts), { code: 'library-write-story-path' }, path);
|
||||
assert.equal(f.refs(), before); assert.equal(f.remoteHead(), head);
|
||||
}
|
||||
});
|
||||
|
||||
test('C2-02 Q4: delete requires an existing 100644 blob; null base never deletes a missing path', async t => {
|
||||
const f = await world(t);
|
||||
for (const path of ['Book One/Notes/Missing.md', 'Book One/Notes/Executable.md', 'Book One/Notes/Link.md', 'Book One/Notes/Submodule.md']) {
|
||||
const before = f.refs();
|
||||
await assert.rejects(writeLibraryDoc(f.args({ path, base: null, content: undefined }), { ...f.opts, op: 'delete' }),
|
||||
{ code: 'library-write-story-path' });
|
||||
assert.equal(f.refs(), before);
|
||||
}
|
||||
});
|
||||
|
||||
test('C2-02: stale base and failed push keep remote/served/tracking refs unchanged', async t => {
|
||||
const f = await world(t), refs = f.refs(), head = f.remoteHead();
|
||||
await assert.rejects(writeLibraryDoc(f.args({ base: 'a'.repeat(40) }), f.opts), { code: 'stale-base' });
|
||||
assert.equal(f.refs(), refs); assert.equal(f.remoteHead(), head);
|
||||
f.reject();
|
||||
await assert.rejects(writeLibraryDoc(f.args(), f.opts), { code: 'library-push-failed' });
|
||||
assert.equal(f.refs(), refs); assert.equal(f.remoteHead(), head);
|
||||
});
|
||||
|
||||
test('C2-02: a moved unrelated blob retries once on the new parent, changed target refuses', async t => {
|
||||
const f = await world(t);
|
||||
const args = f.args(); let raced;
|
||||
f.race(() => { f.external('Inbox Note.md', 'another\n'); raced = f.remoteHead(); });
|
||||
const saved = await writeLibraryDoc(args, f.opts);
|
||||
assert.equal(git(f.remote, 'show', '-s', '--format=%P', saved.commit), raced);
|
||||
assert.equal(git(f.remote, 'show', `${saved.commit}:Inbox Note.md`), 'another');
|
||||
const g = await world(t), before = g.refs();
|
||||
g.race(() => g.external(PATH, 'raced target\n'));
|
||||
await assert.rejects(writeLibraryDoc(g.args(), g.opts), { code: 'stale-base' });
|
||||
assert.equal(g.refs(), before); assert.equal(git(g.remote, 'show', `main:${PATH}`), 'raced target');
|
||||
});
|
||||
|
||||
test('C2-02: create with null base; human revert restores parent and does not require seat credentials', async t => {
|
||||
const f = await world(t);
|
||||
const made = await writeLibraryDoc(f.args({ path: 'Book One/Notes/New.md', base: null, content: 'new\n' }), f.opts);
|
||||
const options = { credential: { ...f.credential, kind: 'human' }, command: f.command };
|
||||
const reverted = await revertLibraryCommit({ ...f.context, commit: made.commit }, options);
|
||||
assert.deepEqual(reverted, { project: 'books', path: 'Book One/Notes/New.md', reverted: made.commit, commit: f.remoteHead() });
|
||||
assert.equal(git(f.remote, 'ls-tree', '-r', reverted.commit, '--', reverted.path), '');
|
||||
assert.equal(git(f.remote, 'show', '-s', '--format=%an <%ae>|%cn <%ce>', reverted.commit),
|
||||
'Human Fixture <[email protected]>|Human Fixture <[email protected]>');
|
||||
assert.equal(git(f.remote, 'show', '-s', '--format=%(trailers:key=Mosaic-Agent,valueonly)', reverted.commit), '');
|
||||
});
|
||||
|
||||
test('C2-02: revert requires reachable business trailer and refuses a later path change', async t => {
|
||||
const f = await world(t), options = { credential: { ...f.credential, kind: 'human' }, command: f.command };
|
||||
await assert.rejects(revertLibraryCommit({ ...f.context, commit: f.commit }, options), { code: 'library-revert-not-agent' });
|
||||
const saved = await writeLibraryDoc(f.args(), f.opts);
|
||||
await saveLibraryFile({ ...f.context, path: PATH, base: git(f.mirror, 'rev-parse', `served:${PATH}`), content: 'human later\n' }, options);
|
||||
await assert.rejects(revertLibraryCommit({ ...f.context, commit: saved.commit }, options), { code: 'stale-base' });
|
||||
});
|
||||
|
||||
test('C2-02: human save reaches story, preserves UTF-8 bytes and uses editor without a trailer', async t => {
|
||||
const f = await world(t), path = 'Book One/Chapters/Chapter 01.md';
|
||||
const content = '# Her text\n' + 'é'.repeat(40000) + '\n\n';
|
||||
const saved = await saveLibraryFile({ ...f.context, path, base: git(f.mirror, 'rev-parse', `served:${path}`), content },
|
||||
{ credential: { ...f.credential, kind: 'human' }, command: f.command });
|
||||
assert.equal(git(f.remote, 'show', '-s', '--format=%an <%ae>', saved.commit), 'Human Fixture <[email protected]>');
|
||||
// git helper trims; compare the stored blob byte count separately.
|
||||
assert.equal(Number(git(f.remote, 'cat-file', '-s', `${saved.commit}:${path}`)), Buffer.byteLength(content));
|
||||
});
|
||||
|
||||
test('C2-02: invalid content/editor/human credential refuse before any git command', async t => {
|
||||
const f = await world(t), before = f.refs();
|
||||
for (const content of ['nul\0', '\ud800', 'x'.repeat(1024 * 1024 + 1)])
|
||||
await assert.rejects(writeLibraryDoc(f.args({ content }), f.opts), { code: 'invalid-request' });
|
||||
const args = { ...f.context, path: PATH, base: null, content: 'x' };
|
||||
writeFileSync(join(f.mirror, 'unexpected'), 'any local git inspection must refuse\n');
|
||||
delete f.entry.editor;
|
||||
await assert.rejects(saveLibraryFile(args, { credential: { ...f.credential, kind: 'human' }, command: f.command }), { code: 'library-mapping-invalid' });
|
||||
await assert.rejects(revertLibraryCommit({ ...f.context, commit: f.commit }, { credential: { ...f.credential, kind: 'human' }, command: f.command }), { code: 'library-mapping-invalid' });
|
||||
f.entry.editor = EDITOR;
|
||||
const n = f.commands.length;
|
||||
await assert.rejects(saveLibraryFile(args, { credential: { kind: 'human', user: 'human', file: '/absent' }, command: f.command }), { code: 'library-human-missing' });
|
||||
await assert.rejects(revertLibraryCommit({ ...f.context, commit: f.commit }, { credential: { kind: 'human', user: 'human', file: '/absent' }, command: f.command }), { code: 'library-human-missing' });
|
||||
assert.equal(f.commands.length, n); assert.equal(f.refs(), before);
|
||||
});
|
||||
|
||||
|
||||
test('C2-02 guard: only the exact owning token permits a push race fetch; sync refuses busy', async t => {
|
||||
const f = await world(t), commit = f.remoteHead();
|
||||
f.external('Inbox Note.md', 'remote moved\n');
|
||||
await withLibraryWriteGuard(f.mirror, async token => {
|
||||
await assert.rejects(sync(f.context, { credential: f.credential, command: f.command }), { code: 'library-sync-busy' });
|
||||
for (const writeToken of [null, {}, Object.freeze({})])
|
||||
await assert.rejects(push(f.context, { commit, credential: f.credential, command: f.command, writeToken }), { code: 'library-sync-busy' });
|
||||
const raced = await push(f.context, { commit, credential: f.credential, command: f.command, writeToken: token });
|
||||
assert.equal(raced.raced, true); assert.equal(raced.tip, f.remoteHead());
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
test('C2-02 publication F1: failed state rename restores served to match prior state', async t => {
|
||||
const f = await world(t), previous = await servedCommit(f.mirror);
|
||||
const commit = git(f.mirror, 'commit-tree', git(f.mirror, 'rev-parse', `${previous}^{tree}`), '-p', previous, '-m', 'candidate');
|
||||
const statePath = join(f.mirror, SYNC_STATE_FILE), backup = join(f.root, 'state-backup');
|
||||
await withLibraryLock(f.mirror, async lock => {
|
||||
// A filesystem failure after the locked state read, before state publication. The original
|
||||
// state is saved outside the mirror and restored after the failed rename.
|
||||
const read = async options => {
|
||||
const state = await lock.readSyncState(options);
|
||||
renameSync(statePath, backup); mkdirSync(statePath, { mode: 0o700 });
|
||||
return state;
|
||||
};
|
||||
try {
|
||||
await assert.rejects(publishLibraryWrite(f.mirror, { mapping: checkMapping(f.entry), commit, previous, readSyncState: read }),
|
||||
{ code: 'outcome-unknown' });
|
||||
assert.equal(git(f.mirror, 'rev-parse', 'served'), previous);
|
||||
} finally { rmSync(statePath, { recursive: true, force: true }); renameSync(backup, statePath); }
|
||||
});
|
||||
assert.equal((await readSyncState(f.mirror, { branch: 'main' })).served, previous);
|
||||
});
|
||||
|
||||
|
||||
test('C2-02 E1: a dotted business id never matches another business trailer', async t => {
|
||||
const f = await world(t, { business: 'acme.co' });
|
||||
writeFileSync(join(f.repo, PATH), 'foreign seat change\n');
|
||||
git(f.repo, 'add', '-A'); git(f.repo, 'commit', '-qm', 'Foreign\n\nMosaic-Agent: acme-co/bean');
|
||||
const foreign = git(f.repo, 'rev-parse', 'HEAD');
|
||||
git(f.repo, 'push', '-q', f.remote, 'HEAD:main');
|
||||
await sync(f.context, { credential: f.credential, command: f.command });
|
||||
const before = f.refs();
|
||||
await assert.rejects(revertLibraryCommit({ ...f.context, commit: foreign },
|
||||
{ credential: { ...f.credential, kind: 'human' }, command: f.command }), { code: 'library-revert-not-agent' });
|
||||
assert.equal(f.refs(), before); assert.equal(f.remoteHead(), foreign);
|
||||
});
|
||||
@@ -68,14 +68,14 @@ try {
|
||||
// the system config, Business can't resolve authority and Settings has
|
||||
// no notifier. A refusal printed here names no path (redactor).
|
||||
// --send without a bus refuses to start rather than serve a composer that can't send.
|
||||
let bus = null, send = null, settingsWrite = null, projectFiles = null, projectFile = null, libraryFile = null, libraryStatus = null, librarySync = null, libraryRevert = null, projectSave = null, chat = null, nameWrite = null, assistantStop = null, instancesWrite = null, routesWrite = null, agentWrite = null, decisionWrite = null, busLine, system = null;
|
||||
let bus = null, send = null, settingsWrite = null, projectFiles = null, projectFile = null, libraryFile = null, libraryStatus = null, librarySync = null, libraryRevert = null, librarySave = null, projectSave = null, chat = null, nameWrite = null, assistantStop = null, instancesWrite = null, routesWrite = null, agentWrite = null, decisionWrite = null, busLine, system = null;
|
||||
let processing = null, ownerCall = null;
|
||||
try {
|
||||
system = loadSystem();
|
||||
const { dataRoot } = system;
|
||||
const call = ownerCall = busCaller({ dataRoot, socket: socketPath(dataRoot), business: options.business ?? null });
|
||||
// FED R6 (#1800): the bus-backed options, shared with each granted user's session (console-bus.mjs).
|
||||
({ processing, chat, bus, projectFiles, projectFile, libraryFile, libraryStatus, send, librarySync, libraryRevert, settingsWrite, projectSave, nameWrite, assistantStop, instancesWrite, routesWrite, agentWrite } = consoleBus(call, { send: sendOn }));
|
||||
({ processing, chat, bus, projectFiles, projectFile, libraryFile, libraryStatus, send, librarySync, libraryRevert, librarySave, settingsWrite, projectSave, nameWrite, assistantStop, instancesWrite, routesWrite, agentWrite } = consoleBus(call, { send: sendOn }));
|
||||
// Row 252 (#1753): Approve and Deny on grant requests, through the console wrapper, only with
|
||||
// --sso and --send.
|
||||
if (ssoOn && sendOn) decisionWrite = consoleCall({ dataRoot, socket: socketPath(dataRoot), business: options.business ?? null });
|
||||
@@ -107,7 +107,7 @@ try {
|
||||
}
|
||||
let server;
|
||||
try {
|
||||
server = await startServer({ board: options.board, port: options.port, bus, reads, preview: options.preview, sso, proof, send, settingsWrite, projectFiles, projectFile, libraryFile, libraryStatus, librarySync, libraryRevert, projectSave, chat, nameWrite, assistantStop, instancesWrite, routesWrite, agentWrite, processing, decisionWrite, userOptions, federation: system?.federation ?? null });
|
||||
server = await startServer({ board: options.board, port: options.port, bus, reads, preview: options.preview, sso, proof, send, settingsWrite, projectFiles, projectFile, libraryFile, libraryStatus, librarySync, libraryRevert, librarySave, projectSave, chat, nameWrite, assistantStop, instancesWrite, routesWrite, agentWrite, processing, decisionWrite, userOptions, federation: system?.federation ?? null });
|
||||
} catch (err) { sso?.close(); throw err; }
|
||||
let testerServer = null;
|
||||
if (testerSocket) {
|
||||
|
||||
@@ -30,10 +30,11 @@ export function consoleBus(call, { send: sendOn = false, owner = null } = {}) {
|
||||
projectFiles: args => call('project.files', args),
|
||||
projectFile: args => call('project.file', args),
|
||||
libraryFile: args => call('library.file', args), libraryStatus: args => call('library.status', args),
|
||||
send: null, librarySync: null, libraryRevert: null, settingsWrite: null, projectSave: null, nameWrite: null, assistantStop: null, instancesWrite: null, routesWrite: null, agentWrite: null,
|
||||
send: null, librarySync: null, libraryRevert: null, librarySave: null, settingsWrite: null, projectSave: null, nameWrite: null, assistantStop: null, instancesWrite: null, routesWrite: null, agentWrite: null,
|
||||
};
|
||||
if (sendOn) {
|
||||
o.send = args => call('message.send', args); o.librarySync = args => call('library.sync', args); o.libraryRevert = args => call('library.revert', args); o.settingsWrite = args => call('limits.set', args);
|
||||
o.librarySave = args => call('library.file.save', args);
|
||||
o.projectSave = args => call('project.file.save', args); o.nameWrite = args => call('name.set', args); o.assistantStop = args => call('assistant.stop', args);
|
||||
o.instancesWrite = args => call('instances.set', args); o.routesWrite = args => call('routes.set', args); o.agentWrite = agentCaller(call, AGENT_VERBS);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
// Row 288: the Console's save option carries only the caller's args over its own transport.
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { consoleBus, userOptionsFor } from '../src/console-bus.mjs';
|
||||
|
||||
test('C2-02: library save option is absent without --send and forwards no editor or identity', async () => {
|
||||
const calls = [], call = async (verb, args) => { calls.push({ verb, args }); return { commit: 'a'.repeat(40) }; };
|
||||
assert.equal(consoleBus(call).librarySave, null);
|
||||
const args = { project: 'books', path: 'Book One/Chapters/01.md', base: 'b'.repeat(40), content: 'her text\n' };
|
||||
assert.equal((await consoleBus(call, { send: true }).librarySave(args)).commit, 'a'.repeat(40));
|
||||
assert.deepEqual(calls, [{ verb: 'library.file.save', args }]);
|
||||
assert.deepEqual(Object.keys(calls[0].args).sort(), ['base', 'content', 'path', 'project']);
|
||||
});
|
||||
|
||||
test('C2-02: user save option uses that user transport; owner transport is never called', async () => {
|
||||
const calls = [];
|
||||
const options = userOptionsFor({ owner: () => { throw Error('owner transport touched'); }, send: true,
|
||||
transport: user => async (verb, args) => { calls.push({ user, verb, args }); } });
|
||||
const args = { project: 'books', path: 'Book One/Chapters/01.md', base: null, content: 'fixture' };
|
||||
await options({ user: 'reader' }).librarySave(args);
|
||||
assert.deepEqual(calls, [{ user: 'reader', verb: 'library.file.save', args }]);
|
||||
});
|
||||
Reference in new issue
Block a user