Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a5370b198e | ||
|
|
c2338ed588 |
No files matched your search
@@ -5532,3 +5532,9 @@ claim. Receipts and limitations in agents/lena/work/queue-296/BUILD.md and run-r
|
||||
- 2026-10-24 (rockerduck, row286 / #1791 r6, sage-4112 / #1825): corrected only two fixture constants to unique19-character values after the landing secrets check refused r5. No waiver/guard/assertion/product change. Added-package-lines secrets grep empty; touched6/6+24/24, library151/151, bus981/981 on fresh shortTMP timeout-k10; both exact new values absent from all four outputs. Mutants not rerun as directed. Plain evidence in agents/rockerduck/work/queue-286/BUILD.md and gate-r6; new manifest/push and round3 follow.
|
||||
|
||||
2026-10-24T14:37:02Z | Sage | Row 286 (#1791, C2-03b, Rockerduck on Codex, Boyd and Dewey) LANDED 5d7801dc1. Before: refactor at d55944edb. After: r6 candidate 75f6d56b1's 19 commits cherry-picked onto d55944edb. The one conflict (64f1ff950, BUILD-LOG.md and SESSIONS.md) is append-only and kept both sides, with 0 lines removed against either parent. Apart from the logs, the tree equals the candidate except on the 6 paths of my doc commit d55944edb. Reviews: round 2 (r5 ec8cb10c0) Boyd c31584 rev 3928, Dewey c31585 rev 3929; round 3 (r6) Boyd c31587 rev 3942, Dewey c31588 rev 3943. Sage posted the requests (c31583, c31586) because Rockerduck has no token. Gate GREEN on r5's tree e9c73ab96 at 14:14:11Z, then the push stopped at push-check STOP secrets. The guard came in today with #1825. Two added fixture constants (SLOT_TOKEN in library-credential.test.mjs, SEAT_TOKEN in sync.test.mjs) were 31 characters and matched its token-assignment pattern. sage-4112: no waiver (mos-1570) and no guard edit; r6 shortened only those two values to 19 characters, with reviewers confirming the delta. Gate GREEN again on 5d7801dc1 at 14:36:42Z with its own npm ci: 17 package suites, 4251 tests, 0 fail, 1 skip, which is the allowed FED R7 entry whose runner passed 1/1. All 12 scripts/test-*.sh rc 0. Neither old nor new fixture values appear in any of the 29 run outputs. Mutants: Rockerduck killed P1, P2 and B285-M1 (gitea-grant X-G1). For Dewey's N1 I also ran B285-M1 against library-credential.test.mjs; T7 and T9 failed (4/6) and passed after the restore. sage-4100 (chief grant seeded as fixture until C2-21) is recorded in the 285 entry. Overlap sweep found no in-review row on the 9 code paths. push-check FF_OK, NO_BINARY, SECRETS_CLEAN, NO_ATTRIBUTION. #1791 closed with c31591. No P19 task names row 286, so there is no Vikunja task to close. Brief template gains a sage-4112 line: test credentials stay under 24 characters. The live stack is not re-pinned.
|
||||
|
||||
- 2026-10-24 — steelbeak — #1792 / row 287 C2-21 before: library grants still fork; creative and chief grants refuse. Recovered Megavolt's binding design on 18334d713 and documented current-code delta in agents/steelbeak/work/queue-287/BUILD.md. No tests or implementation yet; Sage holds test clearance, scope delta reported in steelbeak-0001.
|
||||
|
||||
- 2026-10-24 — steelbeak — #1792 / row 287 C2-21 after: library grants use admin collaborator PUT, creative write/chief read levels, definition scopes, exact publish-wide reach and kind/level binding; business fence and chief install identity/publish updated; T7/T9 seeds replaced by real minting. Scratch Gitea F-A/F-B pass, F-C owner control negative recorded, F-D normal/force control positive. Verified bus 998, business 283, CLI 989 plus dedicated FED 1 (one expected CLI skip), Docker 2, install 27, all 12 shell gates, M1-M11 killed and restored; token audit hits0 and owned-process/container cleanup clean. Test fixture corrections approved under mos-1590/sage-4139; non-executable land-work launcher rc126 resolved by sage-4141 bash invocation. Evidence and manifest at agents/steelbeak/work/queue-287/; handing off to Boyd, then Dewey, with no live change or landing.
|
||||
|
||||
- 2026-10-24 — steelbeak — #1792 / row 287 first-review correction, boyd-0034: frozen candidate c2338ed58 approved, no blocking code findings. Product/test diff-check is clean; the full committed diff has trailing whitespace in preserved raw RED/mutant logs, correcting the earlier unqualified claim. Added verified old-test numstats in BUILD.md as a separate doc-only follow-up under lead137. Queue round1 opened at rev3963; automatic request failed at rev3964 for missing Steelbeak credential, reported to Sage without workaround. No product edits, tests or slot use.
|
||||
@@ -0,0 +1,427 @@
|
||||
# Row 287 design notes: C2-21, the grant executor for a library project (#1792)
|
||||
|
||||
Builder Megavolt, reviewer Dewey, gate owner Sage (sage-2910, Q226, mos-1310). Branch megavolt/row287, a
|
||||
doc-only draft off 94c33a43c, written during Gizmoduck's slot with local git only. The build starts on the
|
||||
candidates of row 285 (C2-08b, Scrooge) and row 226 (C2-13b, Gosalyn) and rebases onto their landings
|
||||
(Q177). Nothing here has been run.
|
||||
|
||||
The brief is "C2-21 the grant executor for a library project" in docs/plans/2026-10-20_case2-rows.md at
|
||||
94c33a43c. Its gate: Dewey approves the manifest; the bus suite, the Gitea Docker test and the install test
|
||||
are green on Sage's gate, along with every `scripts/test-*.sh`; a search of the test run's output for the
|
||||
fixture admin and seat tokens finds nothing.
|
||||
|
||||
## Sources (line cites at 94c33a43c)
|
||||
|
||||
- `packages/bus/src/gitea-grant.mjs`: the admin api (`adminApi`, no collaborator call yet), the seat api
|
||||
(`forkGet`, `fork`, `repos`), `identitySha256`, `REPO_PAGE`/`REPO_PAGES`.
|
||||
- `packages/bus/src/broker.mjs`: `#giteaRole` :2132, `#giteaTargets` :2149, `#giteaGrantBinding` and its
|
||||
block :2167-2196, `#giteaRecheck` :2200, `#giteaSteps` step 4 :2425-2441 and step 5 :2442-2473, the
|
||||
`grant.applied` body and notice :2480-2488, the host refusals `host-creative-role` :1106 and :1254.
|
||||
- `packages/business/src/business.mjs` :266-288: a `kind: "library"` project goes through `checkMapping`, and
|
||||
:286 refuses any role whose `publish` names a library project.
|
||||
- `packages/library/src/mapping.mjs`: a library entry carries `remote`, `branch` and an optional `user`.
|
||||
- `packages/bus/tests/gitea-grant.test.mjs` :297-334 pins the `grant.applied` body (`forks: [...]`) and the
|
||||
block; `fake-gitea-api.mjs` keeps collaborators as a list of logins with no permission.
|
||||
- `packages/bus/tests/gitea-grant-docker.test.mjs`: the scratch gitea/gitea:1.27.1 container; it uses
|
||||
`os.tmpdir()`.
|
||||
|
||||
## 1. Rulings, blockers and questions
|
||||
|
||||
- B1, the publish rule. `business.mjs:286` refuses any `publish` entry that names a library project
|
||||
("library repositories cannot be published by agents"). The brief's chief needs `publish: ["books"]`, and a
|
||||
creative seat needs the library project in `publish` for `#giteaRole` and `#giteaTargets` to pass. No test
|
||||
under packages/*/tests or scripts pins the :286 message (positive control: "publish names" hits
|
||||
agent-identity.test.mjs).
|
||||
- Ruling sage-2916 (18:42Z 10-22), files-owned extension: row 287 owns `packages/business/src/business.mjs`
|
||||
:283-291 and the `packages/business/tests/business.test.mjs` cases for it. Row 285 leaves business.mjs
|
||||
alone. Conditions:
|
||||
- The refusal is rewritten, not deleted. A library project in `publish` loads only for a creative
|
||||
instance (`CREATIVE_DEFINITIONS`) or the chief of staff, each with a gitea credential. Any other
|
||||
instance still refuses, with a new message that names the rule. Tests cover both sides. The grant path
|
||||
serves an allowed one with collaborator reach, never a fork. (To confirm on the base: how business.mjs
|
||||
names the chief, by definition `chief-of-staff` or by `launch.by`.)
|
||||
- Before in-review, grep 'packages/*/tests/*' and `scripts/test-*.sh` again for the old message text,
|
||||
with a positive control.
|
||||
- PRD 1.15 REQ-LIB-6 is the authority for this. If Mos changes 1.15 (sage-2914, `library.write`
|
||||
routing), check this text against what lands.
|
||||
- Q1 to Q4 below: Sage accepted them as stated assumptions (sage-2916). Dewey checks them in the round.
|
||||
- Q1, the hash. `identitySha256` covers identity, publish and remotes, not a project's kind or the role's
|
||||
definition name, and the level comes from the definition name. Proposal: add a `library` key
|
||||
(`{project: level}`) to the hash input only when a publish project is a library, so every non-library
|
||||
grant's hash stays byte-identical and no open grant turns `decision-mismatch` on upgrade.
|
||||
- Q2, the event. `grant.applied` and its notice carry `forks`, and the test at :322 pins that body.
|
||||
Proposal: `forks` stays (empty when every target is a library), and a `repositories:
|
||||
[{project, repository, level}]` key appears only when a library target exists. The notice names them the
|
||||
same way.
|
||||
- Q3, mixed raises. A creative raise must name only library projects (mos-1320 item 2). The chief may
|
||||
name a library project next to forked ones in one raise; step 4 forks the others and adds the collaborator
|
||||
on the library one. Proposal: allow that, and test it. Under sage-2916, any other non-creative instance
|
||||
can't hold a library project in `publish` at all, so the business file refuses it before any raise.
|
||||
- Q4, the token search for the Docker test. The fake API's tokens are fixtures and can be grepped. The
|
||||
Docker test's tokens are minted by the container at run time, so the gate grep can't know them in advance.
|
||||
Proposal: the Docker test captures its own diagnostics and asserts that no token it minted appears in
|
||||
them, and the gate greps the run's output for every fake-API fixture token.
|
||||
|
||||
### Early look, Dewey (dewey-0862, 18:53Z 10-22, on 5bcfef0ad, read only), folded
|
||||
|
||||
These replace the matching proposals in sections 2 and 3 where they differ.
|
||||
|
||||
- Q2 accepted. `forks` stays and is `[]` when every target is a library. `repositories:
|
||||
[{project, repository, level}]` appears only when a library target exists. `repository` is the remote
|
||||
URL, the same form as `forks`; the `full_name` that step 5 matched is asserted in a test and doesn't go
|
||||
in the body. The only src reader of `forks` is the notice (:2483). The notice drops its forks clause when
|
||||
the list is empty and adds "repositories: <url> (<level>)". A non-library notice keeps today's text.
|
||||
- The read line: "The token can read every path of this repository. It can't push to it, because the user
|
||||
is a read collaborator." The seat scope is still `write:repository` (:2189).
|
||||
- B-a: `\nForks:` (:2187) prints only when a fork target exists.
|
||||
- B-b: "Step 5 checks that this is only its forks." (:2193) is unchanged with no library target. With one, it
|
||||
reads "Step 5 checks that this is only its forks and the repositories above at their level."
|
||||
- B-c: `others` (:2181, :2194) splits. An untargeted library project in publish gets its own line, "It may
|
||||
also reach these repositories as a collaborator: <url> (<level>)", because a revoke leaves the
|
||||
collaborator entry and step 5 accepts it publish-wide. Forked others keep today's line. L8 covers B-a to
|
||||
B-c.
|
||||
- Q1 holds. Test: a project flipped to `kind: "library"` between the raise and the recheck gives
|
||||
`decision-mismatch` (L10).
|
||||
- Q3 holds. Library projects stay out of step 5's fork upstream set (:2446), so a fork of her repository
|
||||
refuses `gitea-reach-wider`; L4 covers it.
|
||||
- Q4 holds if the assertion covers what the test prints, TAP stdout and stderr, not only an in-test
|
||||
buffer. The Docker test prints one line, "minted N, hits 0", for the gate to grep.
|
||||
- Step 5 compares exactly the keys `admin`, `push` and `pull`. BUILD.md records F-B's actual `permissions`
|
||||
object, so an extra Gitea key can't pass or fail by accident.
|
||||
- The chief is named by definition `chief-of-staff`, not `launch.by`, matching broker `STAFF_TO` (:240) and
|
||||
row 286 (dewey-0860). A fired chief keeps its roles entry (instance-settings.mjs:355), so B-2 also loads a
|
||||
fired chief's entry.
|
||||
|
||||
### The definitions' gitea credential (dewey-0865)
|
||||
|
||||
At 94c33a43c, chief-of-staff.json and all six creative definitions have `credentials: []`, so `#giteaRole`
|
||||
(:2137-2143) would refuse them all `gitea-not-declared`. Row 287 doesn't add the credential. Row 285 (C2-08b)
|
||||
owns those role files, and its brief (case2-rows.md :1823-1824) gives each creative role and the chief
|
||||
`[{"service": "gitea"}]`, which Mos authorized in mos-1309 item 5. That is why 287 builds after 285 lands. Two
|
||||
notes for 285, not for this row:
|
||||
- The grant path reads only `service === 'gitea'`. The seat token's scopes come from `SEAT_SCOPES` in code,
|
||||
not from the definition. coder.json adds `scopes`, and 285's validator decides whether the bare form passes.
|
||||
- Five bus tests read roles/chief-of-staff.json directly (chief-of-staff, fire-revokes-grants,
|
||||
instances-verb, names, routes-verb), and 285's gate names the business suite, not the bus suite.
|
||||
- Settled (sage-2950, 19:06Z 10-22): 285's gate runs the bus suite whole (sage-2946), and Scrooge has the
|
||||
role.mjs no-scopes check. Dewey withdrew dewey-0865 (dewey-0867). Mos withdrew the mos-1332 item 3 publish
|
||||
fence (mos-1333), so business.mjs stays out of 285's files and sage-2916 stands as written.
|
||||
- Correction (sage-2958, Scrooge): role.mjs refuses `{"service": "gitea"}` with no scopes
|
||||
(checkGiteaScopes :100-111, util.mjs:42); only a host entry may omit them (:134). So 285's entries name
|
||||
their scopes, equal to SEAT_SCOPES (sage-2915). The chief-of-staff.json readers are seven, not five: add
|
||||
host-run and reload. 287's tests that build definitions give gitea entries `scopes: ["read:user",
|
||||
"write:repository"]`.
|
||||
|
||||
## 2. The change (proposed)
|
||||
|
||||
- Order in the executor: step 2 (user) and step 3 (mint) as today. Step 4, for each target: a library
|
||||
project gets `PUT /api/v1/repos/{owner}/{repo}/collaborators/{user}` with `{permission}` through the admin
|
||||
api, op `collaborator-add`, the `token` header, logged like every call. Expect 204, or anything else fails
|
||||
with `gitea-failed`. A non-library project forks as today.
|
||||
- The level: `write` when `CREATIVE_DEFINITIONS.includes(role.definition)`, `read` otherwise. The raise has
|
||||
no field for it; `giteaGrantCheck` still refuses any key other than method, agent, rotateBy and target.
|
||||
- Step 5: the upstream set stays for forks. A repository is accepted when it is the user's fork of a
|
||||
non-library publish upstream (today's rule), or when its `full_name` is a library publish project's
|
||||
`owner/repo` and its `permissions` are exactly `{admin: false, push: level === 'write', pull: true}`. Any
|
||||
other repository, a wider permission or `admin: true` refuses `gitea-reach-wider` with the repository
|
||||
named.
|
||||
- `#giteaRole`: the `CREATIVE_DEFINITIONS` refusal becomes "unless every project the raise names is a
|
||||
library project". `#giteaRole` gets the raise's project list from its two callers (the binding at
|
||||
:2168 and the recheck at :2201), so it can decide. The comment cites mos-1320 item 2 and keeps
|
||||
mos-1163 for every other project and every host grant. The host refusals at :1106 and :1254 and their
|
||||
tests stay as they are.
|
||||
- `#giteaTargets` returns `{project, upstream, library: true, repository, level}` for a library project and
|
||||
today's `{project, upstream, fork}` otherwise. `forkProblem` still runs for both: a library remote on
|
||||
another host refuses `remote-host-mismatch`, and one the seat user owns refuses `remote-not-fork`.
|
||||
- The block: a library target prints `Repository: <remote>, level <write|read>.` in place of its fork line,
|
||||
plus the brief's line "The token can write every path of this repository. Only the bus's path check
|
||||
limits it to supporting folders." for a `write` level. For a `read` level the line says the token can read
|
||||
every path. (The brief gives only the write line; Dewey can rule the read wording.)
|
||||
- Revoke deletes the token as today; the collaborator entry stays, as forks do. A fire sets
|
||||
`prohibit_login` as today. Neither path changes.
|
||||
- `scripts/case2-install.sh` (row 226's file): the chief of staff's instance gets `identity` and
|
||||
`publish: ["books"]`. If `#grantableAgent` refuses the chief as its own target, the row stops and goes to
|
||||
Sage. Read of :2037-2042 says it passes when the chief is `launch.by`; a test proves it.
|
||||
|
||||
## 3. Tests (proposed)
|
||||
|
||||
Docker (`gitea-grant-docker.test.mjs`), each fact first, before any code is trusted:
|
||||
- F-A: the admin token with `ADMIN_SCOPES` adds a collaborator to a repository the admin account doesn't
|
||||
own. F-B: `/user/repos` with the seat token lists that repository with the granted `permissions`. Either
|
||||
failing stops the row; the output goes to Mos.
|
||||
- F-C (recorded, not gating): a protected file pattern on her branch refuses a seat user's push to a story
|
||||
path while her own push passes. F-D (recorded): branch protection refuses a seat user's force-push while a
|
||||
normal push passes. Each logs its result as a diagnostic and doesn't fail the file; BUILD.md and a message
|
||||
to Mos carry them.
|
||||
|
||||
Fake API (`gitea-grant.test.mjs`, `fake-gitea-api.mjs`): collaborators carry a permission, `repoJson`
|
||||
returns `permissions`, and the PUT route requires a site admin token with `write:repository`.
|
||||
- L1 a creative definition gets `write`; L2 the chief gets `read`, including a mixed raise of the library
|
||||
project and a forked one (Q3).
|
||||
- L4 a repository with `push: true` for a read grant, or `admin: true`, or a fork of her library repository,
|
||||
refuses `gitea-reach-wider` naming it.
|
||||
- L5 revoke deletes the token and leaves the collaborator.
|
||||
- L6 a non-library project still forks, and its `grant.applied` body is byte-identical to today's.
|
||||
- L7 a creative raise naming a non-library project, or a library and a non-library one, refuses
|
||||
`gitea-not-declared`.
|
||||
- L8 the block holds the repository, the level and the brief's line, plus B-a to B-c (dewey-0862); the notice
|
||||
has no "forks: ." on a library-only grant.
|
||||
- L10 a project flipped to `kind: "library"` between the raise and the recheck gives `decision-mismatch`.
|
||||
- L9 the chief raises its own grant through `credential.mint`.
|
||||
- Install test: the business file gives the chief `identity` and `publish`.
|
||||
|
||||
Business (`business.test.mjs`, sage-2916): B-1 a creative instance with identity, a gitea credential and a
|
||||
library project in `publish` loads; B-2 so does the chief; B-3 any other instance with a library project in
|
||||
`publish` refuses with the new message; B-4 a creative instance or the chief without a gitea credential
|
||||
still refuses under the existing `publish needs identity and a gitea definition` rule. Mutant: M5 drop the
|
||||
creative-or-chief condition (any instance loads), killed by B-3.
|
||||
|
||||
Mutants: M1 drop the library-only condition from `#giteaRole` (the brief's), killed by L7. Others to list
|
||||
once the code exists: the level forced to `write` (L2), step 5's exact-permission check loosened to
|
||||
`push || pull` (L4), the fork path taken for a library project (L1, a fork call counted).
|
||||
|
||||
## 4. Gate plan (draft)
|
||||
|
||||
On Sage's CLEAR, with TMPDIR from `mktemp -d /mnt/storage/scratch/tmp/gXXXXXX`, stopping at the first red:
|
||||
the touched bus test files whole, then the mutants, the bus suite, the Docker test (checking for `^skip`
|
||||
first, since a missing image skips it), the install test, every `scripts/test-*.sh` under row 281's limits,
|
||||
then the token grep over the saved output. Timeouts and limits go in this file before the run.
|
||||
|
||||
## Runs
|
||||
|
||||
Filled in after the runs.
|
||||
|
||||
## Steelbeak takeover (2026-10-24, base 18334d713)
|
||||
|
||||
Sage-4124 dispatches Steelbeak as builder, Boyd first reviewer and Dewey landing reviewer. The
|
||||
inherited text above is retained as design history; this section records current execution.
|
||||
No test has run. Root npm ci uses this worktree only. Short TMPDIR is
|
||||
`/mnt/storage/scratch/tmp/1ysAmV`; every scripts/test-*.sh invocation will use `timeout -k 10`.
|
||||
Canonical queue next returned nothing while assignment was pending. Start op steelbeak-287-start
|
||||
refused the sage-4122 assign lock; retry no sooner than two minutes after that refusal.
|
||||
|
||||
Delta at 18334d713: broker grantableAgent 2667-2672 (launch.by self eligible), giteaRole 2760-2777
|
||||
(new chief refusal), targets 2781-2789, binding/block 2799-2830, recheck 2832-2842, step4
|
||||
3070-3085, step5 3087-3120, applied/notice 3124-3132. gitea-grant hash 75-80 and API 250-264
|
||||
unchanged. Business publish fence is 291-299. Install library helper is 109-127. Row286
|
||||
library-credential.test.mjs seeds 153-179, consumed at 219, 225 and 286, must become real minting.
|
||||
|
||||
Chief scope delta raised in steelbeak-0001: row285 declares read scopes and sage-2921 fences the
|
||||
existing write-scoped executor; inherited dewey-0862 retained SEAT_SCOPES and Q3 allowed mixed
|
||||
chief grants. Await explicit resolution of that boundary before implementing it. Other accepted
|
||||
rulings (conditional library hash, optional repositories event key, exact permission keys, business
|
||||
publish fence, preserved host creative refusals) remain applicable.
|
||||
|
||||
Tests planned: Docker F-A/F-B gating first; F-C/F-D recorded. Fake L1/L2/L4-L10 and business
|
||||
B1-B4 from inherited plan; install chief identity/publish and row286 real-mint replacement.
|
||||
Mutants M1 library-only guard, M2 forced write, M3 loose permissions, M4 library fork,
|
||||
M5 missing creative-or-chief publish fence. No suite runs before Sage CLEAR.
|
||||
|
||||
### Rulings
|
||||
|
||||
Sage-4125 (2026-10-24 14:49:27Z), replacing Q3 and dewey-0862's chief SEAT_SCOPES line:
|
||||
1. Chief grants name only library projects; mixed and fork-only chief raises refuse gitea-not-declared.
|
||||
2. Library mint scopes come from the definition's gitea credential: chief read:repository/read:user,
|
||||
creative its declared scopes. Fork grants retain SEAT_SCOPES.
|
||||
3. Role comment retains sage-2921's reason and cites mos-1320 item 2 and sage-4125.
|
||||
4. L2 pins chief read collaborator and read token scopes; L7b pins chief nonlibrary/mixed refusal;
|
||||
M6 substitutes SEAT_SCOPES for library minting, M7 drops chief library-only condition.
|
||||
5. Stop/report if grantableAgent or business validator still refuses chief after that condition.
|
||||
|
||||
Root npm ci passed (128 packages), webui npm ci passed (2), both worktree-local with ignore-scripts,
|
||||
no-audit and no-fund. Start retry met a different queue lock (louie-290-in-review); no bypass.
|
||||
|
||||
Prepared tests only (not executed): Docker F-A/F-B against both creative write and chief read users;
|
||||
F-B prints the actual permissions object. F-C/F-D use a private scratch repo and HTTP Git, logging
|
||||
only exit codes. A parent captures the fact child's entire TAP stdout/stderr and checks actual minted
|
||||
tokens before emitting diagnostics. New executor/business/install cases are drafted, including
|
||||
same-definition-hash level drift. Production files and fake API remain unchanged until Docker facts
|
||||
and expected RED evidence are recorded under CLEAR.
|
||||
|
||||
Row286 T7/T9 fixture draft now raises chief/books, resolves the human yes and awaits the executor;
|
||||
asserts repositories read level and actual token read scopes. The mapping remote is the fake's
|
||||
acme/books; revoke/fire assertions are unchanged. This intentional replacement is expected RED on
|
||||
the pre-C2-21 executor, separately from regressions in unchanged tests. No execution yet.
|
||||
|
||||
### Implementation sequence and acceptance
|
||||
|
||||
After CLEAR: run only the C2-21 Docker facts first, under a 360-second outer timeout, and stop/report
|
||||
if F-A or F-B fails or skips. Then run new C2-21 tests and the intentionally replaced T7/T9 cases
|
||||
against unchanged production to establish expected RED. Save the evidence and report the first RED.
|
||||
No production fix until that evidence exists. Old tests retain assertions; the old chief refusal
|
||||
case still targets a non-library project and should remain green.
|
||||
|
||||
Implementation boundaries: definition-based scopes only for library minting, collaborator PUT through
|
||||
admin token, exact admin/push/pull check publish-wide with library projects excluded from fork parents,
|
||||
conditional library hash input preserving legacy hashes, optional repositories event field preserving
|
||||
legacy event shape, scope-accurate block/notice. Business library publish remains fenced to creative
|
||||
or chief and existing identity/credential validation. Install adds only chief identity/publish for the
|
||||
library profile. Host creative refusals remain untouched.
|
||||
|
||||
Builder verification after expected RED and fix: touched test files whole (600 seconds each), bus,
|
||||
business and cli whole (1800 seconds each); mutants M1-M7 with targeted test filters (180 seconds
|
||||
each), restoring source after each; Docker whole (600 seconds); all scripts/test-*.sh (1200 seconds
|
||||
each, timeout -k 10); token search across saved output. Tests do not run concurrently. Stop on an
|
||||
unexpected old-test RED or second RED and report to Sage before any remediation. The gate's cli
|
||||
skip-list rules and Sage's slot ownership remain authoritative. No live host change, push to refactor,
|
||||
merge or deployment is authorized. Own-branch push is authorized after applicable suites are green.
|
||||
|
||||
Canonical start succeeded at 14:56Z: `ok steelbeak-287-start rev 3955 row 287 briefed→in-progress`.
|
||||
Prepared all independent pre-CLEAR work; no tests, production edits, commits or pushes. Reported
|
||||
ready in steelbeak-0004. The first execution is the Docker facts, never a fake or package suite.
|
||||
|
||||
## CLEAR run (sage-4130, 2026-10-24 15:04Z)
|
||||
|
||||
Worktree-local root npm ci passed (128 packages) and webui npm ci passed (2), ignore-scripts,
|
||||
no-audit, no-fund. New short TMPDIR `/mnt/storage/scratch/tmp/HvDvQ8`.
|
||||
First Docker facts invocation returned a vacuous green: inherited NODE_TEST_CONTEXT made Node skip
|
||||
the nested runner, minted 0. INVALID evidence, no fact pass claimed; reported steelbeak-0006.
|
||||
Corrected only the new harness child environment (delete NODE_TEST_CONTEXT) and added a requirement
|
||||
that four actual tokens were minted. Second facts invocation is running. Production unchanged.
|
||||
|
||||
### Docker facts (gitea/gitea:1.27.1, corrected harness, rc 0)
|
||||
|
||||
Evidence: evidence/docker-facts-r2.log, 1 pass, 0 fail, 0 skip; all four tokens minted and no minted
|
||||
value in complete captured TAP stdout/stderr. Container g287-* removed by t.after cleanup.
|
||||
- F-A PASS: exact admin scopes write:admin/write:repository/write:user; collaborator PUT on writer/books
|
||||
(admin does not own it) returned 204 for creative write and chief read.
|
||||
- F-B PASS: seat /user/repos lists writer/books. Actual creative permissions
|
||||
`{"admin":false,"push":true,"pull":true}`; chief permissions
|
||||
`{"admin":false,"push":false,"pull":true}`. Chief token has read:repository/read:user scopes.
|
||||
- F-C NOT ESTABLISHED: Story/** protection rejected both seat push (rc 1) and owner push (rc 1).
|
||||
The required owner control did not pass; do not claim a useful seat-only protected-file fence.
|
||||
- F-D ESTABLISHED: on protected branch normal seat supporting-file push rc 0, force-push rc 1.
|
||||
|
||||
Proceeding to expected RED against untouched production, then sage-4125 implementation.
|
||||
|
||||
### RED and correction record
|
||||
|
||||
New-test first RED: evidence/red-new.log, 17 tests, 4 pass (refusal controls), 13 fail at missing
|
||||
behavior. T7/T9 replacement expected RED: evidence/red-replacement.log, 2 fail gitea-not-declared.
|
||||
After implementation, touched-r1.log: 122 tests, 115 pass, 7 fail, 0 skip, all failures new cases.
|
||||
Stopped/reported steelbeak-0008 before changes. Mos-1590 via sage-4138 approved two fixture fixes:
|
||||
seat-only /user/repos overrides (not admin prechecks) and real declared coder scopes for kind drift.
|
||||
Dedicated-admin refusal already covered by X-G2 "admin set refuses each failed check and writes
|
||||
nothing, then writes the slot once" at gitea-grant.test.mjs:276 (:293 asserts grant-admin-not-dedicated).
|
||||
|
||||
Touched-r2.log: 123 tests, 122 pass, 1 fail, 0 skip. New L10's extra zero-network assertion was false:
|
||||
executor correctly performs four read-only admin checks before binding recheck. Stopped/reported
|
||||
steelbeak-0009. Sage-4139 approves exact GET admin checks, no /admin/users, /tokens or /collaborators,
|
||||
unchanged decision-mismatch, and absent grant file/record. Add M10, an early write ahead of recheck.
|
||||
The added no-declared-scopes case ran green before its mutant, an ordering error recorded honestly;
|
||||
its proof will be M8 removing the scopes check, followed by restored green. No prior RED-first proof
|
||||
is claimed for that added case. Add M9 dedication-check removal against existing X-G2.
|
||||
|
||||
F-C follow-up (sage-4134): a protected Story/** rule refused owner and seat; the first row depending
|
||||
on owner pushes to a protected branch must resolve this. Record only; no 287 behavior changes.
|
||||
Install choice: books is the chief's read project when present; another library project's existing
|
||||
install support is retained by using the first library project. Default identity is business-id-chief
|
||||
on that project's origin, with a mosaic.invalid email; an explicit input identity is preserved.
|
||||
|
||||
Touched-r3.log: 123 tests, 123 pass, 0 fail/skip after sage-4139. No unchanged assertion removed.
|
||||
Mutants M1-M11: all KILLED by assertion failures (rc 1, positive fail count, zero skips), no timeout,
|
||||
syntax or module-load kill. Every edited file restored byte-for-byte after each mutant; runner is
|
||||
mutants.py, evidence/mutants-run.log and evidence/M*.log hold the outcomes. M8's restored proof is
|
||||
the forthcoming full bus run; the added case's initial green remains recorded above.
|
||||
|
||||
| Mutant | Change | Catch |
|
||||
| --- | --- | --- |
|
||||
| M1 | drop creative library-only guard | L7 creative nonlibrary/mixed |
|
||||
| M2 | force library level write | L2 chief read |
|
||||
| M3 | accept pull regardless of admin/push | L4 widened read |
|
||||
| M4 | fork instead of collaborator | L1 creative write |
|
||||
| M5 | remove library publish fence | business only creative/chief |
|
||||
| M6 | library mint SEAT_SCOPES | L2 chief declared read scopes |
|
||||
| M7 | drop chief library-only guard | L7b chief nonlibrary/mixed |
|
||||
| M8 | remove declared-scopes check | no-declared-scopes refusal |
|
||||
| M9 | remove admin dedication check | existing X-G2 |
|
||||
| M10 | move step2 user-create write before binding recheck | L10 exact GETs, no writes/file/record |
|
||||
| M11 | omit library kind/level from identity hash | L10 drift |
|
||||
|
||||
### Full suites in progress
|
||||
|
||||
Restored full bus: evidence/bus-suite.log, rc 0, 998/998 pass, 0 fail/skip. This includes restored M8
|
||||
proof and both Docker tests (fact child diagnostics also carry their own nested summary).
|
||||
Full business: evidence/business-suite.log, rc 0, 283/283 pass, 0 fail/skip.
|
||||
CLI is running sequentially after business at --test-concurrency=2. Every package invocation uses
|
||||
short TMPDIR and an outer timeout -k 10; no live provider opt-in is passed.
|
||||
Docker executor container prefix is now g287-executor-* (only the test's scratch name changed),
|
||||
so the separately required full Docker rerun and final sweep can target only this row's g287-*.
|
||||
|
||||
Full CLI: evidence/cli-suite.log, rc 0, 990 tests, 989 pass, 0 fail, 1 skip, duration 851 seconds.
|
||||
The existing skip is packages/cli/tests/federation-acceptance.test.mjs, "FED R7 scratch two-instance
|
||||
acceptance", runner scripts/test-federation-acceptance.sh. That runner must pass in this same gate
|
||||
before the CLI skip is accepted (BRIEF-TEMPLATE mos-1583); no new skipped test was introduced.
|
||||
Dedicated full Docker and install-file reruns are now running; shell gates follow sequentially.
|
||||
|
||||
Dedicated Docker whole: evidence/docker-whole.log, rc 0, 2/2 pass, 0 fail/skip; actual F-A/F-B
|
||||
permissions and F-C negative/F-D positive reproduced. Complete captured fact TAP: minted 4, hits 0.
|
||||
Dedicated install whole: evidence/install-whole.log, rc 0, 27/27 pass, 0 fail/skip.
|
||||
Docker shell wrapper adds an EXIT/INT/TERM cleanup for g287-* only, alongside each test's t.after.
|
||||
Every scripts/test-*.sh is now running sequentially at timeout -k 10 1200, NO_COLOR=1 and
|
||||
MOSAIC_TEST_CONCURRENCY=2, with MOSAIC_LIVE_PROVIDER and NODE_TEST_CONTEXT unset. Each script's own
|
||||
scratch-project cleanup remains active. Stop at the first nonzero result; shell-summary.txt records rc.
|
||||
|
||||
Shell launcher error: direct timeout invocation of scripts/test-land-work.sh returned rc 126,
|
||||
Permission denied. File is tracked 100644; no assertion ran. Not a test RED. Reported steelbeak-0013;
|
||||
sage-4141 approved bash invocation for this and remaining scripts, matching the landing gate.
|
||||
No chmod or source change. Preserve initial shell-summary.txt and add shell-resume-summary.txt.
|
||||
|
||||
## Builder verification complete (sage-4130)
|
||||
|
||||
All twelve shell scripts passed (shell-summary.txt plus shell-resume-summary.txt), with the recorded
|
||||
rc126 launcher error followed by the approved bash run. Counts: auth 15, conductor 17, config 55,
|
||||
discord 82, extension-package 18, FED 1, foundation 44, land-work 86, push-check 38 checks plus its
|
||||
six killed internal mutants, queue 28 shell checks and 156 node passes, release 15, task 92; zero
|
||||
failed checks. Queue's canonical-only verify/render checks are intentionally skipped by its linked
|
||||
worktree runner. Task's live-provider cases remain off as required by row244 and this assignment.
|
||||
|
||||
Summary: bus 998/998; business 283/283; CLI 989 passed, zero failed and the one existing FED skip
|
||||
covered by dedicated runner 1/1 with zero skips and cleanup receipt; Docker whole 2/2; install 27/27;
|
||||
all 12 shell scripts rc0; M1-M11 killed without timeout/syntax kills and byte-restored.
|
||||
Known fixture search: 3 values across 37 logs, hits0. Fact runner checked four actual minted values
|
||||
against complete TAP stdout/stderr, hits0. Fake cases check every issued value against stored data.
|
||||
Final cleanup: no owned test node/chromium processes and no g287 Docker containers; test-task and
|
||||
release network removal checks passed; FED reports no groups/ports/sockets. Other containers and
|
||||
preexisting harness/MCP services were untouched. git diff --check passes.
|
||||
|
||||
Scope result: a creative library seat gets collaborator write; chief library read uses its declared
|
||||
read token scopes; creative/chief non-library or mixed raises refuse. Business publish remains fenced
|
||||
to creative/chief; legacy forks/event fields and host creative refusals retain coverage. T7/T9 now mint
|
||||
real chief grants before their unchanged revoke/fire assertions. F-C owner-protection follow-up is
|
||||
recorded above; F-D force-push observation is positive. No live Gitea or host change.
|
||||
|
||||
Review handoff: Boyd first, Dewey landing review, Sage gate. Base 18334d713; branch steelbeak/row287.
|
||||
The review manifest freezes all changed code, tests, logs and these notes (excluding only the
|
||||
manifest itself). Commit/push are authorized by sage-4130; review/landing remain separate stages.
|
||||
|
||||
## First-review doc-only follow-up (Boyd boyd-0034)
|
||||
|
||||
Boyd approved the frozen product candidate c2338ed588b9d54e35ebf628f514cfd8d7321b52 and manifest
|
||||
3683b40a32b68163c3ab0430ee0d6a4603c97882bf62e982d33a40058507c2b6 (56 entries), static review,
|
||||
no tests or slot use. Dewey received the Q210 landing-review handoff.
|
||||
|
||||
Correction to the earlier diff-check claims: plain `git diff --check` checked the then-current
|
||||
unstaged changes only. The complete base 18334d713 → frozen candidate diff-check returns rc2 because
|
||||
raw RED/mutant evidence logs retain Node assertion formatting with trailing whitespace. The complete
|
||||
product/test diff-check returns rc0. Raw evidence is preserved; do not claim the entire committed diff
|
||||
is whitespace-clean. This correction changes documentation only and does not rerun or weaken a test.
|
||||
|
||||
Old-test numstats, verified with `git diff --numstat 18334d713 c2338ed58` (added/deleted lines):
|
||||
|
||||
| Existing test file | Added | Deleted |
|
||||
| --- | ---: | ---: |
|
||||
| packages/bus/tests/fake-gitea-api.mjs | 13 | 3 |
|
||||
| packages/bus/tests/gitea-grant-docker.test.mjs | 127 | 2 |
|
||||
| packages/bus/tests/gitea-grant.test.mjs | 155 | 0 |
|
||||
| packages/bus/tests/library-credential.test.mjs | 24 | 35 |
|
||||
| packages/business/tests/business.test.mjs | 25 | 0 |
|
||||
| packages/cli/tests/case2-install.test.mjs | 13 | 0 |
|
||||
|
||||
Canonical queue move steelbeak-287-r1-review succeeded at rev3963, round1 on #1792. Its automatic
|
||||
review request failed at rev3964 because MOSAIC_GITEA_CREDENTIAL_FILE is unset; this seat has no
|
||||
Gitea credential. No substitution or secret access attempted. Sage received the precise boundary
|
||||
in steelbeak-0016 and handles the normal lead request/comment route. The test slot was returned.
|
||||
|
||||
Per lead decision137, this is a separate doc-only follow-up to the approved candidate. The frozen
|
||||
manifest and product/test bytes remain unchanged; the follow-up is not a new product review round.
|
||||
@@ -0,0 +1,40 @@
|
||||
✖ C2-21 L7/L7b: research refuses non-library targets lib (56.821785ms)
|
||||
✖ C2-21 L7/L7b: research refuses non-library targets app,lib (37.82327ms)
|
||||
ℹ tests 2
|
||||
ℹ suites 0
|
||||
ℹ pass 0
|
||||
ℹ fail 2
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 174.924413
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/bus/tests/gitea-grant.test.mjs:1370:5
|
||||
✖ C2-21 L7/L7b: research refuses non-library targets lib (56.821785ms)
|
||||
AssertionError [ERR_ASSERTION]: Missing expected rejection.
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:1372:7)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: undefined,
|
||||
operator: 'rejects',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/bus/tests/gitea-grant.test.mjs:1370:5
|
||||
✖ C2-21 L7/L7b: research refuses non-library targets app,lib (37.82327ms)
|
||||
AssertionError [ERR_ASSERTION]: Missing expected rejection.
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:1372:7)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: undefined,
|
||||
operator: 'rejects',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
✖ C2-21 L10: project kind or library level changing after raise refuses decision-mismatch (94.748852ms)
|
||||
ℹ tests 1
|
||||
ℹ suites 0
|
||||
ℹ pass 0
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 176.166886
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/bus/tests/gitea-grant.test.mjs:1416:1
|
||||
✖ C2-21 L10: project kind or library level changing after raise refuses decision-mismatch (94.748852ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
|
||||
+ actual - expected
|
||||
|
||||
[
|
||||
'GET /api/v1/version',
|
||||
'GET /api/v1/user',
|
||||
'GET /api/v1/token',
|
||||
'GET /api/v1/user/repos',
|
||||
+ 'POST /api/v1/admin/users'
|
||||
]
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:1434:12)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) {
|
||||
generatedMessage: true,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: [ 'GET /api/v1/version', 'GET /api/v1/user', 'GET /api/v1/token', 'GET /api/v1/user/repos', 'POST /api/v1/admin/users' ],
|
||||
expected: [ 'GET /api/v1/version', 'GET /api/v1/user', 'GET /api/v1/token', 'GET /api/v1/user/repos' ],
|
||||
operator: 'deepStrictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
✖ C2-21 L10: project kind or library level changing after raise refuses decision-mismatch (114.899902ms)
|
||||
ℹ tests 1
|
||||
ℹ suites 0
|
||||
ℹ pass 0
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 190.404699
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/bus/tests/gitea-grant.test.mjs:1416:1
|
||||
✖ C2-21 L10: project kind or library level changing after raise refuses decision-mismatch (114.899902ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
+ actual - expected
|
||||
|
||||
+ undefined
|
||||
- 'decision-mismatch'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:1433:12)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) {
|
||||
generatedMessage: true,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: undefined,
|
||||
expected: 'decision-mismatch',
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
✖ C2-21 L1/L2/L9: chief-of-staff gets read on her repository with declared scopes through raise and yes (56.777186ms)
|
||||
ℹ tests 1
|
||||
ℹ suites 0
|
||||
ℹ pass 0
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 137.201615
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/bus/tests/gitea-grant.test.mjs:1325:3
|
||||
✖ C2-21 L1/L2/L9: chief-of-staff gets read on her repository with declared scopes through raise and yes (56.777186ms)
|
||||
AssertionError [ERR_ASSERTION]: The expression evaluated to a falsy value:
|
||||
|
||||
assert.ok(block.includes(`Repository: ${f.gitea.url}/acme/app, level ${level}.`))
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:1338:12)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) {
|
||||
generatedMessage: true,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: false,
|
||||
expected: true,
|
||||
operator: '==',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
✖ C2-21 L4: read widened to write refuses gitea-reach-wider and deletes the minted token (119.045451ms)
|
||||
ℹ tests 1
|
||||
ℹ suites 0
|
||||
ℹ pass 0
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 197.170231
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/bus/tests/gitea-grant.test.mjs:1386:3
|
||||
✖ C2-21 L4: read widened to write refuses gitea-reach-wider and deletes the minted token (119.045451ms)
|
||||
AssertionError [ERR_ASSERTION]: {"action":"grant.applied","method":"gitea","grantDecision":"18acf1bb-f896-4b14-9c47-a5cff85a898d","agent":"chief","user":"demo-chief","userId":7,"tokenId":8,"forks":[],"repositories":[{"project":"app","repository":"https://127.0.0.1:33421/acme/app","level":"read"}]}
|
||||
+ actual - expected
|
||||
|
||||
+ undefined
|
||||
- 'gitea-reach-wider'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:1394:12)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: undefined,
|
||||
expected: 'gitea-reach-wider',
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
✖ C2-21 L1/L2/L9: research gets write on her repository with declared scopes through raise and yes (135.204477ms)
|
||||
ℹ tests 1
|
||||
ℹ suites 0
|
||||
ℹ pass 0
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 215.545162
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/bus/tests/gitea-grant.test.mjs:1325:3
|
||||
✖ C2-21 L1/L2/L9: research gets write on her repository with declared scopes through raise and yes (135.204477ms)
|
||||
AssertionError [ERR_ASSERTION]: {"action":"grant.failed","method":"gitea","grantDecision":"0c2e03db-8b1f-4b25-b460-02d551551433","agent":"coder","code":"gitea-reach-wider","repository":"demo-coder/app","user":"demo-coder"}
|
||||
+ actual - expected
|
||||
|
||||
+ 'grant.failed'
|
||||
- 'grant.applied'
|
||||
^
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:1348:12)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 'grant.failed',
|
||||
expected: 'grant.applied',
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
✖ C2-21: only creative definitions and the chief with gitea may publish a library project (17.773466ms)
|
||||
ℹ tests 1
|
||||
ℹ suites 0
|
||||
ℹ pass 0
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 71.348342
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/business/tests/business.test.mjs:365:1
|
||||
✖ C2-21: only creative definitions and the chief with gitea may publish a library project (17.773466ms)
|
||||
AssertionError [ERR_ASSERTION]: Missing expected exception.
|
||||
at refuses (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/business/tests/business.test.mjs:53:10)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/business/tests/business.test.mjs:380:5)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: undefined,
|
||||
operator: 'throws',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
✖ C2-21 L1/L2/L9: chief-of-staff gets read on her repository with declared scopes through raise and yes (117.793367ms)
|
||||
ℹ tests 1
|
||||
ℹ suites 0
|
||||
ℹ pass 0
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 201.446169
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/bus/tests/gitea-grant.test.mjs:1325:3
|
||||
✖ C2-21 L1/L2/L9: chief-of-staff gets read on her repository with declared scopes through raise and yes (117.793367ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
|
||||
+ actual - expected
|
||||
|
||||
[
|
||||
- 'read:repository',
|
||||
'read:user',
|
||||
+ 'write:repository'
|
||||
]
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:1353:12)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) {
|
||||
generatedMessage: true,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: [ 'read:user', 'write:repository' ],
|
||||
expected: [ 'read:repository', 'read:user' ],
|
||||
operator: 'deepStrictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
✖ C2-21 L7/L7b: chief-of-staff refuses non-library targets lib (52.058911ms)
|
||||
✖ C2-21 L7/L7b: chief-of-staff refuses non-library targets app,lib (38.384854ms)
|
||||
ℹ tests 2
|
||||
ℹ suites 0
|
||||
ℹ pass 0
|
||||
ℹ fail 2
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 169.051619
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/bus/tests/gitea-grant.test.mjs:1370:5
|
||||
✖ C2-21 L7/L7b: chief-of-staff refuses non-library targets lib (52.058911ms)
|
||||
AssertionError [ERR_ASSERTION]: Missing expected rejection.
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:1372:7)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: undefined,
|
||||
operator: 'rejects',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/bus/tests/gitea-grant.test.mjs:1370:5
|
||||
✖ C2-21 L7/L7b: chief-of-staff refuses non-library targets app,lib (38.384854ms)
|
||||
AssertionError [ERR_ASSERTION]: Missing expected rejection.
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:1372:7)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: undefined,
|
||||
operator: 'rejects',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
✖ C2-21: a library grant with no declared scopes refuses gitea-not-declared at raise (53.012865ms)
|
||||
ℹ tests 1
|
||||
ℹ suites 0
|
||||
ℹ pass 0
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 133.866141
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/bus/tests/gitea-grant.test.mjs:1455:1
|
||||
✖ C2-21: a library grant with no declared scopes refuses gitea-not-declared at raise (53.012865ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
+ actual - expected
|
||||
|
||||
+ 'storage-refused'
|
||||
- 'gitea-not-declared'
|
||||
|
||||
at Object.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:40:63)
|
||||
at expectedException (node:assert:547:17)
|
||||
at expectsError (node:assert:672:21)
|
||||
at strict.rejects (node:assert:738:3)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:1460:3)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) {
|
||||
generatedMessage: true,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 'storage-refused',
|
||||
expected: 'gitea-not-declared',
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
✖ X-G2: admin set refuses each failed check and writes nothing, then writes the slot once (146.452903ms)
|
||||
ℹ tests 1
|
||||
ℹ suites 0
|
||||
ℹ pass 0
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 226.323297
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/bus/tests/gitea-grant.test.mjs:275:1
|
||||
✖ X-G2: admin set refuses each failed check and writes nothing, then writes the slot once (146.452903ms)
|
||||
AssertionError [ERR_ASSERTION]: Missing expected rejection.
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:293:3)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: undefined,
|
||||
operator: 'rejects',
|
||||
diff: 'simple'
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,291 @@
|
||||
✔ a grant's level is collaborator or viewer (1.2135ms)
|
||||
✔ any other level refuses with the key path named (0.705817ms)
|
||||
✔ covers tells a viewer grant from a collaborator grant (0.16459ms)
|
||||
✔ covers refuses a call with no level, so a viewer grant can't slip past a collaborator check (0.123619ms)
|
||||
✔ a viewer grant still needs its business, project, instance and expiry to cover (0.12482ms)
|
||||
✔ a valid document passes and comes back frozen (2.021061ms)
|
||||
✔ checkAccess does not freeze or alias the caller's document (0.237983ms)
|
||||
✔ LEVELS is the single allowed level set, collaborator and viewer (row 193a) (0.098348ms)
|
||||
✔ accessFilePath is access.json in the config directory (0.319126ms)
|
||||
✔ top level: object, unknown keys, accessVersion, revision, arrays (0.858299ms)
|
||||
✔ users: unknown keys and the id rule (0.249483ms)
|
||||
✔ users: human, tester and any business's human are refused (0.24794ms)
|
||||
✔ users: home is a given peer or local (0.203275ms)
|
||||
✔ users: subjects, provider, sub, uniqueness and owner subjects (0.531512ms)
|
||||
✔ grants: unknown keys, id, user and business (0.431077ms)
|
||||
✔ grants: projects is ["*"] or distinct lowercase UUIDs (0.609265ms)
|
||||
✔ grants: instances is ["*"] or distinct declared instances of that business (0.320727ms)
|
||||
✔ grants: any level other than collaborator or viewer refuses, naming grants[i].level (0.367521ms)
|
||||
✔ grants: expires is optional ISO time, and a past expiry is not refused (0.747486ms)
|
||||
✔ the context is required and checked (0.336112ms)
|
||||
✔ loadAccess reads a valid file and checks it (0.501525ms)
|
||||
✔ loadAccess refuses a missing file, a symlink and a directory with exit 4 (0.324663ms)
|
||||
✔ loadAccess refuses another owner and group or other write (0.341248ms)
|
||||
✔ loadAccess refuses bad JSON and a bad document with exit 2 (0.328302ms)
|
||||
✔ covers: * and listed ids match (0.194537ms)
|
||||
✔ covers: an unlisted project, instance or business doesn't match (0.116729ms)
|
||||
✔ covers: an expired grant doesn't match, at and after the instant (0.118575ms)
|
||||
✔ covers: level is the least level asked for (row 193a) (0.108766ms)
|
||||
✔ covers: a missing or unknown level throws (row 193a, N1) (0.235393ms)
|
||||
✔ covers: a grant with an unknown level covers nothing (row 193a) (0.095664ms)
|
||||
✔ covers: now must be a valid Date (0.111546ms)
|
||||
✔ today's files load unchanged: identity, publish and remote are optional (4.289784ms)
|
||||
✔ a granted agent loads with its identity, grant and the project's upstream (1.918421ms)
|
||||
✔ N10: publish needs identity and a gitea definition, and the error names the grant (2.193949ms)
|
||||
✔ row 285: a chief with its gitea reference, identity and publish loads (2.646658ms)
|
||||
✔ publish names declared projects that have a remote, each once (2.695792ms)
|
||||
✔ identity fields are checked (2.941449ms)
|
||||
✔ D9c: the upstream must be its own normalized form, exactly /OWNER/REPO (7.397104ms)
|
||||
✔ D9c: one .git is stripped, and the fork is built from the stripped name (0.736416ms)
|
||||
✔ D9c: gitea.url names the instance only (3.765187ms)
|
||||
✔ the upstream's host and port must be the agent's Gitea's (1.274431ms)
|
||||
✔ remote-not-fork: an upstream the agent's own user owns, compared without case (1.27873ms)
|
||||
✔ store references: own path only (design D1, D3a) (0.253823ms)
|
||||
✔ the role file loads and resolves through loadBusiness (5.248984ms)
|
||||
✔ the shipped file is exactly the approved list (0.568801ms)
|
||||
✔ the assistant's authority is message.send and record.write within role (1.643851ms)
|
||||
✔ the assistant talks to the user and the chief of staff only, and no other shipped role is limited (0.468874ms)
|
||||
✔ a business grant through limits does not widen the role (2.103126ms)
|
||||
✔ a launch block whose by is the assistant is refused (1.227137ms)
|
||||
✔ the instance takes no credential references and no tracker (1.712585ms)
|
||||
✔ an arbiter whose role definition declares messaging lists is refused, for each kind (3.414519ms)
|
||||
✔ an arbiter without messaging lists is accepted beside the limited assistant (1.570128ms)
|
||||
✔ the contract passes work to the chief of staff and directs no one (0.485429ms)
|
||||
✔ REQ-HARN-2: the assistant, companion and research hold the web grant, and no other role file does (0.988738ms)
|
||||
✔ C2-04: library mappings load without roots, remain frozen, and retain mapping refusals (11.206347ms)
|
||||
✔ C2-04: an otherwise valid publish grant cannot name a library repo (1.732724ms)
|
||||
✔ config directory and file path follow MOSAIC_CONFIG (0.227073ms)
|
||||
✔ the fixture business validates and comes back frozen (0.994283ms)
|
||||
✔ two instances may share a definition (1.013991ms)
|
||||
✔ top-level refusals (2.939224ms)
|
||||
✔ arbiters and projects (4.586351ms)
|
||||
✔ role instances (2.30027ms)
|
||||
✔ Vikunja bots (5.245177ms)
|
||||
✔ a role without Vikunja takes no tracker block (2.857595ms)
|
||||
✔ credential references match the definition's services (2.272475ms)
|
||||
✔ launch (8.361716ms)
|
||||
✔ REQ-THREAD-2 REQ-LAUNCH-1 launch.threadIdleSeconds: absent is the default, a positive integer is used, anything else refuses (4.793795ms)
|
||||
✔ loadBusiness: file checks (2.040605ms)
|
||||
✔ loadBusiness: not a regular file (41.081919ms)
|
||||
✔ loading writes nothing (1.077813ms)
|
||||
✔ names that are Object.prototype properties don't count as declared (3.161448ms)
|
||||
✔ the shipped example refuses as written and validates once filled in (0.811309ms)
|
||||
✔ persona vars: agent layer, creative definition only, both or neither (8.817701ms)
|
||||
✔ row 285: a chief or creative instance without credentials.gitea refuses at load (6.447728ms)
|
||||
✔ row 285: a creative instance with identity and publish loads (0.774254ms)
|
||||
✔ row 194 keep, noHire and retired validate, and a file without them loads as before (1.57886ms)
|
||||
✔ row 194 each bad keep, noHire or retired entry refuses (5.865272ms)
|
||||
✔ C2-21: only creative definitions and the chief with gitea may publish a library project (10.140247ms)
|
||||
✔ row 323: committed Case 1 document loads unchanged through the business loader (4.882073ms)
|
||||
✔ row 323: real business validate accepts the Case 1 fixture without tracker credentials (65.86572ms)
|
||||
✔ row 323: real factory install accepts the document's scratch input and writes it unchanged (161.468205ms)
|
||||
✔ row 323: the Case 1 business boots without a tracker or @sync credential (50.794373ms)
|
||||
✔ row 323: factory install refuses bus.tester false before writing (34.17225ms)
|
||||
✔ row 323: literal library key refuses through final business validation (178.477589ms)
|
||||
✔ row 323: a library project refuses through the factory profile plan (33.530043ms)
|
||||
✔ row 325: the Case 1 project's sole key matches the repository project id (4.587873ms)
|
||||
✔ row 325: real business validate reports the copied repository project valid (66.272939ms)
|
||||
✔ row 325: the old mosaic-stack project key refuses against those same project bytes (66.102296ms)
|
||||
✔ row 285 amendment 1: no library loads with empty chief credentials (3.385712ms)
|
||||
✔ row 285 amendment 1: a library without chief gitea refuses by name (2.200618ms)
|
||||
✔ row 285 amendment 1: no library with chief gitea refuses the extra reference (0.708673ms)
|
||||
✔ the role file loads and resolves through loadBusiness (8.538323ms)
|
||||
✔ the shipped file is exactly the approved list (0.339503ms)
|
||||
✔ the resolved record matches the approved list, so a widened file fails (2.406238ms)
|
||||
✔ the chief of staff holds role.launch, message.send, role.end, organization.change, record.write, instance.name, role.hire and role.fire; every other action is gated (2.024787ms)
|
||||
✔ the pm does not hold role.launch (2.017621ms)
|
||||
✔ a business grant of a gated action through limits does not widen the role (1.216462ms)
|
||||
✔ a limit that drops role.launch leaves the chief of staff with no launch block (1.161866ms)
|
||||
✔ a launch block whose by is the pm instance is refused (1.3663ms)
|
||||
✔ the contract names no duty to talk to the human and the pm contract no launch (0.368325ms)
|
||||
✔ the instance takes no credential references and no tracker (1.990093ms)
|
||||
✔ C2-04: business validate and resolve skip a library before code projects and credential checks (200.876248ms)
|
||||
✔ usage errors exit 4 (277.707279ms)
|
||||
✔ validate: a good business exits 0 and prints instance digests (70.09797ms)
|
||||
✔ validate: project files (308.414394ms)
|
||||
✔ validate: missing files and a broken system config (237.80596ms)
|
||||
✔ validate: credential reference problems exit 2 and name each one (62.694272ms)
|
||||
✔ validate: a not-issued store agent dated today passes, and an issued one with a past expiry fails (137.769151ms)
|
||||
✔ validate: a token file inside the repository is refused (67.194357ms)
|
||||
✔ validate: role definitions come from MOSAIC_ROLES_DIR (189.791235ms)
|
||||
✔ resolve: prints one instance's record (258.920062ms)
|
||||
✔ resolve: refusals (383.486673ms)
|
||||
✔ parse: exactly one of file or env, plus the service's date (1.863836ms)
|
||||
✔ check: a good file has no problems (0.63731ms)
|
||||
✔ check never opens the file: a write-only token passes (0.259065ms)
|
||||
✔ check: file problems (0.654057ms)
|
||||
✔ check: token files can't live in the repository or dataRoot, even through a linked directory (0.666643ms)
|
||||
✔ check: dates and environment references (0.302686ms)
|
||||
✔ each definition loads, with a contract file, and is exactly the approved list (2.422404ms)
|
||||
✔ an organization file that declares them passes, and each resolves to least privilege (4.82324ms)
|
||||
✔ the chief of staff launches each one; no other role does (2.43161ms)
|
||||
✔ a C-suite role can't be made the launcher: launch.by must hold role.launch (1.392659ms)
|
||||
✔ limits can narrow a C-suite role but never widen it (1.344546ms)
|
||||
✔ any unknown key, tool, action or version in a role file is refused (0.395675ms)
|
||||
✔ row 298: checkGrantScope takes the D2 shape and refuses anything else (1.345091ms)
|
||||
✔ row 298: a missing scope is empty and a bad one falls back to empty, which covers nothing (0.239287ms)
|
||||
✔ row 298: a Gitea grant is in scope only when its agent and every project are listed (0.120581ms)
|
||||
✔ row 298 (sage-3594 Q2): host commands match whole argv tokens, and enrollment is never in scope (0.172913ms)
|
||||
✔ row 298: a bad grantScope in the file still loads, and the read shows the fallback (9.548945ms)
|
||||
✔ row 298: the writer checks the scope, logs the change and removes the key for {} (29.2548ms)
|
||||
✔ row 298 (sage-3594 Q1): the general writers can't write grantScope (6.509756ms)
|
||||
✔ REQ-WAIT-1: GATED_ONLY is exactly the hard waits; the new actions are in the vocabulary (0.953961ms)
|
||||
✔ REQ-ROLE-2: END_PROTECTED includes the assistant's thread definition (0.140081ms)
|
||||
✔ REQ-COS-3: role.hire and role.fire are routed, not hard waits (0.086622ms)
|
||||
✔ REQ-WAIT-3: library.write is routed under REQ-LIB-6 (1.16), not a hard wait (0.079125ms)
|
||||
✔ REQ-WAIT-3: every action is routed or a hard wait, never both, and the routed list names only actions (0.142718ms)
|
||||
✔ REQ-WAIT-3: instance.name is routed under REQ-MSG-2 (1.5), not a hard wait (row 141, sage-0589) (0.070496ms)
|
||||
✔ REQ-WAIT-2: a role listing a hard wait at either class is refused; the actions that left can be granted (2.750593ms)
|
||||
✔ S1: the business file refuses hosts at the top level and in a role, and host.<id> references (12.095424ms)
|
||||
✔ T1: address, user and host key checks, which the enrollment raise uses (4.457254ms)
|
||||
✔ T26: no wildcard and no shell, runner or interpreter as a prefix (1.428718ms)
|
||||
✔ D6: argv shape and the literal prefix match (0.265339ms)
|
||||
✔ row 285 P: creative equal-service changes preserve every existing credential reference (23.443808ms)
|
||||
✔ row 285 P: creative declared-service mismatch refuses even when host has no reference (4.777219ms)
|
||||
✔ row 285 P: an equal-service non-creative target keeps the base credential refusal (4.051484ms)
|
||||
✔ row 285 P: business validation precedes the creative credential fence (4.080403ms)
|
||||
✔ row 285 P: listing derives creative from authority and accepts only existing gitea minting (2.940761ms)
|
||||
✔ C2-04: instance settings skip a library while checking a later code project (24.897209ms)
|
||||
✔ row 165 read lists each instance and the definitions add accepts (2.526881ms)
|
||||
✔ row 165 add: two instances join, validate exits 0, the log names the human, and the writer asks for no restart (row 194, D3) (82.478108ms)
|
||||
✔ row 165 change: definition, vars and launch membership log before and after; none asks for a restart, the bus reloads (row 194, D3) (100.666633ms)
|
||||
✔ row 165 refuses each validation failure and leaves the file's bytes alone (35.076218ms)
|
||||
✔ T19 add of a credentialed definition writes store references dated today, names its bot, and validate exits 0 (85.693778ms)
|
||||
✔ T19 add of a definition that declares host writes references for its token services only (12.728252ms)
|
||||
✔ row 165 refuses a definition change to or from one that declares credentials (5.117931ms)
|
||||
✔ row 165 a vars or launch change on an instance with its binding succeeds and keeps the binding (75.996979ms)
|
||||
✔ row 165 refuses an END_PROTECTED definition and every change to a protected instance (8.113739ms)
|
||||
✔ row 165 refuses while a project file doesn't resolve, as validate would (16.000833ms)
|
||||
✔ row 165 refuses a stale revision and leaves the file's bytes alone (11.161353ms)
|
||||
✔ row 65 an instance write refuses harness-off, thinking-unsupported, an unknown model and a partial fallback (30.579803ms)
|
||||
✔ row 213 a creative instance is the human's: add, vars and definition changes, persona digest only (85.63908ms)
|
||||
✔ row 256 T1-T3, T5 the human deletes a creative instance, no other actor does, and the assistant stays (99.46135ms)
|
||||
✔ row 256 T4 the human's delete of the only listed instance, a creative one, is the last-listed refusal (3.143347ms)
|
||||
✔ REQ-LIMIT-1 schema: every scope unset or zero, adjustable above four, bad values and unknown models refuse (11.58859ms)
|
||||
✔ REQ-LIMIT-1 counts each exact scope at/over limit, unlimited and old tuple after model reassignment (0.653762ms)
|
||||
✔ REQ-ROLE-3 writer: atomic validated revision, immutable previous bytes and authenticated actor/old/new receipt (17.075687ms)
|
||||
✔ REQ-ROLE-3 writer: pre-rename fault leaves old file, post-rename recovery records commit, conflicting recovery refuses (13.840934ms)
|
||||
✔ REQ-LIMIT-1 / decision145 migration prints only, preserves values, rejects ambiguity, and is idempotent (3.72795ms)
|
||||
✔ REQ-LIMIT-1 shared settings validates the complete proposed selection and logs exact actor (19.857864ms)
|
||||
✔ REQ-LIMIT-1 provider is table data: another provider routes separately and aliases share exact counts (1.748236ms)
|
||||
✔ REQ-ROLE-3 refuses live writer locks, symlink targets and uncertain recovery without changing bytes (9.132518ms)
|
||||
✔ Q169 assistants: vars and definition change and log the actor; tracker null leaves no tracker (16.727332ms)
|
||||
✔ Q169 assistants: refuses other instances, other definitions, holder and credentials, empty entries, a tracker the definition has no credential for and a stale revision (18.030778ms)
|
||||
✔ Q169 assistants: a definition with a vikunja credential takes a tracker change and refuses its removal or a used bot (11.719638ms)
|
||||
✔ row 65 routes lists every instance with harness, model, thinking and fallback, the switched-on harnesses, the model keys and each harness's levels (2.212163ms)
|
||||
✔ row 65 setRoutes saves a harness, model, thinking and whole fallback, reads each back from the file, logs the actor, and a null fallback clears all three (14.340575ms)
|
||||
✔ row 65 setRoutes refuses a partial fallback, a switched-off harness, a level the harness doesn't apply, an unloaded module, an unknown model or instance, a malformed route, no change and a stale revision, with the file unchanged (19.4324ms)
|
||||
✔ row 65 a file with harness vars and no plugins.harness loads and its views read; a model change still saves, and a route change refuses harness-off (9.559348ms)
|
||||
✔ B7 kept instance: the models keys it names and the instances key are the human's (25.258332ms)
|
||||
✔ B7 kept instance: its route is the human's (8.773053ms)
|
||||
✔ M10 creative instance: models keys it names, the instances key and its route are the human's (31.644759ms)
|
||||
✔ C2-04 M9: changed playground and hybrid entries leave business and changes.jsonl byte-identical (20.984808ms)
|
||||
✔ C2-04 M9: code projects, additions, deletions and array changes are protected too (15.600225ms)
|
||||
✔ C2-04 M9: canonical key order is allowed and a non-project setter still records its change (12.997939ms)
|
||||
✔ C2-04 M9: a serialization hook cannot change projects after the canonical comparison (9.845749ms)
|
||||
✔ M7/M8: library seats come from library businesses, creative seats from their creative definitions (6.566752ms)
|
||||
✔ M8: a business with no library project puts no seat in either set (1.612265ms)
|
||||
✔ M7/M8: a business file that is not valid JSON throws; no empty set (1.658908ms)
|
||||
✔ M7/M8: an absent businesses directory gives empty sets (0.32243ms)
|
||||
✔ M7/M8: a businesses path that is not a directory throws; no empty set (0.725625ms)
|
||||
✔ M7/M8: a businesses symlink to a missing target throws; no empty set (0.382107ms)
|
||||
✔ M7/M8: a config directory under a regular file throws (lstat fails with ENOTDIR); no empty set (0.297168ms)
|
||||
✔ M7/M8: a business file that can't be read throws; no empty set (0.755072ms)
|
||||
✔ T6 delete refuses in the design's order and leaves the file's bytes alone (28.611933ms)
|
||||
✔ T6 delete refuses the last listed instance (3.226085ms)
|
||||
✔ T6 a clean delete removes the entry and its own references, adds retired, and the name can't come back (83.48393ms)
|
||||
✔ T7 veto: keep, unkeep, nohire and hire each write once, a repeat is no change, and the log names the human (112.086534ms)
|
||||
✔ T7 veto refuses an unknown or END_PROTECTED keep, a bad request, a stale revision and a business without launch (17.584597ms)
|
||||
✔ D2.2 hire adds and lists a new instance, logs the chief's context, and sets only model and limits (78.115614ms)
|
||||
✔ D2.2 hire refuses noHire, a kept name and a listed name, and re-lists a fired one only as it was (22.285503ms)
|
||||
✔ D2.2 fire delists and keeps the entry, a repeat is done with no write, and each refusal holds (79.084361ms)
|
||||
✔ row 256 checkDelete passes the human's creative delete and refuses another actor's; fire still refuses (5.186556ms)
|
||||
✔ row 239 T1 a fire on a revision that goes stale before the writer's lock refuses there and revokes nothing (4.342942ms)
|
||||
✔ row 239 T2 a write after the revoke refused before publication carries revoked, and the file keeps the edit (7.275516ms)
|
||||
✔ row 239 T3 any error after the revoke carries revoked, for a fire and a delete; a revoke that throws carries nothing (24.024395ms)
|
||||
✔ Q247: omitting tracker loads as null and preserves independent role bots (4.88928ms)
|
||||
✔ Q247: real business validate passes without a sync credential reference or file (67.498247ms)
|
||||
✔ Q247: present tracker retains its values and validation without mutating the input (2.36343ms)
|
||||
✔ Q247: present null, undefined and malformed trackers still refuse (3.735298ms)
|
||||
✔ Q247: absent business tracker does not waive a role's Vikunja bot requirement (0.785091ms)
|
||||
✔ REQ-PLUG-1 schema: plugins is optional, names known kinds and checked module names (10.412775ms)
|
||||
✔ REQ-PLUG-1 REQ-ROLE-3 the setter writes plugins through the organization writer and logs old and new names (21.608828ms)
|
||||
✔ vikunja:19/309 a patch changes only the kinds it names; null removes one kind, explicitly and in the trail (22.123619ms)
|
||||
✔ vikunja:19/309 an unknown kind (set or removed), a bad module, an empty patch and null on the wrong shape are refused, nothing written (1.61091ms)
|
||||
✔ REQ-PLUG-1 REQ-ROLE-3 the writer checks the bytes it writes: a Date in plugins is refused there too (3.81691ms)
|
||||
✔ REQ-PLUG-1 REQ-ROLE-3 a refused setter write throws a code, changes nothing and logs nothing (1.453096ms)
|
||||
✔ vikunja:19/305 with an exists check, the setter refuses a module no tree file answers to, before anything is written (6.975244ms)
|
||||
✔ optional project repo is preserved and must be a normalized absolute path (8.248464ms)
|
||||
✔ loaded business project repo cannot be nested in any declared code checkout (1.924299ms)
|
||||
✔ path and load (1.814342ms)
|
||||
✔ refusals (0.952563ms)
|
||||
✔ record.write is within-role for the eleven approved v2 agents, never gated-only (2.278902ms)
|
||||
✔ C2-06 M11 human replacement works; omitted settings return to defaults (27.784744ms)
|
||||
✔ C2-06 M11 agents and tester cannot change settings, including by changing human (24.473091ms)
|
||||
✔ C2-06 M11 serialization cannot replace settings after the first fence (8.808556ms)
|
||||
✔ C2-06 M11 an unrelated actor cannot appoint itself human and change reply limits (7.973319ms)
|
||||
✔ C2-06 invalid setter values preserve the business and changes.jsonl (8.939774ms)
|
||||
✔ C2-06 validator accepts hand-edited settings; raw reader fails closed on invalid files (10.916335ms)
|
||||
✔ C2-04: a library first in projects does not hide a later project layer (6.843821ms)
|
||||
✔ systemVars flattens the validated config (0.836289ms)
|
||||
✔ precedence: system, business, project, project role, agent (1.614161ms)
|
||||
✔ limits narrow the definition and never widen it (2.069759ms)
|
||||
✔ role.launch stays within-role only for the instance the launch block names (4.437162ms)
|
||||
✔ limits.authority without role.launch leaves the launcher with no launch block (1.556099ms)
|
||||
✔ limits.authority narrows cross-role actions too (0.872466ms)
|
||||
✔ classify (0.882709ms)
|
||||
✔ the record carries what the broker and launcher need (0.73785ms)
|
||||
✔ digest: key order doesn't matter, any value change does (5.81602ms)
|
||||
✔ refusals (1.779158ms)
|
||||
✔ the four shipped version 2 roles load (2.378087ms)
|
||||
✔ shipped role scopes match addendum B section 2 and the SR runbook (0.811462ms)
|
||||
✔ shipped authority follows the note's table (4.746427ms)
|
||||
✔ every role file in roles/ loads through the validator, and a v2 contract exists (1.129712ms)
|
||||
✔ version 1 files keep loading with no authority (0.820814ms)
|
||||
✔ the conductor policy isn't a role (0.145369ms)
|
||||
✔ a missing role file is exit 4, a symbolic link too (0.319514ms)
|
||||
✔ version 2 refusals (0.929286ms)
|
||||
✔ authority: closed vocabulary, no gated-only action, no overlap (1.548788ms)
|
||||
✔ messaging: optional, role definitions and human, never the tester (1.049825ms)
|
||||
✔ row 64 skills: optional, distinct skill directory names, none on version 1 (1.019457ms)
|
||||
✔ credentials: Gitea scopes (0.605626ms)
|
||||
✔ credentials: Vikunja scopes are a group-to-verbs map from the grantable list (0.6735ms)
|
||||
✔ credentials: services (0.330683ms)
|
||||
✔ contract: a non-empty regular Markdown file beside the role file (0.469895ms)
|
||||
✔ the six creative roles load: read tools, message.send and library.write, gitea, messaging with the human only (0.353918ms)
|
||||
✔ M1: a creative role file wider than read and answer is refused (2.717807ms)
|
||||
✔ hireable: HIRE_ALLOWED is exactly the six plain work actions, and every one is routed (0.08884ms)
|
||||
✔ hireable: a list naming a definition that holds any refused action refuses at load, one case per action (6.677373ms)
|
||||
✔ hireable: a mixed definition refuses, a clean list loads, and the named file's own hireable isn't read (0.544957ms)
|
||||
✔ hireable: a missing, version 1, duplicate or badly named entry refuses, and the key is optional (1.724693ms)
|
||||
✔ the pm and the cto check a saved spec against settled decisions and work in flight, and answer with a BLOCK or an accept (4.171158ms)
|
||||
✔ the chief of staff relays a BLOCK or an accept to the assistant as a reply to the message that carried it (0.402275ms)
|
||||
✔ the assistant summarizes a spec review in its own words and doesn't repeat the chief of staff's text (0.368389ms)
|
||||
✔ REQ-LIMIT-3 schema: each subject kind, limit 0 and no ceiling are accepted; bad limits, times, keys and undeclared instances refuse (12.866323ms)
|
||||
✔ REQ-LIMIT-3 the writer stamps since: a new limit gets now, a changed one keeps its time, a different time is refused (38.335671ms)
|
||||
✔ REQ-LIMIT-3 only tokens.* at schema depth holds token limits: agent limits named `limit` save as sent, and a token limit named `limit` is stamped and reset (39.769908ms)
|
||||
✔ REQ-LIMIT-3 a reset moves since to now, is logged with its actor, and refuses a path that is not a limit (21.162763ms)
|
||||
✔ REQ-LIMIT-2 schema: launch.account names a credential set; a daily quota needs a reset and an IANA zone and names the account (22.640957ms)
|
||||
✔ REQ-LIMIT-2 the writer stamps a daily quota's since, keeps its reset and zone, and a reset moves since (22.016802ms)
|
||||
✔ every key names known layers and a merge rule (0.630788ms)
|
||||
✔ unknown keys and wrong layers refuse (0.532995ms)
|
||||
✔ types (1.62428ms)
|
||||
✔ merge: defaults, then the most specific layer wins (0.217143ms)
|
||||
✔ merge: limits only narrow, and provenance lists each source (0.286677ms)
|
||||
✔ merge doesn't change its inputs (0.115663ms)
|
||||
✔ web.searxng takes https anywhere and http on loopback, so Jason's 127.0.0.1:8888 passes (0.148911ms)
|
||||
✔ both definitions load, with a contract file, and are exactly the approved lists (1.885538ms)
|
||||
✔ security-reviewer has no write or shell tool and classifies only the verdict as its own (5.091977ms)
|
||||
✔ merge-gate's only grant is git.merge.protected cross-role, and it has no write tool (1.67274ms)
|
||||
✔ neither role can be an arbiter: its messaging lists refuse the business file (row 133) (3.925963ms)
|
||||
✔ every role contract carries the proactive line in its Authority section (0.562759ms)
|
||||
✔ pm and chief-of-staff say where a raise goes under REQ-DEC-2 (0.479526ms)
|
||||
✔ the merge landing takes two runs: merge-gate, cto and chief-of-staff each say their part (0.372532ms)
|
||||
ℹ tests 283
|
||||
ℹ suites 0
|
||||
ℹ pass 283
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 3476.671216
|
||||
@@ -0,0 +1,7 @@
|
||||
owned test node/chromium processes found: 0
|
||||
owned test processes remaining: 0
|
||||
owned g287 Docker containers removed in final sweep: 0
|
||||
owned g287 Docker containers remaining: 0
|
||||
FED runner: no child groups, ports or sockets remain.
|
||||
Task/release scripts: owned compose network removal checks pass.
|
||||
Preexisting Codex/MCP runtime services excluded from the test-process sweep; other containers untouched.
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,27 @@
|
||||
✔ C2-21 F-A/F-B: admin collaborator grant on another owner and seat permission reach; F-C/F-D protection observations (11763.04751ms)
|
||||
ℹ ✔ C2-21 F-A/F-B: admin collaborator grant on another owner and seat permission reach; F-C/F-D protection observations (11616.122395ms)
|
||||
ℹ F-A creative write: PUT status 204
|
||||
ℹ F-B creative: permissions {"admin":false,"push":true,"pull":true}
|
||||
ℹ F-A chief read: PUT status 204
|
||||
ℹ F-B chief: permissions {"admin":false,"push":false,"pull":true}
|
||||
ℹ F-C protected Story/**: seat rc 1, owner rc 1; established false
|
||||
ℹ F-D protected branch: normal rc 0, force rc 1; established true
|
||||
ℹ minted 4, hits 0
|
||||
ℹ tests 1
|
||||
ℹ suites 0
|
||||
ℹ pass 1
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 11727.640162
|
||||
|
||||
ℹ minted 4, hits 0 (complete TAP stdout/stderr)
|
||||
ℹ tests 1
|
||||
ℹ suites 0
|
||||
ℹ pass 1
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 11858.472832
|
||||
@@ -0,0 +1,13 @@
|
||||
✔ C2-21 F-A/F-B: admin collaborator grant on another owner and seat permission reach; F-C/F-D protection observations (40.800678ms)
|
||||
ℹ (node:3897534) Warning: node:test run() is being called recursively within a test file. skipping running files.
|
||||
(Use `node --trace-warnings ...` to show where the warning was created)
|
||||
|
||||
ℹ minted 0, hits 0 (complete TAP stdout/stderr)
|
||||
ℹ tests 1
|
||||
ℹ suites 0
|
||||
ℹ pass 1
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 148.828093
|
||||
@@ -0,0 +1,28 @@
|
||||
✔ C2-21 F-A/F-B: admin collaborator grant on another owner and seat permission reach; F-C/F-D protection observations (12203.540198ms)
|
||||
ℹ ✔ C2-21 F-A/F-B: admin collaborator grant on another owner and seat permission reach; F-C/F-D protection observations (12086.277368ms)
|
||||
ℹ F-A creative write: PUT status 204
|
||||
ℹ F-B creative: permissions {"admin":false,"push":true,"pull":true}
|
||||
ℹ F-A chief read: PUT status 204
|
||||
ℹ F-B chief: permissions {"admin":false,"push":false,"pull":true}
|
||||
ℹ F-C protected Story/**: seat rc 1, owner rc 1; established false
|
||||
ℹ F-D protected branch: normal rc 0, force rc 1; established true
|
||||
ℹ minted 4, hits 0
|
||||
ℹ tests 1
|
||||
ℹ suites 0
|
||||
ℹ pass 1
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 12174.33915
|
||||
|
||||
ℹ minted 4, hits 0 (complete TAP stdout/stderr)
|
||||
✔ X-G3: on scratch 1.27.1, a grant makes the user, mints, forks, checks reach, writes the file, and revoke deletes the token (12131.714969ms)
|
||||
ℹ tests 2
|
||||
ℹ suites 0
|
||||
ℹ pass 2
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 24419.085201
|
||||
@@ -0,0 +1,35 @@
|
||||
✔ the library install writes the business file, personas and slot dir, makes the copy and starts nothing (235.765785ms)
|
||||
✔ a library re-run passes and leaves every file as it was (392.367807ms)
|
||||
✔ a re-run refuses business-file-differs and leaves the file unchanged (273.242685ms)
|
||||
✔ a re-run refuses persona-differs when an installed persona isn't the staged one, and writes nothing (272.374516ms)
|
||||
✔ the factory install writes no library mapping, persona or slot dir (177.367057ms)
|
||||
✔ the factory install refuses a library project and bus.tester off, before anything is written (73.809786ms)
|
||||
✔ the library install refuses bus.tester on and a persona var in the input (75.800921ms)
|
||||
✔ the install refuses root, a business id mismatch, another --home and a config dir with group bits (54.413413ms)
|
||||
✔ the library install needs --personas (5.198673ms)
|
||||
✔ a validate refusal removes the business file this run wrote (239.945383ms)
|
||||
✔ N4: a validate refusal on a re-run leaves the matching business file in place, byte for byte (386.30537ms)
|
||||
✔ a clean library host passes the precheck with no mirror yet (405.96297ms)
|
||||
✔ the library precheck refuses a persona file at 0644 (394.702581ms)
|
||||
✔ the library precheck refuses a persona whose digest isn't persona.sha256 (390.230566ms)
|
||||
✔ the library precheck refuses a persona dir with group bits (384.651188ms)
|
||||
✔ the library precheck refuses a business file at 0644 (394.310416ms)
|
||||
✔ the library precheck refuses a changes.jsonl at 0644 (558.803755ms)
|
||||
✔ the library precheck refuses a Discord binding (390.812738ms)
|
||||
✔ --library-status refuses library-mirror-missing before the bus has made the mirror (303.064191ms)
|
||||
✔ --library-status refuses library-never-synced on a fresh mirror (323.253782ms)
|
||||
✔ --library-status names a recorded lastError before the null lastFetch it leaves (N3) (338.938239ms)
|
||||
✔ --library-status prints each R-problem's path as an escaped JSON string, then the count, and passes (383.891631ms)
|
||||
✔ --library-status takes no --profile (5.425522ms)
|
||||
✔ Vikunja 614: install preserves the explicit notifier decision bytes and mode and creates none when absent (469.515908ms)
|
||||
✔ row 322 F8: library personas install from the copy with no clone node_modules (139.58888ms)
|
||||
✔ row 322 F9: importer refusal keeps the error line rather than the Node banner (88.736058ms)
|
||||
✔ C2-21: library install supplies chief identity and books publish for its real read grant (234.472998ms)
|
||||
ℹ tests 27
|
||||
ℹ suites 0
|
||||
ℹ pass 27
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 7494.342107
|
||||
@@ -0,0 +1,11 @@
|
||||
M1: rc=1 pass=0 fail=2 skip=0 KILLED
|
||||
M2: rc=1 pass=0 fail=1 skip=0 KILLED
|
||||
M3: rc=1 pass=0 fail=1 skip=0 KILLED
|
||||
M4: rc=1 pass=0 fail=1 skip=0 KILLED
|
||||
M5: rc=1 pass=0 fail=1 skip=0 KILLED
|
||||
M6: rc=1 pass=0 fail=1 skip=0 KILLED
|
||||
M7: rc=1 pass=0 fail=2 skip=0 KILLED
|
||||
M8: rc=1 pass=0 fail=1 skip=0 KILLED
|
||||
M9: rc=1 pass=0 fail=1 skip=0 KILLED
|
||||
M10: rc=1 pass=0 fail=1 skip=0 KILLED
|
||||
M11: rc=1 pass=0 fail=1 skip=0 KILLED
|
||||
@@ -0,0 +1,11 @@
|
||||
M1 KILLED rc=1
|
||||
M2 KILLED rc=1
|
||||
M3 KILLED rc=1
|
||||
M4 KILLED rc=1
|
||||
M5 KILLED rc=1
|
||||
M6 KILLED rc=1
|
||||
M7 KILLED rc=1
|
||||
M8 KILLED rc=1
|
||||
M9 KILLED rc=1
|
||||
M10 KILLED rc=1
|
||||
M11 KILLED rc=1
|
||||
@@ -0,0 +1,243 @@
|
||||
✖ C2-21 L1/L2/L9: research gets write on her repository with declared scopes through raise and yes (61.932385ms)
|
||||
✖ C2-21 L1/L2/L9: chief-of-staff gets read on her repository with declared scopes through raise and yes (45.50288ms)
|
||||
✖ C2-21 L1/L2/L9: coder gets read on her repository with declared scopes through raise and yes (46.877421ms)
|
||||
✔ C2-21 L7/L7b: research refuses non-library targets lib (41.832555ms)
|
||||
✔ C2-21 L7/L7b: research refuses non-library targets app,lib (37.221112ms)
|
||||
✔ C2-21 L7/L7b: chief-of-staff refuses non-library targets lib (36.572846ms)
|
||||
✔ C2-21 L7/L7b: chief-of-staff refuses non-library targets app,lib (37.961766ms)
|
||||
✖ C2-21 L4: read widened to write refuses gitea-reach-wider and deletes the minted token (38.226051ms)
|
||||
✖ C2-21 L4: admin privilege refuses gitea-reach-wider and deletes the minted token (39.45444ms)
|
||||
✖ C2-21 L4: missing pull refuses gitea-reach-wider and deletes the minted token (39.007984ms)
|
||||
✖ C2-21 L4: missing permission object refuses gitea-reach-wider and deletes the minted token (39.888865ms)
|
||||
✖ C2-21 L4: library fork refuses gitea-reach-wider and deletes the minted token (40.565327ms)
|
||||
✖ C2-21 L8: untargeted library collaborator reach is disclosed and accepted publish-wide (41.382167ms)
|
||||
✖ C2-21 L10: project kind or library level changing after raise refuses decision-mismatch (123.506544ms)
|
||||
✖ C2-21 L4: creative write requires push true but ignores unrelated permission keys (38.813518ms)
|
||||
✖ C2-21: only creative definitions and the chief with gitea may publish a library project (7.989289ms)
|
||||
✖ C2-21: library install supplies chief identity and books publish for its real read grant (264.267351ms)
|
||||
ℹ tests 17
|
||||
ℹ suites 0
|
||||
ℹ pass 4
|
||||
ℹ fail 13
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 805.736302
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/bus/tests/gitea-grant.test.mjs:1325:3
|
||||
✖ C2-21 L1/L2/L9: research gets write on her repository with declared scopes through raise and yes (61.932385ms)
|
||||
Error: gitea-not-declared
|
||||
at fail (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:219:9)
|
||||
at #giteaRole (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2768:89)
|
||||
at #giteaGrantBinding (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2800:52)
|
||||
at file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2700:114
|
||||
at Store.transaction (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/store.mjs:289:22)
|
||||
at #raiseGrant (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2699:28)
|
||||
at Broker.request (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:4807:88)
|
||||
at call (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:109:49)
|
||||
at Object.raise (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:111:5)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:1336:23) {
|
||||
code: 'gitea-not-declared'
|
||||
}
|
||||
|
||||
test at packages/bus/tests/gitea-grant.test.mjs:1325:3
|
||||
✖ C2-21 L1/L2/L9: chief-of-staff gets read on her repository with declared scopes through raise and yes (45.50288ms)
|
||||
Error: gitea-not-declared
|
||||
at fail (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:219:9)
|
||||
at #giteaRole (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2768:89)
|
||||
at #giteaGrantBinding (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2800:52)
|
||||
at file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2700:114
|
||||
at Store.transaction (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/store.mjs:289:22)
|
||||
at #raiseGrant (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2699:28)
|
||||
at Broker.request (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:4807:88)
|
||||
at call (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:109:49)
|
||||
at Object.raise (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:111:5)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:1336:23) {
|
||||
code: 'gitea-not-declared'
|
||||
}
|
||||
|
||||
test at packages/bus/tests/gitea-grant.test.mjs:1325:3
|
||||
✖ C2-21 L1/L2/L9: coder gets read on her repository with declared scopes through raise and yes (46.877421ms)
|
||||
AssertionError [ERR_ASSERTION]: The expression evaluated to a falsy value:
|
||||
|
||||
assert.ok(block.includes(`Repository: ${f.gitea.url}/acme/app, level ${level}.`))
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:1338:12)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: true,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: false,
|
||||
expected: true,
|
||||
operator: '==',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/bus/tests/gitea-grant.test.mjs:1386:3
|
||||
✖ C2-21 L4: read widened to write refuses gitea-reach-wider and deletes the minted token (38.226051ms)
|
||||
Error: gitea-not-declared
|
||||
at fail (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:219:9)
|
||||
at #giteaRole (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2768:89)
|
||||
at #giteaGrantBinding (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2800:52)
|
||||
at file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2700:114
|
||||
at Store.transaction (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/store.mjs:289:22)
|
||||
at #raiseGrant (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2699:28)
|
||||
at Broker.request (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:4807:88)
|
||||
at call (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:109:49)
|
||||
at raise (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:111:5)
|
||||
at Object.approve (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:121:21) {
|
||||
code: 'gitea-not-declared'
|
||||
}
|
||||
|
||||
test at packages/bus/tests/gitea-grant.test.mjs:1386:3
|
||||
✖ C2-21 L4: admin privilege refuses gitea-reach-wider and deletes the minted token (39.45444ms)
|
||||
Error: gitea-not-declared
|
||||
at fail (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:219:9)
|
||||
at #giteaRole (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2768:89)
|
||||
at #giteaGrantBinding (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2800:52)
|
||||
at file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2700:114
|
||||
at Store.transaction (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/store.mjs:289:22)
|
||||
at #raiseGrant (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2699:28)
|
||||
at Broker.request (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:4807:88)
|
||||
at call (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:109:49)
|
||||
at raise (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:111:5)
|
||||
at Object.approve (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:121:21) {
|
||||
code: 'gitea-not-declared'
|
||||
}
|
||||
|
||||
test at packages/bus/tests/gitea-grant.test.mjs:1386:3
|
||||
✖ C2-21 L4: missing pull refuses gitea-reach-wider and deletes the minted token (39.007984ms)
|
||||
Error: gitea-not-declared
|
||||
at fail (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:219:9)
|
||||
at #giteaRole (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2768:89)
|
||||
at #giteaGrantBinding (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2800:52)
|
||||
at file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2700:114
|
||||
at Store.transaction (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/store.mjs:289:22)
|
||||
at #raiseGrant (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2699:28)
|
||||
at Broker.request (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:4807:88)
|
||||
at call (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:109:49)
|
||||
at raise (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:111:5)
|
||||
at Object.approve (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:121:21) {
|
||||
code: 'gitea-not-declared'
|
||||
}
|
||||
|
||||
test at packages/bus/tests/gitea-grant.test.mjs:1386:3
|
||||
✖ C2-21 L4: missing permission object refuses gitea-reach-wider and deletes the minted token (39.888865ms)
|
||||
Error: gitea-not-declared
|
||||
at fail (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:219:9)
|
||||
at #giteaRole (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2768:89)
|
||||
at #giteaGrantBinding (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2800:52)
|
||||
at file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2700:114
|
||||
at Store.transaction (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/store.mjs:289:22)
|
||||
at #raiseGrant (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2699:28)
|
||||
at Broker.request (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:4807:88)
|
||||
at call (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:109:49)
|
||||
at raise (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:111:5)
|
||||
at Object.approve (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:121:21) {
|
||||
code: 'gitea-not-declared'
|
||||
}
|
||||
|
||||
test at packages/bus/tests/gitea-grant.test.mjs:1386:3
|
||||
✖ C2-21 L4: library fork refuses gitea-reach-wider and deletes the minted token (40.565327ms)
|
||||
Error: gitea-not-declared
|
||||
at fail (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:219:9)
|
||||
at #giteaRole (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2768:89)
|
||||
at #giteaGrantBinding (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2800:52)
|
||||
at file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2700:114
|
||||
at Store.transaction (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/store.mjs:289:22)
|
||||
at #raiseGrant (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2699:28)
|
||||
at Broker.request (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:4807:88)
|
||||
at call (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:109:49)
|
||||
at raise (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:111:5)
|
||||
at Object.approve (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:121:21) {
|
||||
code: 'gitea-not-declared'
|
||||
}
|
||||
|
||||
test at packages/bus/tests/gitea-grant.test.mjs:1402:1
|
||||
✖ C2-21 L8: untargeted library collaborator reach is disclosed and accepted publish-wide (41.382167ms)
|
||||
Error: gitea-not-declared
|
||||
at fail (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:219:9)
|
||||
at #giteaRole (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2768:89)
|
||||
at #giteaGrantBinding (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2800:52)
|
||||
at file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2700:114
|
||||
at Store.transaction (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/store.mjs:289:22)
|
||||
at #raiseGrant (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2699:28)
|
||||
at Broker.request (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:4807:88)
|
||||
at call (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:109:49)
|
||||
at Object.raise (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:111:5)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:1405:21) {
|
||||
code: 'gitea-not-declared'
|
||||
}
|
||||
|
||||
test at packages/bus/tests/gitea-grant.test.mjs:1416:1
|
||||
✖ C2-21 L10: project kind or library level changing after raise refuses decision-mismatch (123.506544ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
+ actual - expected
|
||||
|
||||
+ undefined
|
||||
- 'decision-mismatch'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:1430:12)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: true,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: undefined,
|
||||
expected: 'decision-mismatch',
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/bus/tests/gitea-grant.test.mjs:1435:1
|
||||
✖ C2-21 L4: creative write requires push true but ignores unrelated permission keys (38.813518ms)
|
||||
Error: gitea-not-declared
|
||||
at fail (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:219:9)
|
||||
at #giteaRole (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2768:89)
|
||||
at #giteaGrantBinding (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2800:52)
|
||||
at file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2700:114
|
||||
at Store.transaction (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/store.mjs:289:22)
|
||||
at #raiseGrant (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2699:28)
|
||||
at Broker.request (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:4807:88)
|
||||
at call (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:109:49)
|
||||
at raise (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:111:5)
|
||||
at Object.approve (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:121:21) {
|
||||
code: 'gitea-not-declared'
|
||||
}
|
||||
|
||||
test at packages/business/tests/business.test.mjs:365:1
|
||||
✖ C2-21: only creative definitions and the chief with gitea may publish a library project (7.989289ms)
|
||||
Error [BusinessError]: /mnt/storage/scratch/tmp/HvDvQ8/mosaic-business-qXfqBQ/businesses/acme.json roles.bean.publish names library project books; library repositories cannot be published by agents
|
||||
at refuse (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/business/src/errors.mjs:12:9)
|
||||
at validateBusinessDocument (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/business/src/business.mjs:295:49)
|
||||
at check (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/business/tests/business.test.mjs:66:10)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/business/tests/business.test.mjs:375:22)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) {
|
||||
exitCode: 2
|
||||
}
|
||||
|
||||
test at packages/cli/tests/case2-install.test.mjs:481:1
|
||||
✖ C2-21: library install supplies chief identity and books publish for its real read grant (264.267351ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
|
||||
+ actual - expected
|
||||
|
||||
+ undefined
|
||||
- [
|
||||
- 'books'
|
||||
- ]
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/cli/tests/case2-install.test.mjs:486:10)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.start (node:internal/test_runner/test:1262:17)
|
||||
at startSubtestAfterBootstrap (node:internal/test_runner/harness:387:17) {
|
||||
generatedMessage: true,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: undefined,
|
||||
expected: [ 'books' ],
|
||||
operator: 'deepStrictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
✖ C2-03b T7: a chief revoked through grants.revoke or fired through instance.fire refuses, and the fallback stays ended (55.83288ms)
|
||||
✖ C2-03b T9 runtime: the chief's store file and user, then a real revoke that runs no git with the slot at 0000 (52.459744ms)
|
||||
ℹ tests 2
|
||||
ℹ suites 0
|
||||
ℹ pass 0
|
||||
ℹ fail 2
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 240.265949
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/bus/tests/library-credential.test.mjs:205:1
|
||||
✖ C2-03b T7: a chief revoked through grants.revoke or fired through instance.fire refuses, and the fallback stays ended (55.83288ms)
|
||||
Error: gitea-not-declared
|
||||
at fail (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:219:9)
|
||||
at #giteaRole (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2768:89)
|
||||
at #giteaGrantBinding (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2800:52)
|
||||
at file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2700:114
|
||||
at Store.transaction (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/store.mjs:289:22)
|
||||
at #raiseGrant (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2699:28)
|
||||
at Broker.request (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:4807:88)
|
||||
at Object.mintChiefReadGrant (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/library-credential.test.mjs:154:35)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/library-credential.test.mjs:208:17)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5) {
|
||||
code: 'gitea-not-declared'
|
||||
}
|
||||
|
||||
test at packages/bus/tests/library-credential.test.mjs:265:1
|
||||
✖ C2-03b T9 runtime: the chief's store file and user, then a real revoke that runs no git with the slot at 0000 (52.459744ms)
|
||||
Error: gitea-not-declared
|
||||
at fail (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:219:9)
|
||||
at #giteaRole (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2768:89)
|
||||
at #giteaGrantBinding (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2800:52)
|
||||
at file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2700:114
|
||||
at Store.transaction (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/store.mjs:289:22)
|
||||
at #raiseGrant (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2699:28)
|
||||
at Broker.request (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:4807:88)
|
||||
at Object.mintChiefReadGrant (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/library-credential.test.mjs:154:35)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/library-credential.test.mjs:275:17)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5) {
|
||||
code: 'gitea-not-declared'
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
scripts/test-land-work.sh rc=0
|
||||
scripts/test-push-check.sh rc=0
|
||||
scripts/test-queue.sh rc=0
|
||||
scripts/test-release.sh rc=0
|
||||
scripts/test-task.sh rc=0
|
||||
@@ -0,0 +1,8 @@
|
||||
scripts/test-auth.sh rc=0
|
||||
scripts/test-conductor.sh rc=0
|
||||
scripts/test-config.sh rc=0
|
||||
scripts/test-discord.sh rc=0
|
||||
scripts/test-extension-package.sh rc=0
|
||||
scripts/test-federation-acceptance.sh rc=0
|
||||
scripts/test-foundation.sh rc=0
|
||||
scripts/test-land-work.sh rc=126
|
||||
@@ -0,0 +1,17 @@
|
||||
OK status with missing harness credential exits 3 and still lists accounts
|
||||
OK status reports harness credential (read-only) + mosaic accounts
|
||||
OK api key material never reaches output
|
||||
OK oauth token material never reaches output
|
||||
OK unparseable credential file exits 2
|
||||
OK symlinked credential file exits 4
|
||||
OK env-side credential names reported
|
||||
OK env var values never reach output
|
||||
OK accounts without an accounts dir reports none and creates nothing
|
||||
OK accounts lists files and marks the active one
|
||||
OK loose account perms flagged in listing
|
||||
OK agent --auth with missing account file refuses (exit 4)
|
||||
OK agent --auth with non-0600 account file refuses
|
||||
OK agent --auth with invalid account name refuses
|
||||
OK auth.sh without valid config refuses
|
||||
|
||||
selftest: 15 passed, 0 failed
|
||||
@@ -0,0 +1,23 @@
|
||||
On branch steelbeak/row287
|
||||
Your branch is up to date with 'origin/steelbeak/row287'.
|
||||
|
||||
nothing to commit, working tree clean
|
||||
OK dry-run: allowed change, exit 0, nothing committed (exit 0)
|
||||
OK dry-run committed nothing
|
||||
OK apply: allowed change exits 0 (exit 0)
|
||||
OK apply: attribution in commit subject
|
||||
OK apply: target tree clean after commit
|
||||
OK disallowed path refused (exit 1)
|
||||
OK disallowed path: target untouched
|
||||
OK syntax gate refused broken .mjs (exit 1)
|
||||
OK syntax gate: target untouched
|
||||
OK suite failure refused (exit 1)
|
||||
OK suite failure: target reverted to clean
|
||||
OK disabled policy refused (exit 2)
|
||||
OK disabled policy: target untouched
|
||||
OK failed run refused (exit 1)
|
||||
OK failed run: target untouched
|
||||
OK missing run exits 4 (exit 4)
|
||||
OK invalid policy exits 2 (exit 2)
|
||||
|
||||
selftest: 17 passed, 0 failed
|
||||
@@ -0,0 +1,57 @@
|
||||
OK absent adapter defaults to pi
|
||||
OK adapter mock validates (exit 0)
|
||||
OK unsupported adapter exits 2 (exit 2)
|
||||
OK env exports adapter
|
||||
OK bootstrap creates default when absent (exit 0)
|
||||
OK bootstrap wrote config file
|
||||
OK bootstrap is idempotent on existing config (exit 0)
|
||||
OK bootstrap did not rewrite existing config
|
||||
OK validate missing config exits 3 (exit 3)
|
||||
OK malformed JSON exits 2 (exit 2)
|
||||
OK unsupported configVersion exits 2 (exit 2)
|
||||
OK unknown top-level key exits 2 (exit 2)
|
||||
OK unknown execution key exits 2 (exit 2)
|
||||
OK unsupported backend exits 2 (exit 2)
|
||||
OK unsupported environment exits 2 (exit 2)
|
||||
OK relative dataRoot exits 2 (exit 2)
|
||||
OK non-canonical dataRoot exits 2 (exit 2)
|
||||
OK filesystem root dataRoot exits 2 (exit 2)
|
||||
OK home directory dataRoot exits 2 (exit 2)
|
||||
OK dataRoot containing config dir exits 2 (exit 2)
|
||||
OK control character in provider exits 2 (exit 2)
|
||||
OK symlinked config file exits 2 (exit 2)
|
||||
OK env exports resolve correctly
|
||||
OK valid console.sso loads and is in the output
|
||||
OK console.sso HS256 alg exits 2 naming the path (exit 2)
|
||||
OK console.sso alg none exits 2 naming the path (exit 2)
|
||||
OK console.sso missing slot field exits 2 naming the path (exit 2)
|
||||
OK console.sso slot name outside the pattern exits 2 naming the path (exit 2)
|
||||
OK unknown key under console exits 2 naming the path (exit 2)
|
||||
OK valid federation key loads and is in the output
|
||||
OK federation unknown sub-key exits 2 naming the key (exit 2)
|
||||
OK federation peer unknown sub-key exits 2 naming the key (exit 2)
|
||||
OK federation peer origin over http exits 2 naming the key (exit 2)
|
||||
OK federation peer origin with a path exits 2 naming the key (exit 2)
|
||||
OK federation peer origin with userinfo exits 2 naming the key (exit 2)
|
||||
OK federation peer id equal to self exits 2 naming the key (exit 2)
|
||||
OK federation peer label empty exits 2 naming the key (exit 2)
|
||||
OK federation not an object exits 2 naming the key (exit 2)
|
||||
OK federation self pattern exits 2 naming the key (exit 2)
|
||||
OK federation peers not an array exits 2 naming the key (exit 2)
|
||||
OK federation peer not an object exits 2 naming the key (exit 2)
|
||||
OK federation peer id pattern exits 2 naming the key (exit 2)
|
||||
OK federation peer label over 80 characters exits 2 naming the key (exit 2)
|
||||
OK federation duplicate peer id exits 2 naming the key (exit 2)
|
||||
OK federation peer origin with a trailing slash exits 2 naming the key (exit 2)
|
||||
OK federation self missing exits 2 naming the key (exit 2)
|
||||
OK federation peer id missing exits 2 naming the key (exit 2)
|
||||
OK federation peer label missing exits 2 naming the key (exit 2)
|
||||
OK config without federation loads and has no federation output
|
||||
OK config without console loads with unchanged output
|
||||
OK compactionReadBytes 8388608 loads and is in the output as written
|
||||
OK compactionReadBytes "x" loads and is in the output as written
|
||||
OK compactionStreams 2048 loads and is in the output as written
|
||||
OK compactionStreams "x" loads and is in the output as written
|
||||
OK failed validation modified nothing
|
||||
|
||||
selftest: 55 passed, 0 failed
|
||||
@@ -0,0 +1,86 @@
|
||||
toolchain: node v26.8.1
|
||||
|
||||
OK syntax: packages/discord/src/approvals.mjs
|
||||
OK syntax: packages/discord/src/authorize.mjs
|
||||
OK syntax: packages/discord/src/binding.mjs
|
||||
OK syntax: packages/discord/src/cli.mjs
|
||||
OK syntax: packages/discord/src/connector.mjs
|
||||
OK syntax: packages/discord/src/context.mjs
|
||||
OK syntax: packages/discord/src/engine-pi.mjs
|
||||
OK syntax: packages/discord/src/errors.mjs
|
||||
OK syntax: packages/discord/src/gateway.mjs
|
||||
OK syntax: packages/discord/src/git.mjs
|
||||
OK syntax: packages/discord/src/journal.mjs
|
||||
OK syntax: packages/discord/src/notify.mjs
|
||||
OK syntax: packages/discord/src/processing.mjs
|
||||
OK syntax: packages/discord/src/rest.mjs
|
||||
OK syntax: packages/discord/src/setspark.mjs
|
||||
OK syntax: packages/discord/src/tools.mjs
|
||||
OK syntax: packages/discord/src/web.mjs
|
||||
OK syntax: packages/discord/bin/git-credential.mjs
|
||||
OK syntax: packages/discord/extension/tools.mjs
|
||||
OK syntax: packages/discord/tests/approvals.test.mjs
|
||||
OK syntax: packages/discord/tests/authorize.test.mjs
|
||||
OK syntax: packages/discord/tests/binding.test.mjs
|
||||
OK syntax: packages/discord/tests/connector.test.mjs
|
||||
OK syntax: packages/discord/tests/context.test.mjs
|
||||
OK syntax: packages/discord/tests/copy-service.test.mjs
|
||||
OK syntax: packages/discord/tests/engine.test.mjs
|
||||
OK syntax: packages/discord/tests/fake-pi.mjs
|
||||
OK syntax: packages/discord/tests/gateway.test.mjs
|
||||
OK syntax: packages/discord/tests/git.test.mjs
|
||||
OK syntax: packages/discord/tests/helpers.mjs
|
||||
OK syntax: packages/discord/tests/journal.test.mjs
|
||||
OK syntax: packages/discord/tests/library-seat.test.mjs
|
||||
OK syntax: packages/discord/tests/notify.test.mjs
|
||||
OK syntax: packages/discord/tests/offline-check.test.mjs
|
||||
OK syntax: packages/discord/tests/processing-stop.test.mjs
|
||||
OK syntax: packages/discord/tests/processing.test.mjs
|
||||
OK syntax: packages/discord/tests/recover.test.mjs
|
||||
OK syntax: packages/discord/tests/rest.test.mjs
|
||||
OK syntax: packages/discord/tests/setspark.test.mjs
|
||||
OK syntax: packages/discord/tests/tools.test.mjs
|
||||
OK syntax: packages/discord/tests/web.test.mjs
|
||||
OK syntax: packages/discord/fixtures/claim-worker.mjs
|
||||
OK syntax: packages/discord/fixtures/legacy-owner-worker.mjs
|
||||
OK syntax: scripts/discord.sh
|
||||
OK syntax: scripts/discord-service.sh
|
||||
OK packages/discord declares no dependencies
|
||||
OK no bot-token-shaped string in packages/discord
|
||||
OK fixture binding uses placeholder ids only
|
||||
OK fixture binding validates
|
||||
OK real pi with the extension exposes exactly list_dir, read_file, search and no built-in tool
|
||||
OK real pi with a writable root exposes exactly the three reads plus write_file and edit_file, and writes nothing at start
|
||||
OK real pi with a web key exposes the three reads plus web_fetch and web_search, and no write tool without a writable root
|
||||
OK real pi with a git root exposes the reads, writes and the four git verbs, commits nothing at start, and never shows the token
|
||||
OK real pi with protocol vault adds reserve_id to the git verbs
|
||||
OK real pi with a setspark key exposes the reads and the eight record verbs, no counters, and never shows the key
|
||||
OK real pi refuses a git key on a read-only root (fail closed)
|
||||
OK real pi with the pilot flags (--no-tools) exposes no tool at all
|
||||
OK real pi exits non-zero without MOSAIC_DISCORD_TOOLS: no session, no tools (fail closed)
|
||||
OK a failing nested test fails the run under a parent runner's NODE_TEST_CONTEXT
|
||||
OK node --test packages/discord/tests/ (ℹ pass 200)
|
||||
OK scripts/discord.sh --help exits 0
|
||||
OK scripts/discord.sh check without a binding exits 4
|
||||
OK scripts/discord.sh recover without a binding exits 4
|
||||
OK scripts/discord.sh reload without a binding exits 4
|
||||
OK scripts/discord-service.sh without a command exits 4
|
||||
OK service unit renders with the repository path, a supervised run as the main process, exit 3 never retried, and reload as SIGHUP
|
||||
OK service install writes the rendered unit (0644) and leaves no temp file
|
||||
OK service install a second time reports unchanged
|
||||
OK systemd-analyze verify accepts the rendered unit
|
||||
OK service uninstall removes the unit file
|
||||
OK service install with an unknown flag exits 4
|
||||
OK service install with USER unset finishes and names the account for lingering
|
||||
OK pinned render runs copy CLI with copy cwd/repo and no checkout executable
|
||||
OK pinned install writes the rendered unit in a temporary unit directory
|
||||
OK pinned install refuses short prerequisite and writes no unit
|
||||
OK pinned install refuses missing prerequisite and writes no unit
|
||||
OK pinned install refuses cli prerequisite and writes no unit
|
||||
OK pinned install refuses pi prerequisite and writes no unit
|
||||
OK status names the copy from fake systemctl effective cwd
|
||||
OK checkout install explicitly recommends --copy
|
||||
OK wrapper check --offline passes local fixture and makes no network call
|
||||
OK wrapper run --offline refuses usage
|
||||
|
||||
discord suite: 82 passed, 0 failed
|
||||
@@ -0,0 +1,21 @@
|
||||
OK initial ordinary-file install
|
||||
OK installed tree matches canonical source
|
||||
OK installed tree has no symlinks
|
||||
OK check detects installation drift
|
||||
OK sync refuses to overwrite installation drift
|
||||
OK check detects an extra destination file
|
||||
OK check detects an extra destination directory
|
||||
OK check rejects a destination symlink
|
||||
OK sync accepts a canonical source update
|
||||
OK updated installation matches canonical source
|
||||
scripts/test-extension-package.sh: line 14: 383938 Killed "$@" > /dev/null 2>&1
|
||||
OK forced interruption kills the replacing process
|
||||
OK next invocation recovers old consistent installation
|
||||
OK interrupted replacement rolled back
|
||||
OK sync succeeds after interruption recovery
|
||||
OK unlocked stale lock file does not block
|
||||
OK active lock refuses a concurrent sync
|
||||
OK source symlink fails closed
|
||||
OK nested second entrypoint fails closed
|
||||
|
||||
extension package selftest: 18 passed, 0 failed
|
||||
@@ -0,0 +1,10 @@
|
||||
✔ FED R7 scratch two-instance acceptance (3271.695253ms)
|
||||
ℹ tests 1
|
||||
ℹ suites 0
|
||||
ℹ pass 1
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 3348.152983
|
||||
R7 cleanup: no child groups, ports or sockets remain; no units created
|
||||
@@ -0,0 +1,53 @@
|
||||
toolchain: node v26.8.1, python 3.12.8, jsonschema 4.26.0
|
||||
|
||||
OK syntax: scripts/foundation-inspect.mjs
|
||||
OK syntax: scripts/foundation/strict-json.mjs
|
||||
OK syntax: scripts/foundation/canonical.mjs
|
||||
OK syntax: scripts/foundation/resolve.mjs
|
||||
OK syntax: scripts/foundation/validate-record.mjs
|
||||
OK syntax: scripts/foundation/fixtures/build-fixtures.mjs
|
||||
OK syntax: scripts/foundation/canonical.test.mjs
|
||||
OK syntax: scripts/foundation/cli.test.mjs
|
||||
OK syntax: scripts/foundation/fixtures.test.mjs
|
||||
OK syntax: scripts/foundation/resolve.test.mjs
|
||||
OK syntax: scripts/foundation/strict-json.test.mjs
|
||||
OK syntax: scripts/foundation/verify-schema.py (ast only; no bytecode written)
|
||||
OK fixture generator runs
|
||||
OK checked-in fixtures/bundles equal a fresh generation
|
||||
OK checked-in fixtures/raw equal a fresh generation
|
||||
OK checked-in fixtures/index.json equal a fresh generation
|
||||
OK checked-in demo bundles equal a fresh generation
|
||||
OK a failing nested test fails the run under a parent runner's NODE_TEST_CONTEXT
|
||||
OK node --test scripts/foundation/ (ℹ pass 80)
|
||||
OK differential schema oracle: PASS: differential schema oracle (finite corpus; compatibility evidence, not equivalence proof)
|
||||
platform witness: strftime('%Y') for year 999 -> '999' (pinned checker refuses years 0001..0999)
|
||||
node v26.8.1; corpus 1568 records (38 pinned fixtures, 478 unique bundle records, 1052 typeCase/mutation/lexical cases)
|
||||
schema column: agree-valid 540, agree-invalid 991, DISAGREEMENTS 0; strict-only (parser-bound) cases: 27; unsupported-kind records not schema-assessed by the inspector: 10
|
||||
profile column (schema-valid records only): profile-valid 510, profile-invalid 30
|
||||
profile refusals asserted: 30 schema-agreed-valid records refused only by the strict typed-string profile (rule profile-pattern-mismatch), 12 declared by name; 73 named probes verified against declared schema/profile columns
|
||||
OK oracle: zero schema-column disagreements with the pinned checker
|
||||
OK oracle: strict-only profile refusals are counted and asserted
|
||||
OK demo: permitted read preview exits 0 (exit 0)
|
||||
OK demo: permitted file.change preview exits 0 (exit 0)
|
||||
OK demo: assignment.change proposal is unresolved (exit 3) (exit 3)
|
||||
OK demo: revoked registration is refused (exit 3) (exit 3)
|
||||
OK demo: message is not authority (exit 3) (exit 3)
|
||||
OK usage: no arguments exits 2 (exit 2)
|
||||
OK io: missing file exits 4 (exit 4)
|
||||
OK io: directory exits 4 (exit 4)
|
||||
OK io: symlink exits 4 (O_NOFOLLOW) (exit 4)
|
||||
OK bound: oversize fixture exits 2 (exit 2)
|
||||
OK profile: one final LF in a typed selection id is refused before admission (exit 2) (exit 2)
|
||||
OK profile: two final LFs fail the schema pattern itself (exit 2) (exit 2)
|
||||
OK profile: escaped newlines in free-form text stay allowed (exit 0) (exit 0)
|
||||
OK profile refusal is invalid-request/profile-pattern-mismatch with selection and operation withheld, value not echoed
|
||||
OK text output starts with the disclaimer
|
||||
OK json output is valid JSON with result allowed and exactly the charter §7 fields
|
||||
OK json golden matches byte-for-byte
|
||||
OK sandboxed bundle run (env -i, PATH=/nonexistent) produced the unresolved proposal
|
||||
OK sandbox inventory (path/type/size/mode/uid/gid/inode/mtime/sha256) unchanged by runs
|
||||
OK canary never printed (bundle run and credential-file run)
|
||||
OK a non-bundle JSON file is refused at the shape gate, not read into output
|
||||
OK no field of the non-bundle file is echoed
|
||||
|
||||
selftest: 44 passed, 0 failed
|
||||
@@ -0,0 +1,88 @@
|
||||
OK args: exit 2, target-branch
|
||||
OK args: exit 2, target-branch
|
||||
OK args: exit 4
|
||||
OK args: exit 4
|
||||
OK args: exit 4
|
||||
OK --base next refuses before the config is read (got 2)
|
||||
OK a missing system config exits 3 (got 3)
|
||||
OK nobus: exit 3
|
||||
OK schema-table: exit 2, bus-schema
|
||||
OK schema-meta: exit 2, bus-schema
|
||||
OK openBus opens the database read-only
|
||||
OK other-business: exit 2, decision-not-found
|
||||
OK no-raise: exit 2, decision-not-found
|
||||
OK not-a-merge: exit 2, not-a-merge
|
||||
OK open: exit 2, not-resolved
|
||||
OK withdrawn: exit 2, not-resolved
|
||||
OK by-agent: exit 2, not-human
|
||||
OK via-webui: exit 2, not-human
|
||||
OK no-answer: exit 2, not-human
|
||||
OK answer-role: exit 2, not-human
|
||||
OK answer-run: exit 2, not-human
|
||||
OK answer-choice: exit 2, not-human
|
||||
OK two-answers: exit 2, not-human
|
||||
OK arbiter-cross: exit 2, not-human
|
||||
OK arbiter-cited: exit 2, not-human
|
||||
OK chose-no: exit 2, not-merge-option
|
||||
OK two-refs: exit 2, work-ref
|
||||
OK bad-ref: exit 2, work-ref
|
||||
OK task-ref-disagrees: exit 2, work-ref
|
||||
OK raise-task-disagrees: exit 2, work-ref
|
||||
OK target-main: exit 2, target-branch
|
||||
OK superseded-question: exit 2, superseded
|
||||
OK superseded-task: exit 2, superseded
|
||||
OK superseded-by: exit 2, superseded
|
||||
OK no-verdict: exit 2, no-verdict
|
||||
OK changes-later: exit 2, not-approved
|
||||
OK short-commit: exit 2, not-approved
|
||||
OK wrong-task-body: exit 2, not-approved
|
||||
OK args: exit 2, decision-not-found
|
||||
OK earlier-merge: exit 0
|
||||
OK ref-moved: exit 2, ref-moved
|
||||
OK root-top: exit 2, root-file
|
||||
OK root-newdir: exit 2, root-file
|
||||
OK root-src: exit 2, root-file
|
||||
OK unrelated: exit 2, base-conflict
|
||||
OK empty: exit 2, base-conflict
|
||||
OK an empty reviewed diff says so
|
||||
OK dirty-changed: exit 2, checkout-dirty
|
||||
OK dirty-staged: exit 2, checkout-dirty
|
||||
OK dirty-untracked: exit 2, checkout-dirty
|
||||
OK conflict: exit 2, base-conflict
|
||||
OK a refusal after the worktree is added leaves no worktree or scratch
|
||||
OK cleanup looks for the worktree's Docker network
|
||||
OK dirty-during: exit 2, checkout-dirty
|
||||
OK a reviewed path dirtied during the gate moves no ref
|
||||
OK drift: exit 2, patch-drift
|
||||
OK patch-drift leaves no worktree or scratch
|
||||
OK gate-pkg: exit 2, gate-failed
|
||||
OK gate-failed names the failing step's log, and it exists
|
||||
OK gate-failed leaves no worktree or scratch
|
||||
OK gate-script: exit 2, gate-failed
|
||||
OK a failing scripts/test-*.sh fails the gate
|
||||
OK a failed gate moves no ref
|
||||
OK nomods: exit 4
|
||||
OK a target without node_modules leaves no worktree or scratch
|
||||
OK moved: exit 2, base-moved
|
||||
OK base-moved keeps the base where it moved to
|
||||
OK dry: exit 0
|
||||
OK --dry-run writes no ref
|
||||
OK --dry-run prints the commit with --subject and --attribution
|
||||
OK --dry-run runs the gate
|
||||
OK --dry-run leaves no worktree or scratch
|
||||
OK land: exit 0
|
||||
OK the landing is one commit on the old base
|
||||
OK its diff is the reviewed diff by patch-id
|
||||
OK its tree is the dry run's
|
||||
OK the message has the option's subject, the citations and the attribution last
|
||||
OK it prints the line for the PM
|
||||
OK the checked-out base has the reviewed paths and keeps the unrelated edit
|
||||
OK only refs/heads/refactor exists in the target
|
||||
OK the work ref is unchanged
|
||||
OK the bus database is unchanged
|
||||
OK the gate ran each package suite but queue, and each scripts/test-*.sh
|
||||
OK the landing leaves no worktree or scratch
|
||||
OK the target's node_modules is still there, with its contents
|
||||
OK the worktree's Docker network is removed
|
||||
---
|
||||
land-work selftests: 86 passed, 0 failed
|
||||
@@ -0,0 +1 @@
|
||||
timeout: failed to run command ‘scripts/test-land-work.sh’: Permission denied
|
||||
@@ -0,0 +1,7 @@
|
||||
PASS push-check 38 checks
|
||||
KILLED skip-hash
|
||||
KILLED accept-changes
|
||||
KILLED earlier-round
|
||||
KILLED working-tree
|
||||
KILLED no-row
|
||||
KILLED skip-nul-scan
|
||||
@@ -0,0 +1,36 @@
|
||||
toolchain: node v26.8.1, git version 2.55.0
|
||||
|
||||
OK syntax: packages/queue/src/cli.mjs
|
||||
OK syntax: packages/queue/src/errors.mjs
|
||||
OK syntax: packages/queue/src/io.mjs
|
||||
OK syntax: packages/queue/src/lock.mjs
|
||||
OK syntax: packages/queue/src/queue.mjs
|
||||
OK syntax: packages/queue/src/review.mjs
|
||||
OK syntax: packages/queue/src/store.mjs
|
||||
OK syntax: packages/queue/tests/commit.test.mjs
|
||||
OK syntax: packages/queue/tests/data.test.mjs
|
||||
OK syntax: packages/queue/tests/dispatch.test.mjs
|
||||
OK syntax: packages/queue/tests/gate-owner.test.mjs
|
||||
OK syntax: packages/queue/tests/helpers.mjs
|
||||
OK syntax: packages/queue/tests/lock.test.mjs
|
||||
OK syntax: packages/queue/tests/migration.test.mjs
|
||||
OK syntax: packages/queue/tests/review.test.mjs
|
||||
OK syntax: packages/queue/tests/store.test.mjs
|
||||
OK syntax: packages/queue/tests/write.test.mjs
|
||||
OK syntax: packages/queue/tests/fixtures/fake-gitea.mjs
|
||||
OK syntax: packages/queue/tests/fixtures/kill-at.mjs
|
||||
OK syntax: packages/queue/tests/fixtures/lock-child.mjs
|
||||
OK syntax: packages/queue/tests/fixtures/mosaic-pre-a2.sh
|
||||
OK syntax: scripts/queue-commit.sh
|
||||
OK syntax: scripts/git-hooks/pre-commit
|
||||
OK syntax: scripts/mosaic
|
||||
OK queue-commit.sh, the guard and scripts/mosaic are executable
|
||||
OK packages/queue declares no dependencies
|
||||
ℹ tests 156
|
||||
ℹ pass 156
|
||||
ℹ fail 0
|
||||
OK node --test packages/queue/tests/
|
||||
OK scripts/mosaic queue help
|
||||
skip queue verify and render --check: this checkout (/home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287) is not the queue's canonical root (/mnt/storage/src/mosaic-stack)
|
||||
|
||||
queue suite: 28 passed, 0 failed
|
||||
@@ -0,0 +1,17 @@
|
||||
OK valid RELEASE resolves (exit 0)
|
||||
OK invalid RELEASE exits 1 (exit 1)
|
||||
OK missing RELEASE exits 1 (exit 1)
|
||||
OK valid RELEASE leaves image tag consistent with version
|
||||
OK status safe on empty state (exit 0)
|
||||
OK status created no pointer
|
||||
OK fault-injected activation refuses (exit 1)
|
||||
OK refused activation wrote no pointer
|
||||
OK refusal logged exactly once with valid fields
|
||||
OK healthy activation succeeds (exit 0)
|
||||
OK pointer written with valid fields
|
||||
OK repeat activation succeeds (log grows) (exit 0)
|
||||
OK log is append-only across activations
|
||||
OK rollback without previous refuses (exit 1)
|
||||
OK compose network made under the run's project and removed after the run
|
||||
|
||||
selftest: 15 passed, 0 failed
|
||||
@@ -0,0 +1,97 @@
|
||||
OK valid task validates (exit 0)
|
||||
OK unknown task key exits 2 (exit 2)
|
||||
OK unsupported taskVersion exits 2 (exit 2)
|
||||
OK invalid task id exits 2 (exit 2)
|
||||
OK empty prompt exits 2 (exit 2)
|
||||
OK NUL in expectExact exits 2 (exit 2)
|
||||
OK out-of-range timeout exits 2 (exit 2)
|
||||
OK missing mission file exits 4 (exit 4)
|
||||
OK task with valid mission validates (exit 0)
|
||||
OK invalid mission exits 2 (exit 2)
|
||||
OK validate missing task exits 4 (exit 4)
|
||||
OK validation does not modify the task file
|
||||
OK prune dry-run exits 0 (exit 0)
|
||||
OK dry-run deleted nothing
|
||||
OK prune --keep=2 --yes removes oldest (exit 0)
|
||||
OK kept exactly 2 newest runs
|
||||
OK newest run kept, oldest pruned
|
||||
OK append-only receipt written (3 entries)
|
||||
OK sessions/workspaces untouched by prune
|
||||
OK prune with invalid keep exits 4 (exit 4)
|
||||
OK mock adapter: gate passes on matching mock response (exit 0)
|
||||
OK mock adapter: expect-mismatch recorded (exit 1)
|
||||
OK mismatch reason recorded
|
||||
OK unknown adapter fails closed (exit 1)
|
||||
OK mission task runs via mock adapter (exit 0)
|
||||
OK mission section injected into generated prompt
|
||||
OK retry of mission run succeeds (exit 0)
|
||||
OK retriedFrom lineage recorded
|
||||
OK mission section present after retry (relative path resolved)
|
||||
OK retry of missing run exits 4 (exit 4)
|
||||
OK skill install bundled ms-tools (exit 0)
|
||||
OK installed to skills-available
|
||||
OK double install refuses (exit 1)
|
||||
OK activate enables skill (exit 0)
|
||||
OK enabled dir populated
|
||||
OK uninstall while enabled refuses (exit 1)
|
||||
OK deactivate moves back to available (exit 0)
|
||||
OK uninstall removes from available (exit 0)
|
||||
OK seat with enabled skill launches
|
||||
OK skill path delivered to adapter
|
||||
OK shipped researcher contract resolves (ceiling + network)
|
||||
OK resolve-role refuses a non-role document (conductor policy) (exit 2)
|
||||
OK resolve-role refuses name/filename mismatch (exit 2)
|
||||
OK resolve-role refuses unknown network declaration (exit 2)
|
||||
OK resolve-role refuses duplicate role tool (exit 2)
|
||||
OK resolve-role refuses unsupported tool (exit 2)
|
||||
OK resolve-role refuses missing contract file (exit 4)
|
||||
OK version 1 role prints no contract line
|
||||
OK shipped version 2 roles resolve with their contracts (pm, cto, coder, reviewer)
|
||||
OK resolve-role accepts a minimal version 2 role (exit 0)
|
||||
OK resolve-role refuses an action outside the vocabulary (exit 2)
|
||||
OK resolve-role refuses a gated-only action in a role file (exit 2)
|
||||
OK resolve-role refuses a Vikunja verb no role may hold (exit 2)
|
||||
OK resolve-role refuses a version 2 role whose contract is missing (exit 2)
|
||||
OK resolve-role refuses a contract that is a symbolic link (exit 2)
|
||||
OK seat launches under role ceiling
|
||||
OK role ceiling narrows seat tools (read,write,bash -> read,bash)
|
||||
OK narrowing recorded loudly
|
||||
OK missing role contract refuses launch (exit 2)
|
||||
OK missing-contract refusal names the role
|
||||
OK empty ceiling intersection -> tool-free seat
|
||||
OK tool-free outcome recorded loudly
|
||||
OK overridden agents dir without seat definition refuses (exit 4)
|
||||
OK seat-resolution refusal names the seat
|
||||
OK policy: mission only -> mission tools
|
||||
OK policy: task only -> task tools
|
||||
OK policy: both -> intersection (task narrowed)
|
||||
OK policy: empty intersection -> tool-free
|
||||
OK invalid mission capabilities rejected (exit 2)
|
||||
mosaic-task: unsupported mission tool: "sudo" (supported: read, write, edit, bash, grep, find, ls)
|
||||
OK task run with user layer present (exit 0)
|
||||
OK user context dispatched into generated prompt
|
||||
OK workspace+tools task runs via mock (exit 0)
|
||||
OK workspace path + tools delivered to adapter
|
||||
OK persistent workspace created on host
|
||||
OK plain task still runs (no workspace/tools) (exit 0)
|
||||
OK workspace var present but empty when absent
|
||||
OK tools empty when absent
|
||||
OK unknown tool exits 2 (exit 2)
|
||||
OK workspace traversal exits 2 (exit 2)
|
||||
OK hello task succeeds with exact marker
|
||||
OK result.json written in run dir
|
||||
OK result.json contents are correct
|
||||
OK wrong expectExact fails with exit 1 (reason: expect-mismatch)
|
||||
OK each run gets a distinct run dir (no clobber)
|
||||
OK list shows both runs
|
||||
skip live provider cases (row 244: no provider call in the gate)
|
||||
OK onboard without name exits 4 (non-interactive) (exit 4)
|
||||
OK onboard --name renders profile (exit 0)
|
||||
OK profile written
|
||||
OK canon structure: required filled, optional placeholdered
|
||||
OK canon sections present
|
||||
skip live provider cases (row 244: no provider call in the gate)
|
||||
OK no agent identity on headless run
|
||||
OK compose network made under the run's project and removed after the run
|
||||
|
||||
selftest: 92 passed, 0 failed
|
||||
@@ -0,0 +1,5 @@
|
||||
known fixture values searched: 3
|
||||
logs searched: 37
|
||||
fixture hits: 0
|
||||
Docker fact runner separately checked all four actual minted values against complete TAP stdout/stderr: hits 0.
|
||||
Fake API cases assertClean/noLeak cover every issued token against events, messages, decisions and request logs.
|
||||
@@ -0,0 +1,265 @@
|
||||
✔ versionAtLeast compares numbers and refuses a prerelease of 1.27.0 (1.139073ms)
|
||||
✔ X-G1: a raise that fails a check refuses and stores nothing (75.530438ms)
|
||||
✔ X-G1: a creative role, a role without identity and a missing or due slot refuse at raise (193.46698ms)
|
||||
✔ X-G1: the chief of staff refuses at raise, whatever its definition, identity and publish declare (39.523907ms)
|
||||
✔ X-G2: admin set refuses each failed check and writes nothing, then writes the slot once (153.731441ms)
|
||||
✔ X-G3 to X-G8: an approve makes the user, mints the token, forks, checks reach and writes the file, record and grant.applied (106.729946ms)
|
||||
✔ X-G4: a user this stack did not make, an admin, a disabled user and a remade user each refuse (363.191747ms)
|
||||
✔ X-G6 and X-G8: the user and mint steps are written before their calls, and each result after its call (100.705375ms)
|
||||
✔ X-G4: a fork by the seat user of another repository than the project refuses fork-foreign (86.135966ms)
|
||||
✔ X-G5: a failure after the mint deletes the token, unloads nothing it did not load and leaves no file (164.209877ms)
|
||||
✔ X-G5: a token delete that fails is left at Gitea, shown by status and taken by sweep (125.633285ms)
|
||||
✔ X-G5: a mint that fails deletes by name, and a refusal at step 0 leaves the decision unspent (151.600735ms)
|
||||
✔ X-G5: an agent held for an unknown token is refused at step 0 with no Gitea call (65.645239ms)
|
||||
✔ X-G5 and X-G9 (step 7): a rotation whose old-token delete fails stays applied with the old id left (174.510424ms)
|
||||
✔ X-G10: in a rotation the store file holds the new token before the old one is deleted (148.773407ms)
|
||||
✔ X-G6: a rotation cut before the rename settles back at once, and the old token stays live (171.547646ms)
|
||||
✔ X-G6: a rotation whose step 8 write fails is settle-pending, and a restart settles it forward (179.655701ms)
|
||||
✔ X-G6: a restart settles an open rotation back when the file still holds the old token (135.974464ms)
|
||||
✔ X-G6: a restart settles an open rotation forward when the file holds the new token (135.770314ms)
|
||||
✔ X-G6: a 401 on the file token loses the grant: both tokens go, the record goes and memory unloads (138.899782ms)
|
||||
✔ X-G6: a 403, 404, 500, a hang and a dropped socket each leave the rotation settle-pending with nothing deleted (1029.94154ms)
|
||||
✔ X-G6: a file token no grant minted is held; the next restart says so again; a revoke clears it and takes both ids (162.896203ms)
|
||||
✔ N9: a revoke of an open rotation, then a restart: no settle, no Gitea call and no message (138.973458ms)
|
||||
✔ N9: a 404 on a delete by id counts as done (133.050723ms)
|
||||
✔ X-G6: a restart undoes each step a killed child left, and spends an approved unspent decision (414.134804ms)
|
||||
✔ X-G6: a restart with no admin slot leaves the user an orphan and the token left (55.861549ms)
|
||||
✔ X-G7: a change to the definition, identity, publish remotes or admin slot after the raise ends decision-mismatch (125.765675ms)
|
||||
✔ X-G7 and X-G9: an admin rotate voids open decisions, and rotate --finish deletes what was left (114.25118ms)
|
||||
✔ X-G9: a rotate whose name is taken refuses and writes nothing; remove takes the slot away (66.692412ms)
|
||||
✔ X-G9: a slot past its rotate-by date refuses a run at step 0, leaving the decision unspent (49.649111ms)
|
||||
✔ X-G11: run refuses every caller but the human, an unapproved, a spent and a busy decision (108.282506ms)
|
||||
✔ kill switch (plan :16): while halted the chief still reads grants.status, and an agent grants.revoke refuses (106.579322ms)
|
||||
✔ X-G11: a revoke while the executor runs aborts it and the grant ends revoked (107.282814ms)
|
||||
✔ X-G11: a revoke unloads memory and removes the file before its Gitea delete (131.713832ms)
|
||||
✔ X-G11: a revoke during a rotation step 7 ends it revoked, and step 8 writes no record back (203.032995ms)
|
||||
✔ X-G11: a revoke while a sweep settles a rotation, forward or back, ends it revoked with no record (329.237353ms)
|
||||
✔ X-G11: a revoke during a settle whoami that finds a foreign token ends it revoked, not held (194.559652ms)
|
||||
✔ X-G11: a revoke during a settle whoami that gets a 401 ends it revoked, not lost (184.236777ms)
|
||||
✔ X-G12: a token that reaches wider than its forks fails with the repository named (389.923565ms)
|
||||
✔ X-G12: reach is paged by X-Total-Count and a count that does not add up refuses (266.358571ms)
|
||||
✔ X-G13: an agent cannot record a grant event, and the trigger refuses one from any writer but the grant writer (43.235444ms)
|
||||
✔ X-G14: credentials delete fires the user: tokens go, login is prohibited, and grant.revoke.closed records it (281.776428ms)
|
||||
✔ X-G14: a fire whose token delete or login PATCH fails lands it in serviceLeft (133.556646ms)
|
||||
✔ X-G14: a fire whose user is gone or remade skips the fire steps and says why (264.851425ms)
|
||||
✔ X-G14: a sweep retries a fire only for the user it fired: a remade or gone user is skipped and named (279.235184ms)
|
||||
✔ X-G14: a revoke Gitea cannot reach stays open, and a restart closes it (133.659853ms)
|
||||
✔ row 281 T8: a tester grants.revoke refuses tester-dev-only once bus.tester is off; the human revoke keeps its records and starts no job (113.137966ms)
|
||||
✔ X-G15: look-alike commit steps outside the grant writer are ignored at start, by revoke and by status (112.990765ms)
|
||||
✔ X-G15: a look-alike spend under another kind with the grant writer pair is not an open grant (46.225594ms)
|
||||
✔ status lists the slot, users, grants and nothing secret, and every grants verb needs service gitea (113.742539ms)
|
||||
✔ C2-21 L1/L2/L9: research gets write on her repository with declared scopes through raise and yes (129.484221ms)
|
||||
✔ C2-21 L1/L2/L9: chief-of-staff gets read on her repository with declared scopes through raise and yes (106.138514ms)
|
||||
✔ C2-21 L1/L2/L9: coder gets read on her repository with declared scopes through raise and yes (104.316693ms)
|
||||
✔ C2-21 L7/L7b: research refuses non-library targets lib (42.155496ms)
|
||||
✔ C2-21 L7/L7b: research refuses non-library targets app,lib (40.652006ms)
|
||||
✔ C2-21 L7/L7b: chief-of-staff refuses non-library targets lib (40.430096ms)
|
||||
✔ C2-21 L7/L7b: chief-of-staff refuses non-library targets app,lib (40.868584ms)
|
||||
✖ C2-21 L4: read widened to write refuses gitea-reach-wider and deletes the minted token (64.643789ms)
|
||||
✖ C2-21 L4: admin privilege refuses gitea-reach-wider and deletes the minted token (63.595938ms)
|
||||
✖ C2-21 L4: missing pull refuses gitea-reach-wider and deletes the minted token (64.302062ms)
|
||||
✖ C2-21 L4: missing permission object refuses gitea-reach-wider and deletes the minted token (65.102053ms)
|
||||
✖ C2-21 L4: library fork refuses gitea-reach-wider and deletes the minted token (64.826649ms)
|
||||
✔ C2-21 L8: untargeted library collaborator reach is disclosed and accepted publish-wide (87.339871ms)
|
||||
✖ C2-21 L10: project kind or library level changing after raise refuses decision-mismatch (64.895333ms)
|
||||
✖ C2-21 L4: creative write requires push true but ignores unrelated permission keys (64.965654ms)
|
||||
✔ C2-03b T6: the slot before any chief evidence, then the listed chief's live grant (32.72782ms)
|
||||
✔ C2-03b T7: a chief revoked through grants.revoke or fired through instance.fire refuses, and the fallback stays ended (296.498344ms)
|
||||
✔ C2-03b T7 choice checks: delisted, a fire under way and held each refuse; a rehire lists the chief again (36.638485ms)
|
||||
✔ C2-03b T8: a chief grant on another project ends the fallback, and two listed chiefs refuse library-fetch-no-grant (32.149673ms)
|
||||
✔ C2-03b departure (sage-2928): with no chief entry, any Gitea grant evidence refuses (18.858172ms)
|
||||
✔ C2-03b T9 runtime: the chief's store file and user, then a real revoke that runs no git with the slot at 0000 (782.448934ms)
|
||||
✔ C2-04: library mappings load without roots, remain frozen, and retain mapping refusals (11.031163ms)
|
||||
✔ C2-04: an otherwise valid publish grant cannot name a library repo (1.672501ms)
|
||||
✔ config directory and file path follow MOSAIC_CONFIG (0.332343ms)
|
||||
✔ the fixture business validates and comes back frozen (0.995298ms)
|
||||
✔ two instances may share a definition (0.963652ms)
|
||||
✔ top-level refusals (2.767585ms)
|
||||
✔ arbiters and projects (3.905801ms)
|
||||
✔ role instances (2.3953ms)
|
||||
✔ Vikunja bots (4.888461ms)
|
||||
✔ a role without Vikunja takes no tracker block (8.053822ms)
|
||||
✔ credential references match the definition's services (3.719872ms)
|
||||
✔ launch (11.051038ms)
|
||||
✔ REQ-THREAD-2 REQ-LAUNCH-1 launch.threadIdleSeconds: absent is the default, a positive integer is used, anything else refuses (6.346401ms)
|
||||
✔ loadBusiness: file checks (2.217137ms)
|
||||
✔ loadBusiness: not a regular file (44.303522ms)
|
||||
✔ loading writes nothing (1.158385ms)
|
||||
✔ names that are Object.prototype properties don't count as declared (3.580542ms)
|
||||
✔ the shipped example refuses as written and validates once filled in (1.167705ms)
|
||||
✔ persona vars: agent layer, creative definition only, both or neither (9.835969ms)
|
||||
✔ row 285: a chief or creative instance without credentials.gitea refuses at load (7.574546ms)
|
||||
✔ row 285: a creative instance with identity and publish loads (0.773536ms)
|
||||
✔ row 194 keep, noHire and retired validate, and a file without them loads as before (2.123104ms)
|
||||
✔ row 194 each bad keep, noHire or retired entry refuses (7.372634ms)
|
||||
✔ C2-21: only creative definitions and the chief with gitea may publish a library project (12.000435ms)
|
||||
✔ the library install writes the business file, personas and slot dir, makes the copy and starts nothing (260.836427ms)
|
||||
✔ a library re-run passes and leaves every file as it was (404.249515ms)
|
||||
✔ a re-run refuses business-file-differs and leaves the file unchanged (279.629668ms)
|
||||
✔ a re-run refuses persona-differs when an installed persona isn't the staged one, and writes nothing (284.561951ms)
|
||||
✔ the factory install writes no library mapping, persona or slot dir (178.261551ms)
|
||||
✔ the factory install refuses a library project and bus.tester off, before anything is written (71.855815ms)
|
||||
✔ the library install refuses bus.tester on and a persona var in the input (80.555838ms)
|
||||
✔ the install refuses root, a business id mismatch, another --home and a config dir with group bits (54.023333ms)
|
||||
✔ the library install needs --personas (5.849596ms)
|
||||
✔ a validate refusal removes the business file this run wrote (233.892356ms)
|
||||
✔ N4: a validate refusal on a re-run leaves the matching business file in place, byte for byte (404.647484ms)
|
||||
✔ a clean library host passes the precheck with no mirror yet (426.936455ms)
|
||||
✔ the library precheck refuses a persona file at 0644 (398.6846ms)
|
||||
✔ the library precheck refuses a persona whose digest isn't persona.sha256 (404.193929ms)
|
||||
✔ the library precheck refuses a persona dir with group bits (411.810016ms)
|
||||
✔ the library precheck refuses a business file at 0644 (404.979409ms)
|
||||
✔ the library precheck refuses a changes.jsonl at 0644 (572.602427ms)
|
||||
✔ the library precheck refuses a Discord binding (408.215092ms)
|
||||
✔ --library-status refuses library-mirror-missing before the bus has made the mirror (324.192926ms)
|
||||
✔ --library-status refuses library-never-synced on a fresh mirror (337.170027ms)
|
||||
✔ --library-status names a recorded lastError before the null lastFetch it leaves (N3) (354.03021ms)
|
||||
✔ --library-status prints each R-problem's path as an escaped JSON string, then the count, and passes (397.284837ms)
|
||||
✔ --library-status takes no --profile (5.679559ms)
|
||||
✔ Vikunja 614: install preserves the explicit notifier decision bytes and mode and creates none when absent (477.032132ms)
|
||||
✔ row 322 F8: library personas install from the copy with no clone node_modules (138.947889ms)
|
||||
✔ row 322 F9: importer refusal keeps the error line rather than the Node banner (94.497291ms)
|
||||
✔ C2-21: library install supplies chief identity and books publish for its real read grant (241.629315ms)
|
||||
ℹ tests 122
|
||||
ℹ suites 0
|
||||
ℹ pass 115
|
||||
ℹ fail 7
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 9691.559494
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/bus/tests/gitea-grant.test.mjs:1386:3
|
||||
✖ C2-21 L4: read widened to write refuses gitea-reach-wider and deletes the minted token (64.643789ms)
|
||||
AssertionError [ERR_ASSERTION]: {"action":"grant.failed","method":"gitea","grantDecision":"e07beff5-0dd2-474a-987b-7f742de95dee","agent":"chief","code":"grant-admin-not-dedicated"}
|
||||
+ actual - expected
|
||||
|
||||
+ 'grant-admin-not-dedicated'
|
||||
- 'gitea-reach-wider'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:1394:12)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 'grant-admin-not-dedicated',
|
||||
expected: 'gitea-reach-wider',
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/bus/tests/gitea-grant.test.mjs:1386:3
|
||||
✖ C2-21 L4: admin privilege refuses gitea-reach-wider and deletes the minted token (63.595938ms)
|
||||
AssertionError [ERR_ASSERTION]: {"action":"grant.failed","method":"gitea","grantDecision":"83dfd9a1-0794-4fb2-8fa5-a21aecaaa40a","agent":"chief","code":"grant-admin-not-dedicated"}
|
||||
+ actual - expected
|
||||
|
||||
+ 'grant-admin-not-dedicated'
|
||||
- 'gitea-reach-wider'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:1394:12)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 'grant-admin-not-dedicated',
|
||||
expected: 'gitea-reach-wider',
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/bus/tests/gitea-grant.test.mjs:1386:3
|
||||
✖ C2-21 L4: missing pull refuses gitea-reach-wider and deletes the minted token (64.302062ms)
|
||||
AssertionError [ERR_ASSERTION]: {"action":"grant.failed","method":"gitea","grantDecision":"88073d5b-3e4f-4a9a-b241-5b110d3be2c0","agent":"chief","code":"grant-admin-not-dedicated"}
|
||||
+ actual - expected
|
||||
|
||||
+ 'grant-admin-not-dedicated'
|
||||
- 'gitea-reach-wider'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:1394:12)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 'grant-admin-not-dedicated',
|
||||
expected: 'gitea-reach-wider',
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/bus/tests/gitea-grant.test.mjs:1386:3
|
||||
✖ C2-21 L4: missing permission object refuses gitea-reach-wider and deletes the minted token (65.102053ms)
|
||||
AssertionError [ERR_ASSERTION]: {"action":"grant.failed","method":"gitea","grantDecision":"d2e4e251-e9f3-4ede-aa40-284f014243ab","agent":"chief","code":"grant-admin-not-dedicated"}
|
||||
+ actual - expected
|
||||
|
||||
+ 'grant-admin-not-dedicated'
|
||||
- 'gitea-reach-wider'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:1394:12)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 'grant-admin-not-dedicated',
|
||||
expected: 'gitea-reach-wider',
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/bus/tests/gitea-grant.test.mjs:1386:3
|
||||
✖ C2-21 L4: library fork refuses gitea-reach-wider and deletes the minted token (64.826649ms)
|
||||
AssertionError [ERR_ASSERTION]: {"action":"grant.failed","method":"gitea","grantDecision":"676ebbc8-8397-4b63-90f4-3485fc277029","agent":"chief","code":"grant-admin-not-dedicated"}
|
||||
+ actual - expected
|
||||
|
||||
+ 'grant-admin-not-dedicated'
|
||||
- 'gitea-reach-wider'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:1394:12)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 'grant-admin-not-dedicated',
|
||||
expected: 'gitea-reach-wider',
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/bus/tests/gitea-grant.test.mjs:1416:1
|
||||
✖ C2-21 L10: project kind or library level changing after raise refuses decision-mismatch (64.895333ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
+ actual - expected
|
||||
|
||||
+ 'gitea-not-declared'
|
||||
- 'decision-mismatch'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:1430:12)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: true,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 'gitea-not-declared',
|
||||
expected: 'decision-mismatch',
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/bus/tests/gitea-grant.test.mjs:1435:1
|
||||
✖ C2-21 L4: creative write requires push true but ignores unrelated permission keys (64.965654ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
+ actual - expected
|
||||
|
||||
+ 'grant-admin-not-dedicated'
|
||||
- 'gitea-reach-wider'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:1444:23)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: true,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 'grant-admin-not-dedicated',
|
||||
expected: 'gitea-reach-wider',
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
✔ versionAtLeast compares numbers and refuses a prerelease of 1.27.0 (1.633441ms)
|
||||
✔ X-G1: a raise that fails a check refuses and stores nothing (77.604936ms)
|
||||
✔ X-G1: a creative role, a role without identity and a missing or due slot refuse at raise (240.549157ms)
|
||||
✔ X-G1: the chief of staff refuses at raise, whatever its definition, identity and publish declare (45.498213ms)
|
||||
✔ X-G2: admin set refuses each failed check and writes nothing, then writes the slot once (183.346403ms)
|
||||
✔ X-G3 to X-G8: an approve makes the user, mints the token, forks, checks reach and writes the file, record and grant.applied (112.792295ms)
|
||||
✔ X-G4: a user this stack did not make, an admin, a disabled user and a remade user each refuse (377.194723ms)
|
||||
✔ X-G6 and X-G8: the user and mint steps are written before their calls, and each result after its call (104.97076ms)
|
||||
✔ X-G4: a fork by the seat user of another repository than the project refuses fork-foreign (110.564422ms)
|
||||
✔ X-G5: a failure after the mint deletes the token, unloads nothing it did not load and leaves no file (196.260428ms)
|
||||
✔ X-G5: a token delete that fails is left at Gitea, shown by status and taken by sweep (133.845626ms)
|
||||
✔ X-G5: a mint that fails deletes by name, and a refusal at step 0 leaves the decision unspent (185.353007ms)
|
||||
✔ X-G5: an agent held for an unknown token is refused at step 0 with no Gitea call (65.526316ms)
|
||||
✔ X-G5 and X-G9 (step 7): a rotation whose old-token delete fails stays applied with the old id left (192.404202ms)
|
||||
✔ X-G10: in a rotation the store file holds the new token before the old one is deleted (174.549257ms)
|
||||
✔ X-G6: a rotation cut before the rename settles back at once, and the old token stays live (174.724588ms)
|
||||
✔ X-G6: a rotation whose step 8 write fails is settle-pending, and a restart settles it forward (207.75696ms)
|
||||
✔ X-G6: a restart settles an open rotation back when the file still holds the old token (152.596063ms)
|
||||
✔ X-G6: a restart settles an open rotation forward when the file holds the new token (139.158765ms)
|
||||
✔ X-G6: a 401 on the file token loses the grant: both tokens go, the record goes and memory unloads (144.64518ms)
|
||||
✔ X-G6: a 403, 404, 500, a hang and a dropped socket each leave the rotation settle-pending with nothing deleted (1100.789745ms)
|
||||
✔ X-G6: a file token no grant minted is held; the next restart says so again; a revoke clears it and takes both ids (181.734467ms)
|
||||
✔ N9: a revoke of an open rotation, then a restart: no settle, no Gitea call and no message (146.803459ms)
|
||||
✔ N9: a 404 on a delete by id counts as done (143.732923ms)
|
||||
✔ X-G6: a restart undoes each step a killed child left, and spends an approved unspent decision (469.337565ms)
|
||||
✔ X-G6: a restart with no admin slot leaves the user an orphan and the token left (63.327553ms)
|
||||
✔ X-G7: a change to the definition, identity, publish remotes or admin slot after the raise ends decision-mismatch (143.161323ms)
|
||||
✔ X-G7 and X-G9: an admin rotate voids open decisions, and rotate --finish deletes what was left (130.729706ms)
|
||||
✔ X-G9: a rotate whose name is taken refuses and writes nothing; remove takes the slot away (77.884977ms)
|
||||
✔ X-G9: a slot past its rotate-by date refuses a run at step 0, leaving the decision unspent (56.193013ms)
|
||||
✔ X-G11: run refuses every caller but the human, an unapproved, a spent and a busy decision (125.223336ms)
|
||||
✔ kill switch (plan :16): while halted the chief still reads grants.status, and an agent grants.revoke refuses (118.148549ms)
|
||||
✔ X-G11: a revoke while the executor runs aborts it and the grant ends revoked (132.457409ms)
|
||||
✔ X-G11: a revoke unloads memory and removes the file before its Gitea delete (134.269691ms)
|
||||
✔ X-G11: a revoke during a rotation step 7 ends it revoked, and step 8 writes no record back (205.166166ms)
|
||||
✔ X-G11: a revoke while a sweep settles a rotation, forward or back, ends it revoked with no record (340.037735ms)
|
||||
✔ X-G11: a revoke during a settle whoami that finds a foreign token ends it revoked, not held (196.238899ms)
|
||||
✔ X-G11: a revoke during a settle whoami that gets a 401 ends it revoked, not lost (189.34344ms)
|
||||
✔ X-G12: a token that reaches wider than its forks fails with the repository named (401.574274ms)
|
||||
✔ X-G12: reach is paged by X-Total-Count and a count that does not add up refuses (267.846613ms)
|
||||
✔ X-G13: an agent cannot record a grant event, and the trigger refuses one from any writer but the grant writer (44.259928ms)
|
||||
✔ X-G14: credentials delete fires the user: tokens go, login is prohibited, and grant.revoke.closed records it (284.587133ms)
|
||||
✔ X-G14: a fire whose token delete or login PATCH fails lands it in serviceLeft (151.958016ms)
|
||||
✔ X-G14: a fire whose user is gone or remade skips the fire steps and says why (334.678591ms)
|
||||
✔ X-G14: a sweep retries a fire only for the user it fired: a remade or gone user is skipped and named (366.243469ms)
|
||||
✔ X-G14: a revoke Gitea cannot reach stays open, and a restart closes it (134.383532ms)
|
||||
✔ row 281 T8: a tester grants.revoke refuses tester-dev-only once bus.tester is off; the human revoke keeps its records and starts no job (111.177819ms)
|
||||
✔ X-G15: look-alike commit steps outside the grant writer are ignored at start, by revoke and by status (132.34568ms)
|
||||
✔ X-G15: a look-alike spend under another kind with the grant writer pair is not an open grant (44.455018ms)
|
||||
✔ status lists the slot, users, grants and nothing secret, and every grants verb needs service gitea (94.729541ms)
|
||||
✔ C2-21 L1/L2/L9: research gets write on her repository with declared scopes through raise and yes (132.848006ms)
|
||||
✔ C2-21 L1/L2/L9: chief-of-staff gets read on her repository with declared scopes through raise and yes (108.726865ms)
|
||||
✔ C2-21 L1/L2/L9: coder gets read on her repository with declared scopes through raise and yes (105.566834ms)
|
||||
✔ C2-21 L7/L7b: research refuses non-library targets lib (43.04793ms)
|
||||
✔ C2-21 L7/L7b: research refuses non-library targets app,lib (43.226528ms)
|
||||
✔ C2-21 L7/L7b: chief-of-staff refuses non-library targets lib (40.740414ms)
|
||||
✔ C2-21 L7/L7b: chief-of-staff refuses non-library targets app,lib (41.612697ms)
|
||||
✔ C2-21 L4: read widened to write refuses gitea-reach-wider and deletes the minted token (106.28642ms)
|
||||
✔ C2-21 L4: admin privilege refuses gitea-reach-wider and deletes the minted token (112.291928ms)
|
||||
✔ C2-21 L4: missing pull refuses gitea-reach-wider and deletes the minted token (108.82183ms)
|
||||
✔ C2-21 L4: missing permission object refuses gitea-reach-wider and deletes the minted token (107.25133ms)
|
||||
✔ C2-21 L4: library fork refuses gitea-reach-wider and deletes the minted token (111.661727ms)
|
||||
✔ C2-21 L8: untargeted library collaborator reach is disclosed and accepted publish-wide (110.617198ms)
|
||||
✖ C2-21 L10: project kind or library level changing after raise refuses decision-mismatch (71.782699ms)
|
||||
✔ C2-21 L4: creative write requires push true but ignores unrelated permission keys (216.51561ms)
|
||||
✔ C2-21: a library grant with no declared scopes refuses gitea-not-declared at raise (45.980982ms)
|
||||
✔ C2-03b T6: the slot before any chief evidence, then the listed chief's live grant (33.812383ms)
|
||||
✔ C2-03b T7: a chief revoked through grants.revoke or fired through instance.fire refuses, and the fallback stays ended (388.895625ms)
|
||||
✔ C2-03b T7 choice checks: delisted, a fire under way and held each refuse; a rehire lists the chief again (38.05536ms)
|
||||
✔ C2-03b T8: a chief grant on another project ends the fallback, and two listed chiefs refuse library-fetch-no-grant (35.538194ms)
|
||||
✔ C2-03b departure (sage-2928): with no chief entry, any Gitea grant evidence refuses (16.650711ms)
|
||||
✔ C2-03b T9 runtime: the chief's store file and user, then a real revoke that runs no git with the slot at 0000 (899.871176ms)
|
||||
✔ C2-04: library mappings load without roots, remain frozen, and retain mapping refusals (12.126655ms)
|
||||
✔ C2-04: an otherwise valid publish grant cannot name a library repo (1.81521ms)
|
||||
✔ config directory and file path follow MOSAIC_CONFIG (0.250758ms)
|
||||
✔ the fixture business validates and comes back frozen (1.103053ms)
|
||||
✔ two instances may share a definition (1.017458ms)
|
||||
✔ top-level refusals (2.951099ms)
|
||||
✔ arbiters and projects (5.522959ms)
|
||||
✔ role instances (2.771534ms)
|
||||
✔ Vikunja bots (6.347953ms)
|
||||
✔ a role without Vikunja takes no tracker block (2.577895ms)
|
||||
✔ credential references match the definition's services (2.989169ms)
|
||||
✔ launch (9.573192ms)
|
||||
✔ REQ-THREAD-2 REQ-LAUNCH-1 launch.threadIdleSeconds: absent is the default, a positive integer is used, anything else refuses (5.256373ms)
|
||||
✔ loadBusiness: file checks (2.239389ms)
|
||||
✔ loadBusiness: not a regular file (46.809559ms)
|
||||
✔ loading writes nothing (1.257951ms)
|
||||
✔ names that are Object.prototype properties don't count as declared (3.811661ms)
|
||||
✔ the shipped example refuses as written and validates once filled in (0.970387ms)
|
||||
✔ persona vars: agent layer, creative definition only, both or neither (11.317037ms)
|
||||
✔ row 285: a chief or creative instance without credentials.gitea refuses at load (8.600878ms)
|
||||
✔ row 285: a creative instance with identity and publish loads (0.949694ms)
|
||||
✔ row 194 keep, noHire and retired validate, and a file without them loads as before (2.393095ms)
|
||||
✔ row 194 each bad keep, noHire or retired entry refuses (7.173244ms)
|
||||
✔ C2-21: only creative definitions and the chief with gitea may publish a library project (13.234306ms)
|
||||
✔ the library install writes the business file, personas and slot dir, makes the copy and starts nothing (319.004449ms)
|
||||
✔ a library re-run passes and leaves every file as it was (470.879755ms)
|
||||
✔ a re-run refuses business-file-differs and leaves the file unchanged (312.005718ms)
|
||||
✔ a re-run refuses persona-differs when an installed persona isn't the staged one, and writes nothing (335.01291ms)
|
||||
✔ the factory install writes no library mapping, persona or slot dir (201.940121ms)
|
||||
✔ the factory install refuses a library project and bus.tester off, before anything is written (83.824548ms)
|
||||
✔ the library install refuses bus.tester on and a persona var in the input (76.59824ms)
|
||||
✔ the install refuses root, a business id mismatch, another --home and a config dir with group bits (56.483195ms)
|
||||
✔ the library install needs --personas (5.954284ms)
|
||||
✔ a validate refusal removes the business file this run wrote (256.190647ms)
|
||||
✔ N4: a validate refusal on a re-run leaves the matching business file in place, byte for byte (437.854592ms)
|
||||
✔ a clean library host passes the precheck with no mirror yet (441.383612ms)
|
||||
✔ the library precheck refuses a persona file at 0644 (461.331192ms)
|
||||
✔ the library precheck refuses a persona whose digest isn't persona.sha256 (405.1089ms)
|
||||
✔ the library precheck refuses a persona dir with group bits (464.242283ms)
|
||||
✔ the library precheck refuses a business file at 0644 (457.841445ms)
|
||||
✔ the library precheck refuses a changes.jsonl at 0644 (635.778232ms)
|
||||
✔ the library precheck refuses a Discord binding (462.22964ms)
|
||||
✔ --library-status refuses library-mirror-missing before the bus has made the mirror (331.544735ms)
|
||||
✔ --library-status refuses library-never-synced on a fresh mirror (347.586128ms)
|
||||
✔ --library-status names a recorded lastError before the null lastFetch it leaves (N3) (356.091238ms)
|
||||
✔ --library-status prints each R-problem's path as an escaped JSON string, then the count, and passes (401.900674ms)
|
||||
✔ --library-status takes no --profile (5.777431ms)
|
||||
✔ Vikunja 614: install preserves the explicit notifier decision bytes and mode and creates none when absent (482.873578ms)
|
||||
✔ row 322 F8: library personas install from the copy with no clone node_modules (146.255507ms)
|
||||
✔ row 322 F9: importer refusal keeps the error line rather than the Node banner (89.465774ms)
|
||||
✔ C2-21: library install supplies chief identity and books publish for its real read grant (294.210478ms)
|
||||
ℹ tests 123
|
||||
ℹ suites 0
|
||||
ℹ pass 122
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 10963.095817
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/bus/tests/gitea-grant.test.mjs:1416:1
|
||||
✖ C2-21 L10: project kind or library level changing after raise refuses decision-mismatch (71.782699ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
|
||||
4 !== 0
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:1434:12)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: true,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 4,
|
||||
expected: 0,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
✔ versionAtLeast compares numbers and refuses a prerelease of 1.27.0 (1.280112ms)
|
||||
✔ X-G1: a raise that fails a check refuses and stores nothing (71.349417ms)
|
||||
✔ X-G1: a creative role, a role without identity and a missing or due slot refuse at raise (218.719606ms)
|
||||
✔ X-G1: the chief of staff refuses at raise, whatever its definition, identity and publish declare (45.959539ms)
|
||||
✔ X-G2: admin set refuses each failed check and writes nothing, then writes the slot once (166.537485ms)
|
||||
✔ X-G3 to X-G8: an approve makes the user, mints the token, forks, checks reach and writes the file, record and grant.applied (108.835383ms)
|
||||
✔ X-G4: a user this stack did not make, an admin, a disabled user and a remade user each refuse (368.565402ms)
|
||||
✔ X-G6 and X-G8: the user and mint steps are written before their calls, and each result after its call (109.068207ms)
|
||||
✔ X-G4: a fork by the seat user of another repository than the project refuses fork-foreign (103.652877ms)
|
||||
✔ X-G5: a failure after the mint deletes the token, unloads nothing it did not load and leaves no file (132.5244ms)
|
||||
✔ X-G5: a token delete that fails is left at Gitea, shown by status and taken by sweep (125.127842ms)
|
||||
✔ X-G5: a mint that fails deletes by name, and a refusal at step 0 leaves the decision unspent (147.844515ms)
|
||||
✔ X-G5: an agent held for an unknown token is refused at step 0 with no Gitea call (64.597545ms)
|
||||
✔ X-G5 and X-G9 (step 7): a rotation whose old-token delete fails stays applied with the old id left (190.16061ms)
|
||||
✔ X-G10: in a rotation the store file holds the new token before the old one is deleted (168.111001ms)
|
||||
✔ X-G6: a rotation cut before the rename settles back at once, and the old token stays live (172.49915ms)
|
||||
✔ X-G6: a rotation whose step 8 write fails is settle-pending, and a restart settles it forward (189.011204ms)
|
||||
✔ X-G6: a restart settles an open rotation back when the file still holds the old token (141.149461ms)
|
||||
✔ X-G6: a restart settles an open rotation forward when the file holds the new token (143.41922ms)
|
||||
✔ X-G6: a 401 on the file token loses the grant: both tokens go, the record goes and memory unloads (142.157997ms)
|
||||
✔ X-G6: a 403, 404, 500, a hang and a dropped socket each leave the rotation settle-pending with nothing deleted (1052.205948ms)
|
||||
✔ X-G6: a file token no grant minted is held; the next restart says so again; a revoke clears it and takes both ids (173.950313ms)
|
||||
✔ N9: a revoke of an open rotation, then a restart: no settle, no Gitea call and no message (139.936508ms)
|
||||
✔ N9: a 404 on a delete by id counts as done (135.846367ms)
|
||||
✔ X-G6: a restart undoes each step a killed child left, and spends an approved unspent decision (438.925152ms)
|
||||
✔ X-G6: a restart with no admin slot leaves the user an orphan and the token left (58.025191ms)
|
||||
✔ X-G7: a change to the definition, identity, publish remotes or admin slot after the raise ends decision-mismatch (128.865648ms)
|
||||
✔ X-G7 and X-G9: an admin rotate voids open decisions, and rotate --finish deletes what was left (122.928741ms)
|
||||
✔ X-G9: a rotate whose name is taken refuses and writes nothing; remove takes the slot away (67.477228ms)
|
||||
✔ X-G9: a slot past its rotate-by date refuses a run at step 0, leaving the decision unspent (51.433851ms)
|
||||
✔ X-G11: run refuses every caller but the human, an unapproved, a spent and a busy decision (106.410351ms)
|
||||
✔ kill switch (plan :16): while halted the chief still reads grants.status, and an agent grants.revoke refuses (109.111514ms)
|
||||
✔ X-G11: a revoke while the executor runs aborts it and the grant ends revoked (128.36085ms)
|
||||
✔ X-G11: a revoke unloads memory and removes the file before its Gitea delete (134.277671ms)
|
||||
✔ X-G11: a revoke during a rotation step 7 ends it revoked, and step 8 writes no record back (184.595189ms)
|
||||
✔ X-G11: a revoke while a sweep settles a rotation, forward or back, ends it revoked with no record (343.152025ms)
|
||||
✔ X-G11: a revoke during a settle whoami that finds a foreign token ends it revoked, not held (199.845732ms)
|
||||
✔ X-G11: a revoke during a settle whoami that gets a 401 ends it revoked, not lost (185.12583ms)
|
||||
✔ X-G12: a token that reaches wider than its forks fails with the repository named (399.290325ms)
|
||||
✔ X-G12: reach is paged by X-Total-Count and a count that does not add up refuses (314.011296ms)
|
||||
✔ X-G13: an agent cannot record a grant event, and the trigger refuses one from any writer but the grant writer (49.068987ms)
|
||||
✔ X-G14: credentials delete fires the user: tokens go, login is prohibited, and grant.revoke.closed records it (296.702448ms)
|
||||
✔ X-G14: a fire whose token delete or login PATCH fails lands it in serviceLeft (158.52798ms)
|
||||
✔ X-G14: a fire whose user is gone or remade skips the fire steps and says why (302.003039ms)
|
||||
✔ X-G14: a sweep retries a fire only for the user it fired: a remade or gone user is skipped and named (351.593821ms)
|
||||
✔ X-G14: a revoke Gitea cannot reach stays open, and a restart closes it (134.257555ms)
|
||||
✔ row 281 T8: a tester grants.revoke refuses tester-dev-only once bus.tester is off; the human revoke keeps its records and starts no job (109.730792ms)
|
||||
✔ X-G15: look-alike commit steps outside the grant writer are ignored at start, by revoke and by status (131.637028ms)
|
||||
✔ X-G15: a look-alike spend under another kind with the grant writer pair is not an open grant (44.820972ms)
|
||||
✔ status lists the slot, users, grants and nothing secret, and every grants verb needs service gitea (114.502261ms)
|
||||
✔ C2-21 L1/L2/L9: research gets write on her repository with declared scopes through raise and yes (127.306297ms)
|
||||
✔ C2-21 L1/L2/L9: chief-of-staff gets read on her repository with declared scopes through raise and yes (109.289905ms)
|
||||
✔ C2-21 L1/L2/L9: coder gets read on her repository with declared scopes through raise and yes (106.048934ms)
|
||||
✔ C2-21 L7/L7b: research refuses non-library targets lib (42.311032ms)
|
||||
✔ C2-21 L7/L7b: research refuses non-library targets app,lib (41.695354ms)
|
||||
✔ C2-21 L7/L7b: chief-of-staff refuses non-library targets lib (41.862836ms)
|
||||
✔ C2-21 L7/L7b: chief-of-staff refuses non-library targets app,lib (40.716511ms)
|
||||
✔ C2-21 L4: read widened to write refuses gitea-reach-wider and deletes the minted token (87.149976ms)
|
||||
✔ C2-21 L4: admin privilege refuses gitea-reach-wider and deletes the minted token (106.353171ms)
|
||||
✔ C2-21 L4: missing pull refuses gitea-reach-wider and deletes the minted token (108.373202ms)
|
||||
✔ C2-21 L4: missing permission object refuses gitea-reach-wider and deletes the minted token (109.736478ms)
|
||||
✔ C2-21 L4: library fork refuses gitea-reach-wider and deletes the minted token (107.72263ms)
|
||||
✔ C2-21 L8: untargeted library collaborator reach is disclosed and accepted publish-wide (113.114472ms)
|
||||
✔ C2-21 L10: project kind or library level changing after raise refuses decision-mismatch (144.543711ms)
|
||||
✔ C2-21 L4: creative write requires push true but ignores unrelated permission keys (214.033085ms)
|
||||
✔ C2-21: a library grant with no declared scopes refuses gitea-not-declared at raise (47.400615ms)
|
||||
✔ C2-03b T6: the slot before any chief evidence, then the listed chief's live grant (31.577774ms)
|
||||
✔ C2-03b T7: a chief revoked through grants.revoke or fired through instance.fire refuses, and the fallback stays ended (333.151597ms)
|
||||
✔ C2-03b T7 choice checks: delisted, a fire under way and held each refuse; a rehire lists the chief again (36.808837ms)
|
||||
✔ C2-03b T8: a chief grant on another project ends the fallback, and two listed chiefs refuse library-fetch-no-grant (35.425842ms)
|
||||
✔ C2-03b departure (sage-2928): with no chief entry, any Gitea grant evidence refuses (18.152711ms)
|
||||
✔ C2-03b T9 runtime: the chief's store file and user, then a real revoke that runs no git with the slot at 0000 (731.269457ms)
|
||||
✔ C2-04: library mappings load without roots, remain frozen, and retain mapping refusals (12.697745ms)
|
||||
✔ C2-04: an otherwise valid publish grant cannot name a library repo (1.895805ms)
|
||||
✔ config directory and file path follow MOSAIC_CONFIG (0.218002ms)
|
||||
✔ the fixture business validates and comes back frozen (1.113423ms)
|
||||
✔ two instances may share a definition (1.085918ms)
|
||||
✔ top-level refusals (3.201752ms)
|
||||
✔ arbiters and projects (4.428142ms)
|
||||
✔ role instances (2.586059ms)
|
||||
✔ Vikunja bots (6.556892ms)
|
||||
✔ a role without Vikunja takes no tracker block (3.259111ms)
|
||||
✔ credential references match the definition's services (2.945148ms)
|
||||
✔ launch (9.192439ms)
|
||||
✔ REQ-THREAD-2 REQ-LAUNCH-1 launch.threadIdleSeconds: absent is the default, a positive integer is used, anything else refuses (5.144938ms)
|
||||
✔ loadBusiness: file checks (1.735446ms)
|
||||
✔ loadBusiness: not a regular file (43.733004ms)
|
||||
✔ loading writes nothing (1.135021ms)
|
||||
✔ names that are Object.prototype properties don't count as declared (3.860107ms)
|
||||
✔ the shipped example refuses as written and validates once filled in (0.993456ms)
|
||||
✔ persona vars: agent layer, creative definition only, both or neither (11.635685ms)
|
||||
✔ row 285: a chief or creative instance without credentials.gitea refuses at load (7.937253ms)
|
||||
✔ row 285: a creative instance with identity and publish loads (0.868629ms)
|
||||
✔ row 194 keep, noHire and retired validate, and a file without them loads as before (1.843993ms)
|
||||
✔ row 194 each bad keep, noHire or retired entry refuses (6.103965ms)
|
||||
✔ C2-21: only creative definitions and the chief with gitea may publish a library project (11.142387ms)
|
||||
✔ the library install writes the business file, personas and slot dir, makes the copy and starts nothing (266.122171ms)
|
||||
✔ a library re-run passes and leaves every file as it was (419.422061ms)
|
||||
✔ a re-run refuses business-file-differs and leaves the file unchanged (294.559417ms)
|
||||
✔ a re-run refuses persona-differs when an installed persona isn't the staged one, and writes nothing (294.731302ms)
|
||||
✔ the factory install writes no library mapping, persona or slot dir (189.209583ms)
|
||||
✔ the factory install refuses a library project and bus.tester off, before anything is written (73.526324ms)
|
||||
✔ the library install refuses bus.tester on and a persona var in the input (75.290564ms)
|
||||
✔ the install refuses root, a business id mismatch, another --home and a config dir with group bits (53.415023ms)
|
||||
✔ the library install needs --personas (5.673132ms)
|
||||
✔ a validate refusal removes the business file this run wrote (237.469747ms)
|
||||
✔ N4: a validate refusal on a re-run leaves the matching business file in place, byte for byte (430.013288ms)
|
||||
✔ a clean library host passes the precheck with no mirror yet (417.064064ms)
|
||||
✔ the library precheck refuses a persona file at 0644 (430.044192ms)
|
||||
✔ the library precheck refuses a persona whose digest isn't persona.sha256 (395.659433ms)
|
||||
✔ the library precheck refuses a persona dir with group bits (420.475466ms)
|
||||
✔ the library precheck refuses a business file at 0644 (418.657123ms)
|
||||
✔ the library precheck refuses a changes.jsonl at 0644 (612.437015ms)
|
||||
✔ the library precheck refuses a Discord binding (405.124749ms)
|
||||
✔ --library-status refuses library-mirror-missing before the bus has made the mirror (329.538375ms)
|
||||
✔ --library-status refuses library-never-synced on a fresh mirror (341.974764ms)
|
||||
✔ --library-status names a recorded lastError before the null lastFetch it leaves (N3) (357.309304ms)
|
||||
✔ --library-status prints each R-problem's path as an escaped JSON string, then the count, and passes (394.067863ms)
|
||||
✔ --library-status takes no --profile (5.871472ms)
|
||||
✔ Vikunja 614: install preserves the explicit notifier decision bytes and mode and creates none when absent (536.196884ms)
|
||||
✔ row 322 F8: library personas install from the copy with no clone node_modules (164.278338ms)
|
||||
✔ row 322 F9: importer refusal keeps the error line rather than the Node banner (95.063494ms)
|
||||
✔ C2-21: library install supplies chief identity and books publish for its real read grant (261.537958ms)
|
||||
ℹ tests 123
|
||||
ℹ suites 0
|
||||
ℹ pass 123
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 10632.428445
|
||||
@@ -0,0 +1,39 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Row287 semantic mutants. Each restoration is byte-checked; timeout/error is never a kill."""
|
||||
from pathlib import Path
|
||||
import hashlib, os, re, subprocess, sys
|
||||
root=Path(__file__).resolve().parents[4]
|
||||
os.chdir(root)
|
||||
broker='packages/bus/src/broker.mjs'; grant='packages/bus/src/gitea-grant.mjs'; business='packages/business/src/business.mjs'
|
||||
bus_test='packages/bus/tests/gitea-grant.test.mjs'; biz_test='packages/business/tests/business.test.mjs'
|
||||
evidence=Path('agents/steelbeak/work/queue-287/evidence')
|
||||
mutants=[
|
||||
('M1',broker,"(CREATIVE_DEFINITIONS.includes(role.definition) || role.definition === STAFF_TO) && !libraryOnly", "role.definition === STAFF_TO && !libraryOnly",'C2-21 L7/L7b: research',bus_test),
|
||||
('M2',broker,"level: CREATIVE_DEFINITIONS.includes(role.definition) ? 'write' : 'read'", "level: 'write'",'C2-21 L1/L2/L9: chief-of-staff',bus_test),
|
||||
('M3',broker,"repo.permissions?.admin === false && repo.permissions?.push === (level === 'write') && repo.permissions?.pull === true", "repo.permissions?.pull === true",'C2-21 L4: read widened',bus_test),
|
||||
('M4',broker," if (t.library) {", " if (false && t.library) {",'C2-21 L1/L2/L9: research',bus_test),
|
||||
('M5',business,"projects[project].kind === 'library' && !CREATIVE_DEFINITIONS.includes(role.definition) && role.definition !== 'chief-of-staff'", "false",'C2-21: only creative',biz_test),
|
||||
('M6',broker,"api.tokenCreate(user, tokenName, scopes)", "api.tokenCreate(user, tokenName)",'C2-21 L1/L2/L9: chief-of-staff',bus_test),
|
||||
('M7',broker,"(CREATIVE_DEFINITIONS.includes(role.definition) || role.definition === STAFF_TO) && !libraryOnly", "CREATIVE_DEFINITIONS.includes(role.definition) && !libraryOnly",'C2-21 L7/L7b: chief-of-staff',bus_test),
|
||||
('M8',broker," role.publish.length === 0 ||\n (projects.some((p) => b.projects?.[p]?.kind === 'library') &&\n !definition.credentials.some((c) => c.service === 'gitea' && Array.isArray(c.scopes) && c.scopes.length > 0))", " role.publish.length === 0",'C2-21: a library grant with no declared scopes',bus_test),
|
||||
('M9',grant," if (r.json.length !== 0 || (r.total !== null && r.total !== 0)) deny('grant-admin-not-dedicated');", " // M9: omit dedicated-admin reach check.",'^X-G2:',bus_test),
|
||||
('M10',broker," // Step 1, one transaction: spend and recheck. scope-changed ends spent (D6);", " await api.userCreate({ username: 'early-write', email: '[email protected]', full_name: 'Early write', password: 'fx287-early-q7', must_change_password: false });\n // Step 1, one transaction: spend and recheck. scope-changed ends spent (D6);",'C2-21 L10:',bus_test),
|
||||
('M11',grant,"...(Object.keys(library).length ? { library } : {})", "...(false ? { library } : {})",'C2-21 L10:',bus_test),
|
||||
]
|
||||
results=[]
|
||||
for key,file,old,new,pattern,test in mutants:
|
||||
p=Path(file); original=p.read_bytes(); digest=hashlib.sha256(original).hexdigest(); source=original.decode()
|
||||
if source.count(old)!=1: raise RuntimeError(f'{key}: expected one mutation site, got {source.count(old)}')
|
||||
try:
|
||||
p.write_text(source.replace(old,new,1))
|
||||
out=subprocess.run(['timeout','-k','10','180','node','--test',f'--test-name-pattern={pattern}',test],stdout=subprocess.PIPE,stderr=subprocess.STDOUT,text=True,env=os.environ)
|
||||
(evidence/f'{key}.log').write_text(out.stdout)
|
||||
passes=re.search(r'^ℹ pass (\d+)$',out.stdout,re.M); failures=re.search(r'^ℹ fail (\d+)$',out.stdout,re.M); skips=re.search(r'^ℹ skipped (\d+)$',out.stdout,re.M)
|
||||
killed=out.returncode==1 and failures and int(failures[1])>0 and skips and int(skips[1])==0 and 'AssertionError' in out.stdout and not re.search(r'SyntaxError|ERR_MODULE_NOT_FOUND',out.stdout)
|
||||
print(f'{key}: rc={out.returncode} pass={passes[1] if passes else "?"} fail={failures[1] if failures else "?"} skip={skips[1] if skips else "?"} {"KILLED" if killed else "INVALID/SURVIVED"}',flush=True)
|
||||
results.append(f'{key} {"KILLED" if killed else "INVALID/SURVIVED"} rc={out.returncode}')
|
||||
if not killed: sys.exit(1)
|
||||
finally:
|
||||
p.write_bytes(original)
|
||||
if hashlib.sha256(p.read_bytes()).hexdigest()!=digest: raise RuntimeError(f'{key}: restoration mismatch')
|
||||
(evidence/'mutants-summary.txt').write_text('\n'.join(results)+'\n')
|
||||
@@ -0,0 +1,56 @@
|
||||
69f91b4989a39bf09201a7b328f6c38e833e9897e310f026ce893d4caa2ec214 BUILD-LOG.md
|
||||
d64255c7493e10e1d82949c2335e4fce47ad5f4130901941fdd091e224b9c2c8 agents/steelbeak/work/queue-287/BUILD.md
|
||||
fb8615d02a3cdb29e96434bf97e3d6b26f447e9fa05cf26bf97c26f6c659b1c8 agents/steelbeak/work/queue-287/evidence/M1.log
|
||||
df427effd5b4ce587229ccf258816b3a65fe93f25a1e3706f8f1bef9223dcc42 agents/steelbeak/work/queue-287/evidence/M10.log
|
||||
777dd9500f451d9fbca215816efd5e1f94ba803ef1649a724b18ea3ec65b4d10 agents/steelbeak/work/queue-287/evidence/M11.log
|
||||
08e3628a711afed00d475cb9f9b1ee2dffd3a480d8a78e91478783aaea55b8f3 agents/steelbeak/work/queue-287/evidence/M2.log
|
||||
8d958f9bd3791d2e98392924ae93f335559ea2e35b72763be0847f72fc7539a9 agents/steelbeak/work/queue-287/evidence/M3.log
|
||||
8a82af3e7b46206fbeb1eeb4d6976fe34be703de5fc3b09328b51d806a13fd53 agents/steelbeak/work/queue-287/evidence/M4.log
|
||||
27841d51d2aa04fc8b23a2cdb0f481ef0c1b6a9ba689dc84d3fbf212276aa43a agents/steelbeak/work/queue-287/evidence/M5.log
|
||||
422a05a364404dd89d0730605f637552dc7d95058e785cdb8b2916690021cbe3 agents/steelbeak/work/queue-287/evidence/M6.log
|
||||
cdb11e0c732c8ee0aa6c4c7167b499fabb079a1fb3c0f64e09776efe1ce94ec9 agents/steelbeak/work/queue-287/evidence/M7.log
|
||||
8c3d35bbdec28394133f17ce427122dcbb3a00859122a2ba3e220698b08c7ac6 agents/steelbeak/work/queue-287/evidence/M8.log
|
||||
f697d8606f9a879b7afc16aedc722bf89cd56fb79d1947933b40354bbdcbc3cb agents/steelbeak/work/queue-287/evidence/M9.log
|
||||
015595d7539fa0a3fdd8993ca5f6bb37eb3a4e90d21de9902490b68ce65790c6 agents/steelbeak/work/queue-287/evidence/bus-suite.log
|
||||
1186834379a25300c4e7657dd560194346bfd960206d716d3ec80d083b874ac7 agents/steelbeak/work/queue-287/evidence/business-suite.log
|
||||
fb407cecad775daeb964996f5286504478e78abb2639dfddad901da72a8299da agents/steelbeak/work/queue-287/evidence/cleanup-audit.txt
|
||||
1b5994ef1fbceb0d85b7dc27b8c80c38763e9443d255f5e0ae8768a52cc49a6f agents/steelbeak/work/queue-287/evidence/cli-suite.log
|
||||
b9df0f83ac9cdcbd9904d48e91c2c9b29704b6b1ed1d86bd5da8208d8ae6e5c5 agents/steelbeak/work/queue-287/evidence/docker-facts-r2.log
|
||||
d0a408745b860ddcf0415f1eaa56f733117cf01eb911e89acc398d48ac6d3a12 agents/steelbeak/work/queue-287/evidence/docker-facts.log
|
||||
e214557bb114e1ddcfc9fe1779664c5775e0006e1531beb42432cf994a6ee4dd agents/steelbeak/work/queue-287/evidence/docker-whole.log
|
||||
67bb508ab388f24adbfb519edc22fe5438e80796e90f7c22e858a1360dca3682 agents/steelbeak/work/queue-287/evidence/install-whole.log
|
||||
354a30815e25b949cba560d44dd373e7bd10a2151a7c8809a4de9eb64bbfede4 agents/steelbeak/work/queue-287/evidence/mutants-run.log
|
||||
22d7df0a069ff63ebad0ae6d7d7d21f25727a85f31c5811b05d37d975c08bce9 agents/steelbeak/work/queue-287/evidence/mutants-summary.txt
|
||||
5b0e61c389a337fb507f73254d0a60141ef36a8b0abd4f41e0dd20f81f1df247 agents/steelbeak/work/queue-287/evidence/red-new.log
|
||||
c840276c920023dce945bcfeb3afd3b111d6abf6dcfb0ae65daa3fccbaa720ce agents/steelbeak/work/queue-287/evidence/red-replacement.log
|
||||
9b5f6010e9f7c56834371b673710a95eb3bdbb256d77cc3bc8ab160c3301b40e agents/steelbeak/work/queue-287/evidence/shell-resume-summary.txt
|
||||
c17fca8ffdf905a467e3a3f201876cc96719e691ff2b94c0366ecb54e8e3286f agents/steelbeak/work/queue-287/evidence/shell-summary.txt
|
||||
f91fc95a173339137f981e24fbf630acfde957e32c3cd71e28304532083c6a77 agents/steelbeak/work/queue-287/evidence/test-auth.log
|
||||
d681bd46560d9f359ccd70fdb2694e8b2a4457fe5cc70796e3044447c3fb9562 agents/steelbeak/work/queue-287/evidence/test-conductor.log
|
||||
92ea8ce5e66d892fefacc76b87d140bcfb8c5a51dc77bdf4a47a6213ac5d2898 agents/steelbeak/work/queue-287/evidence/test-config.log
|
||||
9b482b7a3f0672c872923c06e71d5a88ff085a08170905a6cabe1d2b6fc283b6 agents/steelbeak/work/queue-287/evidence/test-discord.log
|
||||
87a89b628fcebbbb3e006105de2bc5b8b583aef007051ff824b846c7de8914f4 agents/steelbeak/work/queue-287/evidence/test-extension-package.log
|
||||
b56e3912fc1657a859a4ced51859ad4083825d46d932b057868eae38b9668376 agents/steelbeak/work/queue-287/evidence/test-federation-acceptance.log
|
||||
83dfaa21c4e4ad941088efab3f69e87ebd2f6bd3cc72ede938e21dc06da8a34f agents/steelbeak/work/queue-287/evidence/test-foundation.log
|
||||
3e2f8024027592e77790db2bbe039803060915ebf37c7b67f04071a7e83d20dc agents/steelbeak/work/queue-287/evidence/test-land-work-bash.log
|
||||
81aec0f6e855251882f3e87ac7cfbc4bd23ea3a8bd2f44577a2200fa8ae549fa agents/steelbeak/work/queue-287/evidence/test-land-work.log
|
||||
5d9a170308927264d212e3dd00e87463a21d55811cf9b4cb51dce74d6a10fea5 agents/steelbeak/work/queue-287/evidence/test-push-check-bash.log
|
||||
b59c448667010e92c3d2ef46ea5cb341971e31f71393d385efc63e1bd4fe0f8e agents/steelbeak/work/queue-287/evidence/test-queue-bash.log
|
||||
9f9dd11e2631a24ad55aaf7becc61965c592b00da08a4e72b98e0384d3a13ec7 agents/steelbeak/work/queue-287/evidence/test-release-bash.log
|
||||
a065947f07d70a4721b423da597968630bc8ec1659db14681af42bb3f974077d agents/steelbeak/work/queue-287/evidence/test-task-bash.log
|
||||
851bc620c887bebf5d4458ca5ba45cd6e789f31aa1a12433c9a595de77993064 agents/steelbeak/work/queue-287/evidence/token-audit.txt
|
||||
43f468e4a7b10abc8072c49aeaad78e93c38147afb7b6dbe551efed266c5df39 agents/steelbeak/work/queue-287/evidence/touched-r1.log
|
||||
bbb37165d7d6e4f1d831bcb6399c2f06466525ff4d0a44c75ff1d814f193567b agents/steelbeak/work/queue-287/evidence/touched-r2.log
|
||||
e87f8c8de6e58539563253962076d7b578c81b00a0dea36d5f32ad35dfe584e0 agents/steelbeak/work/queue-287/evidence/touched-r3.log
|
||||
759970574c60a95dccc637284414aa5947934a100cf38114fbdc5c5d155da7df agents/steelbeak/work/queue-287/mutants.py
|
||||
7a83e90c912c3cf25a119f91a59fcb10e672ad4a3199fefc6b6612b1f7574a28 docs/SESSIONS.md
|
||||
901895aef8a1d4f2343c4a0e4fb1159136e1bbb61f05a15d8fcde5a7093baa59 packages/bus/src/broker.mjs
|
||||
27d7d5322c2f12641e350867d3ac6f838b33832951e69f057e3a7a28b8ac4d5b packages/bus/src/gitea-grant.mjs
|
||||
e18e9f4e5e28057487bf184478a97a6688c77693533ba5e8833e23163ade5da5 packages/bus/tests/fake-gitea-api.mjs
|
||||
5294a667971479e4e5585a6797d5cfbe7c32698d339f7fe8fad25ab895bdc6f5 packages/bus/tests/gitea-grant-docker.test.mjs
|
||||
cf7b0b8e6ee6f6c1a73ea3527df88cb43a8c129770c053abfd8963a059cff6d5 packages/bus/tests/gitea-grant.test.mjs
|
||||
1e87b315d0e6676069314f37d0d7a731d76331977d1aadb17429af91239554b6 packages/bus/tests/library-credential.test.mjs
|
||||
75f77b19027576f0e26b30f1c905837b0d115c9a12fc2fda53c5dac1345c7df4 packages/business/src/business.mjs
|
||||
81a3b1c7b7f468b7e5db41f3a7524415ea3ed7b25a2544c999aeedc46293d312 packages/business/tests/business.test.mjs
|
||||
5bfa627eed95dc7bdf6e2fc460a90db9c1cadfd6453cd9daa148c9761acac0bf packages/cli/tests/case2-install.test.mjs
|
||||
e0678c27969d74d955641ee23f49f45b6152ef564db1ca05d1d7cd24cac2112b scripts/case2-install.sh
|
||||
@@ -1667,3 +1667,7 @@ are never rewritten or removed; corrections are new entries.
|
||||
|
||||
- 2026-10-24 — rockerduck (a403aa78-b550-4bab-a613-7ea3fd75ec79), row286 / #1791 r6 under sage-4112: two fixture-value replacements only, exact secrets grep empty, touched6/6+24/24 and whole151/151+981/981, zero output fixture matches; no assertion/production/guard change, manifest prepared for round3.
|
||||
2026-10-24T14:37:02Z | Sage (1ef1e4f8) | Row 286 / #1791 landed (mos-1576, sage-4100, sage-4112) | Boyd and Dewey approved r5 and r6; first push STOP secrets on two fixture values, r6 shortened them; gate GREEN 14:36:42Z (4251 tests); pushed 5d7801dc1; #1791 c31591 closed; case2 brief re-pinned for 225/229/284/287-290 (revs 3930-3936); brief template sage-4112 line
|
||||
- 2026-10-24 — steelbeak — row 287 C2-21 (#1792), steelbeak/row287 at 18334d713: recovered inherited design and current code; canonical start initially refused assign lock; tests held pending Sage CLEAR, implementation not yet started.
|
||||
- 2026-10-24 — steelbeak — row 287 C2-21 (#1792): canonical start steelbeak-287-start succeeded at queue rev 3955; inherited design and sage-4125 recorded, local root/webui npm ci passed, test drafts prepared, production untouched and all tests held pending Sage CLEAR.
|
||||
- 2026-10-24 — steelbeak — row 287 C2-21 (#1792), builder complete under sage-4130: collaborator library grants and chief real read mint built; bus 998/business 283/CLI 989+FED1/Docker2/install27/all12 shell gates green, M1-M11 killed/restored, token/cleanup audits clean; preparing steelbeak/row287 push and manifest handoff to Boyd then Dewey, no live change or landing.
|
||||
- 2026-10-24 — steelbeak — row 287 first-review follow-up: Boyd approved c2338ed58/3683b40... with two doc-only corrections; append-only BUILD correction distinguishes clean product/test diff from raw-log whitespace and records old-test numstats. Queue round1 open; missing-seat-credential request failure reported to Sage. Frozen product and manifest preserved; no tests or slot use.
|
||||
+55
-24
@@ -2756,22 +2756,24 @@ export class Broker {
|
||||
: `\nThis makes a new key for ${g.agent} on ${g.host}.`);
|
||||
return { body, block };
|
||||
}
|
||||
// Row 236 (#1742, #436b r5 section 2): the agent's role and definition, if the agent is a role of the
|
||||
// business whose definition declares gitea and which has identity and a publish grant (199 D8). A
|
||||
// creative role gets no grant, whatever its definition declares (mos-1163). Row 285 (#1790, sage-2921):
|
||||
// nor does the chief of staff, whose definition declares gitea for a read grant only, since this path
|
||||
// mints SEAT_SCOPES, write:repository included.
|
||||
#giteaRole(business, agent) {
|
||||
// Row 236: identity, publish and a Gitea declaration are required. mos-1163 keeps creative
|
||||
// roles out of non-library grants and every host grant. mos-1320 item 2 and sage-4125 allow
|
||||
// creative/chief targets only when all are library projects. sage-2921 refused the chief because
|
||||
// SEAT_SCOPES included write:repository; library grants now use its definition's read scopes.
|
||||
#giteaRole(business, agent, projects) {
|
||||
const b = this.#business(business);
|
||||
if (!Object.hasOwn(b.roles, agent)) fail('gitea-not-declared');
|
||||
const role = b.roles[agent];
|
||||
if (CREATIVE_DEFINITIONS.includes(role.definition) || role.definition === STAFF_TO) fail('gitea-not-declared');
|
||||
const libraryOnly = projects.every((p) => b.projects?.[p]?.kind === 'library');
|
||||
if ((CREATIVE_DEFINITIONS.includes(role.definition) || role.definition === STAFF_TO) && !libraryOnly) fail('gitea-not-declared');
|
||||
const definition = Object.hasOwn(b.definitions ?? {}, role.definition) ? b.definitions[role.definition] : null;
|
||||
if (
|
||||
!definition?.credentials?.some((c) => c.service === 'gitea') ||
|
||||
!role.identity?.gitea ||
|
||||
!Array.isArray(role.publish) ||
|
||||
role.publish.length === 0
|
||||
role.publish.length === 0 ||
|
||||
(projects.some((p) => b.projects?.[p]?.kind === 'library') &&
|
||||
!definition.credentials.some((c) => c.service === 'gitea' && Array.isArray(c.scopes) && c.scopes.length > 0))
|
||||
)
|
||||
fail('gitea-not-declared');
|
||||
return { b, role, definition };
|
||||
@@ -2784,6 +2786,8 @@ export class Broker {
|
||||
if (!role.publish.includes(project) || !upstream) fail('remote-not-granted');
|
||||
const problem = forkProblem(upstream, role.identity);
|
||||
if (problem) fail(problem);
|
||||
if (b.projects[project].kind === 'library')
|
||||
return { project, upstream, library: true, repository: upstream, level: CREATIVE_DEFINITIONS.includes(role.definition) ? 'write' : 'read' };
|
||||
return { project, upstream, fork: forkUrl(upstream, role.identity.gitea.user) };
|
||||
});
|
||||
}
|
||||
@@ -2797,7 +2801,7 @@ export class Broker {
|
||||
// Section 2: the store checks and the raise binding. The caller owns the transaction. The chief
|
||||
// can't shape the block: every line comes from the business, the slot and the store.
|
||||
#giteaGrantBinding(business, g) {
|
||||
const { b, role, definition } = this.#giteaRole(business, g.agent);
|
||||
const { b, role, definition } = this.#giteaRole(business, g.agent, g.target.projects);
|
||||
this.#grantableAgent(business, g.agent);
|
||||
const targets = this.#giteaTargets(b, role, g.target.projects);
|
||||
const meta = this.#giteaAdminMeta(business, role.identity.gitea.url);
|
||||
@@ -2815,23 +2819,31 @@ export class Broker {
|
||||
identitySha256: this.#giteaGrant.identitySha256(role, b.projects),
|
||||
adminSha256: this.#giteaGrant.adminSha256(meta),
|
||||
};
|
||||
const others = role.publish.filter((p) => !g.target.projects.includes(p)).map((p) => b.projects?.[p]?.remote ?? p);
|
||||
const others = role.publish.filter((p) => !g.target.projects.includes(p));
|
||||
const forkOthers = others.filter((p) => b.projects?.[p]?.kind !== 'library').map((p) => b.projects?.[p]?.remote ?? p);
|
||||
const libraryOthers = others.filter((p) => b.projects?.[p]?.kind === 'library');
|
||||
const forks = targets.filter((t) => !t.library), repositories = targets.filter((t) => t.library);
|
||||
const level = CREATIVE_DEFINITIONS.includes(role.definition) ? 'write' : 'read';
|
||||
const scopes = repositories.length ? definition.credentials.find((c) => c.service === 'gitea').scopes : ['read:user', 'write:repository'];
|
||||
const { gitea, email } = role.identity;
|
||||
const block =
|
||||
`\n\nGrant ${g.agent} a Gitea token: user ${gitea.user}, email ${email}, url ${gitea.url}.` +
|
||||
`\nForks:` +
|
||||
targets.map((t) => `\n${t.fork}`).join('') +
|
||||
`\nScopes: ${['read:user', 'write:repository'].join(', ')}.` +
|
||||
(forks.length ? `\nForks:` + forks.map((t) => `\n${t.fork}`).join('') : '') +
|
||||
repositories.map((t) => `\nRepository: ${t.repository}, level ${t.level}.` + (t.level === 'write'
|
||||
? `\nThe token can write every path of this repository. Only the bus's path check limits it to supporting folders.`
|
||||
: `\nThe token can read every path of this repository. It can't push to it, because the user is a read collaborator.`)).join('') +
|
||||
`\nScopes: ${scopes.join(', ')}.` +
|
||||
`\nRotate by ${g.rotateBy}.` +
|
||||
`\nKind: ${kind}.` +
|
||||
`\nThe token can write every repository this user can write. Step 5 checks that this is only its forks.` +
|
||||
(others.length ? `\nIt may also reach this user's existing forks of:` + others.map((u) => `\n${u}`).join('') : '');
|
||||
`\nThe token can write every repository this user can write. Step 5 checks that this is only its forks${repositories.length ? ' and the repositories above at their level' : ''}.` +
|
||||
(forkOthers.length ? `\nIt may also reach this user's existing forks of:` + forkOthers.map((u) => `\n${u}`).join('') : '') +
|
||||
(libraryOthers.length ? `\nIt may also reach these repositories as a collaborator: ` + libraryOthers.map((p) => `${b.projects[p].remote} (${level})`).join(', ') : '');
|
||||
return { body, block };
|
||||
}
|
||||
// Section 4 step 1's checks after the spend. Throws the code that ends the grant.
|
||||
#giteaRecheck(business, decision, g) {
|
||||
this.#scopeRecheck(business, decision, g);
|
||||
const { b, role, definition } = this.#giteaRole(business, g.agent);
|
||||
const { b, role, definition } = this.#giteaRole(business, g.agent, g.target.projects);
|
||||
if (this.#giteaGrant.definitionSha256(definition) !== g.definitionSha256) fail('decision-mismatch');
|
||||
if (this.#giteaGrant.identitySha256(role, b.projects) !== g.identitySha256) fail('decision-mismatch');
|
||||
const meta = this.#giteaAdminMeta(business, role.identity.gitea.url);
|
||||
@@ -3061,7 +3073,10 @@ export class Broker {
|
||||
tokenName = this.#giteaGrant.tokenName(business, agent, decision);
|
||||
step('mint', { tokenName });
|
||||
minting = true;
|
||||
const minted = await api.tokenCreate(user, tokenName);
|
||||
const scopes = targets.some((t) => t.library)
|
||||
? b.definitions[role.definition].credentials.find((c) => c.service === 'gitea').scopes
|
||||
: undefined;
|
||||
const minted = await api.tokenCreate(user, tokenName, scopes);
|
||||
if (minted.status !== 201 || !giteaId(minted.json?.id) || typeof minted.json?.sha1 !== 'string') fail('gitea-failed');
|
||||
tokenId = minted.json.id;
|
||||
const token = this.#giteaGrant.checkToken(minted.json.sha1);
|
||||
@@ -3069,9 +3084,16 @@ export class Broker {
|
||||
stop();
|
||||
// Step 4.
|
||||
const seat = this.#giteaGrant.seat(meta.url, token, log);
|
||||
const forks = [];
|
||||
const forks = [], repositories = [];
|
||||
for (const t of targets) {
|
||||
const { owner, repo } = parseGiteaUrl(t.upstream, { repo: true });
|
||||
if (t.library) {
|
||||
const added = await api.collaboratorAdd(owner, repo, user, t.level);
|
||||
stop();
|
||||
if (added.status !== 204) fail('gitea-failed');
|
||||
repositories.push({ project: t.project, repository: t.repository, level: t.level });
|
||||
continue;
|
||||
}
|
||||
const f = await seat.forkGet(user, repo);
|
||||
stop();
|
||||
if (f.status === 200) {
|
||||
@@ -3088,10 +3110,14 @@ export class Broker {
|
||||
stop();
|
||||
if (me.status !== 200) fail('gitea-reach-unknown');
|
||||
if (lower(me.json?.login) !== lower(user) || me.json?.id !== userId) fail('gitea-user-foreign');
|
||||
const upstreams = new Set();
|
||||
const upstreams = new Set(), libraryReach = new Map();
|
||||
for (const p of role.publish) {
|
||||
const u = parseGiteaUrl(b.projects?.[p]?.remote, { repo: true });
|
||||
if (u.url) upstreams.add(lower(`${u.owner}/${u.repo}`));
|
||||
if (u.url) {
|
||||
const name = lower(`${u.owner}/${u.repo}`);
|
||||
if (b.projects[p].kind === 'library') libraryReach.set(name, CREATIVE_DEFINITIONS.includes(role.definition) ? 'write' : 'read');
|
||||
else upstreams.add(name);
|
||||
}
|
||||
}
|
||||
let seen = 0,
|
||||
total = null,
|
||||
@@ -3105,8 +3131,11 @@ export class Broker {
|
||||
seen += r.json.length;
|
||||
for (const repo of r.json) {
|
||||
const parent = typeof repo?.parent?.full_name === 'string' ? lower(repo.parent.full_name) : null;
|
||||
if (wider === null && (lower(repo?.owner?.login) !== lower(user) || !parent || !upstreams.has(parent)))
|
||||
wider = typeof repo?.full_name === 'string' ? repo.full_name : 'unknown';
|
||||
const level = libraryReach.get(lower(repo?.full_name));
|
||||
const allowed = level
|
||||
? repo.permissions?.admin === false && repo.permissions?.push === (level === 'write') && repo.permissions?.pull === true
|
||||
: lower(repo?.owner?.login) === lower(user) && parent && upstreams.has(parent);
|
||||
if (wider === null && !allowed) wider = typeof repo?.full_name === 'string' ? repo.full_name : 'unknown';
|
||||
}
|
||||
if (r.json.length < this.#giteaGrant.repoPage) break;
|
||||
}
|
||||
@@ -3122,11 +3151,13 @@ export class Broker {
|
||||
step('commit', { previousTokenId: previous?.tokenId ?? null, tokenId, tokenName, previous });
|
||||
stop();
|
||||
const applied = (extra = {}) => {
|
||||
this.#grantEvent(business, { action: 'grant.applied', method: 'gitea', grantDecision: decision, agent, user, userId, tokenId, forks, ...extra }, agent);
|
||||
this.#grantEvent(business, { action: 'grant.applied', method: 'gitea', grantDecision: decision, agent, user, userId, tokenId, forks, ...(repositories.length ? { repositories } : {}), ...extra }, agent);
|
||||
this.#grantNotice(
|
||||
business,
|
||||
decision,
|
||||
`${agent} has a Gitea token as ${user} on ${meta.url} (decision ${decision}), forks: ${forks.join(', ')}.` +
|
||||
`${agent} has a Gitea token as ${user} on ${meta.url} (decision ${decision})` +
|
||||
(forks.length ? `, forks: ${forks.join(', ')}` : '') +
|
||||
(repositories.length ? `, repositories: ${repositories.map((r) => `${r.repository} (${r.level})`).join(', ')}` : '') + '.' +
|
||||
(extra.serviceLeft?.length ? ` The previous token ${extra.serviceLeft.join(', ')} is still live. Run: mosaic grants sweep gitea` : ''),
|
||||
);
|
||||
};
|
||||
|
||||
@@ -18,7 +18,7 @@ import { BusError, GRANT_EVIDENCE } from './broker.mjs';
|
||||
import { day, privateDir } from './credentials.mjs';
|
||||
import { definitionSha256, grantTag } from './host-grant.mjs';
|
||||
import { canonicalJson } from '../../business/src/util.mjs';
|
||||
import { forkUrl, giteaName, ID_PATTERN, parseGiteaUrl } from '../../business/src/vocabulary.mjs';
|
||||
import { forkUrl, giteaName, ID_PATTERN, parseGiteaUrl, CREATIVE_DEFINITIONS } from '../../business/src/vocabulary.mjs';
|
||||
|
||||
const deny = (code) => {
|
||||
throw new BusError(code);
|
||||
@@ -72,12 +72,17 @@ export function giteaGrantCheck(grant, today) {
|
||||
|
||||
// Section 2: over the agent's identity, its publish list and the remote of every project in it, since
|
||||
// step 5 checks against all of them. `projects` is the business's project map.
|
||||
export const identitySha256 = (role, projects) =>
|
||||
sha256({
|
||||
export const identitySha256 = (role, projects) => {
|
||||
const library = Object.fromEntries((role.publish ?? []).filter((p) => projects?.[p]?.kind === 'library')
|
||||
.map((p) => [p, CREATIVE_DEFINITIONS.includes(role.definition) ? 'write' : 'read']));
|
||||
return sha256({
|
||||
identity: role.identity ?? null,
|
||||
publish: role.publish ?? [],
|
||||
remotes: Object.fromEntries((role.publish ?? []).map((p) => [p, projects?.[p]?.remote ?? null])),
|
||||
// C2-21: bind kind and level, preserving every legacy non-library binding byte for byte.
|
||||
...(Object.keys(library).length ? { library } : {}),
|
||||
});
|
||||
};
|
||||
// Section 2: over the admin slot's metadata, never the token.
|
||||
export const adminSha256 = (meta) => sha256({ url: meta.url, account: meta.account, setAt: meta.setAt });
|
||||
// Step 3: `mosaic-<tag>-<decision>`. grantTag already starts with "mosaic-".
|
||||
@@ -263,6 +268,8 @@ export function giteaGrant({ configDir, ca = undefined, timeoutMs = CALL_MS, tra
|
||||
tokenCreate: (user, name, scopes = SEAT_SCOPES, op = 'token-create') =>
|
||||
go(op, 'POST', `/api/v1/users/${segment(user)}/tokens`, basic, { name, scopes: [...scopes] }),
|
||||
tokenDelete: (user, idOrName) => go('token-delete', 'DELETE', `/api/v1/users/${segment(user)}/tokens/${segment(idOrName)}`, basic),
|
||||
collaboratorAdd: (owner, repo, user, permission) =>
|
||||
go('collaborator-add', 'PUT', `/api/v1/repos/${segment(owner)}/${segment(repo)}/collaborators/${segment(user)}`, { token }, { permission }),
|
||||
};
|
||||
};
|
||||
return {
|
||||
|
||||
@@ -22,9 +22,10 @@ export async function fakeGiteaApi(t, dir, { version = '1.27.1', admin = 'mosaic
|
||||
const state = { version };
|
||||
const lower = (x) => String(x).toLowerCase();
|
||||
const userJson = (u) => ({ id: u.id, login: u.login, email: u.email, full_name: u.full_name, is_admin: u.is_admin, prohibit_login: u.prohibit_login });
|
||||
const repoJson = (r) => ({
|
||||
const repoJson = (r, user = null) => ({
|
||||
id: r.id, name: r.name, full_name: `${r.owner}/${r.name}`, owner: { login: r.owner }, fork: Boolean(r.parent),
|
||||
parent: r.parent ? { full_name: r.parent } : null,
|
||||
permissions: { admin: lower(user) === lower(r.owner), push: lower(user) === lower(r.owner) || (r.permissions[lower(user)] ?? 'write') === 'write', pull: true },
|
||||
});
|
||||
const api = {
|
||||
url: null,
|
||||
@@ -52,7 +53,7 @@ export async function fakeGiteaApi(t, dir, { version = '1.27.1', admin = 'mosaic
|
||||
users.delete(lower(login));
|
||||
},
|
||||
addRepo(owner, name, { parent = null, collaborators = [] } = {}) {
|
||||
const r = { id: nextId++, owner, name, parent, collaborators: collaborators.map(lower) };
|
||||
const r = { id: nextId++, owner, name, parent, collaborators: collaborators.map(lower), permissions: {} };
|
||||
repos.push(r);
|
||||
return r;
|
||||
},
|
||||
@@ -100,7 +101,7 @@ export async function fakeGiteaApi(t, dir, { version = '1.27.1', admin = 'mosaic
|
||||
const all = reachable(me);
|
||||
const limit = Number(query.get('limit') ?? 30);
|
||||
const page = Number(query.get('page') ?? 1);
|
||||
return [200, all.slice((page - 1) * limit, page * limit).map(repoJson), all.length];
|
||||
return [200, all.slice((page - 1) * limit, page * limit).map((r) => repoJson(r, me.login)), all.length];
|
||||
}
|
||||
if (p[0] === 'users' && p.length === 2 && method === 'GET') {
|
||||
const u = users.get(lower(p[1]));
|
||||
@@ -144,6 +145,15 @@ export async function fakeGiteaApi(t, dir, { version = '1.27.1', admin = 'mosaic
|
||||
return [204, null];
|
||||
}
|
||||
}
|
||||
if (p[0] === 'repos' && p.length === 5 && p[3] === 'collaborators' && method === 'PUT') {
|
||||
if (auth.kind !== 'token' || !me.is_admin || !who.token.scopes.includes('write:repository')) return [403, { message: 'forbidden' }];
|
||||
const r = repos.find((x) => lower(x.owner) === lower(p[1]) && lower(x.name) === lower(p[2]));
|
||||
if (!r || !users.has(lower(p[4]))) return [404, { message: 'not found' }];
|
||||
if (!['read', 'write'].includes(body.permission)) return [422, { message: 'invalid permission' }];
|
||||
if (!r.collaborators.includes(lower(p[4]))) r.collaborators.push(lower(p[4]));
|
||||
r.permissions[lower(p[4])] = body.permission;
|
||||
return [204, null];
|
||||
}
|
||||
if (p[0] === 'repos' && p.length === 3 && method === 'GET') {
|
||||
const r = repos.find((x) => lower(x.owner) === lower(p[1]) && lower(x.name) === lower(p[2]));
|
||||
return r ? [200, repoJson(r)] : [404, { message: 'not found' }];
|
||||
|
||||
@@ -6,11 +6,12 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { randomBytes } from 'node:crypto';
|
||||
import { chmodSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, statSync } from 'node:fs';
|
||||
import { chmodSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, statSync, writeFileSync } from 'node:fs';
|
||||
import { request } from 'node:https';
|
||||
import { createServer } from 'node:net';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { Broker, GRANT_EVIDENCE } from '../src/broker.mjs';
|
||||
import { Credentials } from '../src/credentials.mjs';
|
||||
@@ -58,6 +59,130 @@ function call(url, ca, method, path, { auth = null, body = null } = {}) {
|
||||
});
|
||||
}
|
||||
|
||||
// C2-21: independent of the executor. F-A and F-B gate production changes; F-C/F-D are observations.
|
||||
test('C2-21 F-A/F-B: admin collaborator grant on another owner and seat permission reach; F-C/F-D protection observations', { skip, timeout: 300000 }, async (t) => {
|
||||
// Capture the child's complete TAP stdout/stderr before any of it reaches this runner. The private
|
||||
// scratch file lets the parent search actual minted values without putting one in an assertion.
|
||||
if (!process.env.C2_GRANT_FACT_CAPTURE) {
|
||||
const capture = mkdtempSync(join(tmpdir(), 'g287-capture-'));
|
||||
t.after(() => rmSync(capture, { recursive: true, force: true }));
|
||||
const file = join(capture, 'minted');
|
||||
const childEnv = { ...process.env, C2_GRANT_FACT_CAPTURE: file };
|
||||
delete childEnv.NODE_TEST_CONTEXT;
|
||||
const child = spawnSync(process.execPath, ['--test', '--test-name-pattern=^C2-21 F-A/F-B:', fileURLToPath(import.meta.url)], {
|
||||
encoding: 'utf8', timeout: 280000, env: childEnv,
|
||||
});
|
||||
const values = existsSync(file) ? JSON.parse(readFileSync(file, 'utf8')) : [];
|
||||
const output = (child.stdout ?? '') + (child.stderr ?? '');
|
||||
assert.ok(values.every((value) => !output.includes(value)), 'complete TAP stdout/stderr contain no minted token');
|
||||
t.diagnostic(output);
|
||||
t.diagnostic(`minted ${values.length}, hits 0 (complete TAP stdout/stderr)`);
|
||||
assert.equal(child.status, 0, 'Docker facts child passed');
|
||||
assert.equal(values.length, 4, 'all four scratch tokens were minted: facts actually ran');
|
||||
return;
|
||||
}
|
||||
const dir = mkdtempSync(join(tmpdir(), 'g287-facts-'));
|
||||
t.after(() => rmSync(dir, { recursive: true, force: true }));
|
||||
const tlsDir = join(dir, 'tls'), tls = scratchCa(tlsDir);
|
||||
chmodSync(tlsDir, 0o755);
|
||||
chmodSync(join(tlsDir, 'server.key'), 0o644);
|
||||
const ca = readFileSync(tls.ca, 'utf8'), port = await freePort();
|
||||
const url = `https://127.0.0.1:${port}`, name = 'g287-' + randomBytes(6).toString('hex');
|
||||
t.after(() => docker('rm', '-f', name));
|
||||
const started = docker('run', '-d', '--rm', '--name', name, '-p', `127.0.0.1:${port}:3000`, '-v', `${tlsDir}:/certs:ro`,
|
||||
'-e', 'GITEA__security__INSTALL_LOCK=true', '-e', 'GITEA__database__DB_TYPE=sqlite3',
|
||||
'-e', 'GITEA__service__DISABLE_REGISTRATION=true', '-e', 'GITEA__server__PROTOCOL=https',
|
||||
'-e', 'GITEA__server__CERT_FILE=/certs/server.pem', '-e', 'GITEA__server__KEY_FILE=/certs/server.key',
|
||||
'-e', `GITEA__server__ROOT_URL=${url}/`, IMAGE);
|
||||
assert.equal(started.status, 0, 'scratch container starts');
|
||||
const end = Date.now() + 120000;
|
||||
while ((await call(url, ca, 'GET', '/api/v1/version')).status !== 200) {
|
||||
assert.ok(Date.now() < end, 'scratch Gitea answers in 120 seconds');
|
||||
await sleep(500);
|
||||
}
|
||||
const gitea = (...args) => docker('exec', '-u', 'git', name, 'gitea', ...args);
|
||||
const secrets = [], diagnostics = [];
|
||||
const say = (line) => { diagnostics.push(line); t.diagnostic(line); };
|
||||
for (const [user, admin] of [['mosaic-grant', true], ['writer', false], ['creative', false], ['chief', false]]) {
|
||||
const r = gitea('admin', 'user', 'create', '--username', user, '--email', `${user}@example.test`,
|
||||
'--password', randomBytes(16).toString('base64url'), '--must-change-password=false', ...(admin ? ['--admin'] : []));
|
||||
assert.equal(r.status, 0, `create scratch ${user}`);
|
||||
}
|
||||
const mint = (user, scopes) => {
|
||||
const r = gitea('admin', 'user', 'generate-access-token', '--username', user, '--token-name', 'facts', '--scopes', scopes.join(','), '--raw');
|
||||
assert.equal(r.status, 0, `mint scratch ${user}`);
|
||||
const value = r.stdout.trim();
|
||||
assert.ok(/^[0-9a-f]{40}$/.test(value), 'minted token shape');
|
||||
secrets.push(value);
|
||||
writeFileSync(process.env.C2_GRANT_FACT_CAPTURE, JSON.stringify(secrets), { mode: 0o600 });
|
||||
return value;
|
||||
};
|
||||
const adminToken = mint('mosaic-grant', ['write:admin', 'write:repository', 'write:user']);
|
||||
const writerToken = mint('writer', ['read:user', 'write:repository']);
|
||||
const creativeToken = mint('creative', ['read:user', 'write:repository']);
|
||||
const chiefToken = mint('chief', ['read:repository', 'read:user']);
|
||||
const made = await call(url, ca, 'POST', '/api/v1/admin/users/writer/repos', {
|
||||
auth: 'token ' + adminToken, body: { name: 'books', auto_init: true, private: true },
|
||||
});
|
||||
assert.equal(made.status, 201, 'create repository owned by writer, not admin');
|
||||
for (const [user, level, value, push] of [['creative', 'write', creativeToken, true], ['chief', 'read', chiefToken, false]]) {
|
||||
const added = await call(url, ca, 'PUT', `/api/v1/repos/writer/books/collaborators/${user}`, {
|
||||
auth: 'token ' + adminToken, body: { permission: level },
|
||||
});
|
||||
say(`F-A ${user} ${level}: PUT status ${added.status}`);
|
||||
assert.equal(added.status, 204, 'F-A admin scopes can add collaborator to another owner');
|
||||
const listed = await call(url, ca, 'GET', '/api/v1/user/repos', { auth: 'token ' + value });
|
||||
assert.equal(listed.status, 200, 'F-B seat lists reachable repos');
|
||||
const repository = listed.json?.find((r) => r.full_name === 'writer/books');
|
||||
assert.ok(repository, 'F-B seat lists the granted repository');
|
||||
say(`F-B ${user}: permissions ${JSON.stringify(repository.permissions)}`);
|
||||
assert.deepEqual({ admin: repository.permissions?.admin, push: repository.permissions?.push, pull: repository.permissions?.pull },
|
||||
{ admin: false, push, pull: true }, 'F-B exact admin/push/pull keys');
|
||||
}
|
||||
|
||||
// No diagnostic includes a Git child result: HTTP auth is supplied only in that child's env.
|
||||
const git = (args, user = 'writer', value = writerToken, cwd = dir) => spawnSync('git', args, {
|
||||
cwd, encoding: 'utf8', timeout: 30000,
|
||||
env: { ...process.env, GIT_CONFIG_NOSYSTEM: '1', GIT_CONFIG_GLOBAL: '/dev/null', GIT_TERMINAL_PROMPT: '0',
|
||||
GIT_SSL_CAINFO: tls.ca, GIT_CONFIG_COUNT: '3', GIT_CONFIG_KEY_0: 'http.extraHeader',
|
||||
GIT_CONFIG_VALUE_0: 'Authorization: Basic ' + Buffer.from(`${user}:${value}`).toString('base64'),
|
||||
GIT_CONFIG_KEY_1: 'user.name', GIT_CONFIG_VALUE_1: 'Scratch writer',
|
||||
GIT_CONFIG_KEY_2: 'user.email', GIT_CONFIG_VALUE_2: '[email protected]' },
|
||||
});
|
||||
const work = join(dir, 'work');
|
||||
const branch = made.json.default_branch;
|
||||
const clone = git(['clone', `${url}/writer/books.git`, work]);
|
||||
const protection = await call(url, ca, 'POST', '/api/v1/repos/writer/books/branch_protections', {
|
||||
auth: 'token ' + writerToken,
|
||||
body: { branch_name: branch, rule_name: branch, enable_push: true, enable_push_whitelist: false,
|
||||
enable_force_push: false, protected_file_patterns: 'Story/**' },
|
||||
});
|
||||
if (clone.status !== 0 || protection.status !== 201) {
|
||||
say(`F-C/F-D not established: clone rc ${clone.status}, protection HTTP ${protection.status}`);
|
||||
} else {
|
||||
const commit = (path, text) => {
|
||||
mkdirSync(join(work, path.split('/')[0]), { recursive: true });
|
||||
writeFileSync(join(work, path), text);
|
||||
const add = git(['add', path], 'writer', writerToken, work);
|
||||
const made = git(['commit', '-m', 'scratch fact'], 'writer', writerToken, work);
|
||||
return add.status === 0 && made.status === 0;
|
||||
};
|
||||
const story = commit('Story/chapter.md', 'scratch story\n');
|
||||
const seatStory = story ? git(['push', 'origin', `HEAD:${branch}`], 'creative', creativeToken, work).status : null;
|
||||
const ownerStory = story ? git(['push', 'origin', `HEAD:${branch}`], 'writer', writerToken, work).status : null;
|
||||
say(`F-C protected Story/**: seat rc ${seatStory}, owner rc ${ownerStory}; established ${seatStory !== null && seatStory !== 0 && ownerStory === 0}`);
|
||||
// Start from the actual remote tip even when the file-pattern observation failed.
|
||||
const fetched = git(['fetch', 'origin', branch], 'writer', writerToken, work);
|
||||
const reset = git(['reset', '--hard', 'FETCH_HEAD'], 'writer', writerToken, work);
|
||||
const supporting = fetched.status === 0 && reset.status === 0 && commit('Notes/note.md', 'scratch supporting note\n');
|
||||
const normal = supporting ? git(['push', 'origin', `HEAD:${branch}`], 'creative', creativeToken, work).status : null;
|
||||
const force = normal === 0 ? git(['push', '--force', 'origin', `HEAD~1:${branch}`], 'creative', creativeToken, work).status : null;
|
||||
say(`F-D protected branch: normal rc ${normal}, force rc ${force}; established ${normal === 0 && force !== null && force !== 0}`);
|
||||
}
|
||||
assert.ok(secrets.every((value) => diagnostics.every((line) => !line.includes(value))), 'diagnostics contain no minted token');
|
||||
say(`minted ${secrets.length}, hits 0`);
|
||||
});
|
||||
|
||||
test('X-G3: on scratch 1.27.1, a grant makes the user, mints, forks, checks reach, writes the file, and revoke deletes the token', { skip, timeout: 300000 }, async (t) => {
|
||||
const dir = mkdtempSync(join(tmpdir(), 'g236-docker-'));
|
||||
t.after(() => rmSync(dir, { recursive: true, force: true }));
|
||||
@@ -69,7 +194,7 @@ test('X-G3: on scratch 1.27.1, a grant makes the user, mints, forks, checks reac
|
||||
const ca = readFileSync(tls.ca, 'utf8');
|
||||
const port = await freePort();
|
||||
const url = `https://127.0.0.1:${port}`;
|
||||
const name = 'g236-' + randomBytes(6).toString('hex');
|
||||
const name = 'g287-executor-' + randomBytes(6).toString('hex');
|
||||
t.after(() => docker('rm', '-f', name));
|
||||
const started = docker('run', '-d', '--rm', '--name', name, '-p', `127.0.0.1:${port}:3000`, '-v', `${tlsDir}:/certs:ro`,
|
||||
'-e', 'GITEA__security__INSTALL_LOCK=true',
|
||||
|
||||
@@ -1306,3 +1306,158 @@ test('status lists the slot, users, grants and nothing secret, and every grants
|
||||
await rejects(bare.request(human, { verb: 'grants.status', args: { service: 'gitea' } }), 'gitea-not-configured');
|
||||
f.noLeak();
|
||||
});
|
||||
|
||||
// C2-21: these catch library forking, wrong collaborator/token level, widened reach, and stale bindings.
|
||||
function libraryBusiness(b, { agent = 'coder', definition = 'research', publish = ['app'], library = ['app'] } = {}) {
|
||||
const original = b.roles.coder;
|
||||
b.roles[agent] = { ...original, definition, publish,
|
||||
identity: { ...original.identity, gitea: { ...original.identity.gitea, user: agent === 'chief' ? 'demo-chief' : USER } } };
|
||||
b.definitions[definition] = { roleVersion: 2, credentials: [{ service: 'gitea',
|
||||
scopes: definition === 'chief-of-staff' ? ['read:repository', 'read:user'] : ['read:user', 'write:repository'] }] };
|
||||
for (const project of library) b.projects[project].kind = 'library';
|
||||
}
|
||||
|
||||
for (const [definition, agent, level, scopes] of [
|
||||
['research', 'coder', 'write', ['read:user', 'write:repository']],
|
||||
['chief-of-staff', 'chief', 'read', ['read:repository', 'read:user']],
|
||||
['coder', 'coder', 'read', ['read:user', 'write:repository']],
|
||||
]) {
|
||||
test(`C2-21 L1/L2/L9: ${definition} gets ${level} on her repository with declared scopes through raise and yes`, async (t) => {
|
||||
const f = await setup(t, { business: (b) => {
|
||||
libraryBusiness(b, { agent, definition });
|
||||
b.launch = { by: 'chief', instances: ['coder', 'pm'] };
|
||||
} });
|
||||
const user = agent === 'chief' ? 'demo-chief' : USER;
|
||||
const puts = [];
|
||||
f.gitea.rule((e) => e.method === 'PUT' && e.path.includes('/collaborators/'), (e, body) => {
|
||||
puts.push({ path: e.path, as: e.as, auth: e.auth, body });
|
||||
return { pass: true };
|
||||
});
|
||||
const d = await f.raise({ agent });
|
||||
const block = f.store.get('SELECT question FROM decisions WHERE id=?', d.id).question;
|
||||
assert.ok(block.includes(`Repository: ${f.gitea.url}/acme/app, level ${level}.`));
|
||||
assert.doesNotMatch(block, /\nForks:/);
|
||||
assert.ok(block.includes(`Scopes: ${scopes.join(', ')}.`));
|
||||
assert.ok(block.includes(level === 'write'
|
||||
? "The token can write every path of this repository. Only the bus's path check limits it to supporting folders."
|
||||
: "The token can read every path of this repository. It can't push to it, because the user is a read collaborator."));
|
||||
f.call('human', 'decision.resolve', { id: d.id, choice: 'yes' });
|
||||
await until(() => f.ended(d.id));
|
||||
await f.broker.giteaIdle();
|
||||
const end = f.ended(d.id);
|
||||
assert.equal(end.action, 'grant.applied', JSON.stringify(end));
|
||||
assert.deepEqual(end.forks, []);
|
||||
assert.deepEqual(end.repositories, [{ project: 'app', repository: `${f.gitea.url}/acme/app`, level }]);
|
||||
assert.deepEqual(puts, [{ path: `/api/v1/repos/acme/app/collaborators/${user}`, as: 'mosaic-grant', auth: 'token', body: { permission: level } }]);
|
||||
assert.equal(f.gitea.calls().filter((c) => c.endsWith('/forks')).length, 0);
|
||||
assert.deepEqual(f.gitea.tokens(user)[0].scopes, scopes);
|
||||
const messages = f.messages(d.id).map((m) => m.body).join('\n');
|
||||
assert.ok(messages.includes(`repositories: ${f.gitea.url}/acme/app (${level})`));
|
||||
assert.doesNotMatch(messages, /forks: \./);
|
||||
const results = f.grantEvents();
|
||||
assert.ok(results.some((e) => e.action === 'grant.call' && e.op === 'collaborator-add'));
|
||||
f.noLeak();
|
||||
// L5: token revocation leaves the collaborator, just as legacy forks remain.
|
||||
await f.call('human', 'grants.revoke', { service: 'gitea', agent, reason: 'C2-21' });
|
||||
await f.broker.giteaIdle();
|
||||
assert.deepEqual(f.gitea.tokens(user), []);
|
||||
assert.ok(f.gitea.repos.find((r) => r.owner === 'acme' && r.name === 'app').collaborators.includes(user));
|
||||
});
|
||||
}
|
||||
|
||||
for (const [agent, definition] of [['coder', 'research'], ['chief', 'chief-of-staff']]) {
|
||||
for (const projects of [['lib'], ['app', 'lib']]) {
|
||||
test(`C2-21 L7/L7b: ${definition} refuses non-library targets ${projects.join(',')}`, async (t) => {
|
||||
const f = await setup(t, { business: (b) => libraryBusiness(b, { agent, definition, publish: ['app', 'lib'] }) });
|
||||
await rejects(f.raise({ agent, target: { projects } }), 'gitea-not-declared');
|
||||
assert.equal(f.store.get('SELECT count(*) AS n FROM decisions').n, 0);
|
||||
assert.equal(f.gitea.requests.length, 0);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
for (const [name, permissions, fork] of [
|
||||
['read widened to write', { admin: false, push: true, pull: true }, false],
|
||||
['admin privilege', { admin: true, push: false, pull: true }, false],
|
||||
['missing pull', { admin: false, push: false, pull: false }, false],
|
||||
['missing permission object', null, false],
|
||||
['library fork', { admin: false, push: false, pull: true }, true],
|
||||
]) {
|
||||
test(`C2-21 L4: ${name} refuses gitea-reach-wider and deletes the minted token`, async (t) => {
|
||||
const f = await setup(t, { business: (b) => libraryBusiness(b, { agent: 'chief', definition: 'chief-of-staff' }) });
|
||||
const repository = fork ? 'demo-chief/app' : 'acme/app';
|
||||
f.gitea.rule((e) => e.as === 'demo-chief' && e.path.startsWith('/api/v1/user/repos?'), { status: 200, total: 1, json: [{
|
||||
full_name: repository, owner: { login: fork ? 'demo-chief' : 'acme' },
|
||||
parent: fork ? { full_name: 'acme/app' } : null, permissions,
|
||||
}] });
|
||||
const { end } = await f.approve({ agent: 'chief' });
|
||||
assert.equal(end.code, 'gitea-reach-wider', JSON.stringify(end));
|
||||
assert.equal(end.repository, repository);
|
||||
assert.deepEqual(f.gitea.tokens('demo-chief'), []);
|
||||
assert.equal(f.store.get("SELECT 1 FROM gitea_grants WHERE agent='chief'"), undefined);
|
||||
f.noLeak();
|
||||
});
|
||||
}
|
||||
|
||||
test('C2-21 L8: untargeted library collaborator reach is disclosed and accepted publish-wide', async (t) => {
|
||||
const f = await setup(t, { business: (b) => libraryBusiness(b, { publish: ['app', 'lib'], library: ['app', 'lib'] }) });
|
||||
f.gitea.repos.find((r) => r.owner === 'acme' && r.name === 'lib').collaborators.push(USER);
|
||||
const d = await f.raise();
|
||||
const block = f.store.get('SELECT question FROM decisions WHERE id=?', d.id).question;
|
||||
assert.ok(block.includes(`It may also reach these repositories as a collaborator: ${f.gitea.url}/acme/lib (write)`));
|
||||
assert.ok(block.includes('Step 5 checks that this is only its forks and the repositories above at their level.'));
|
||||
assert.doesNotMatch(block, /existing forks of:/);
|
||||
f.call('human', 'decision.resolve', { id: d.id, choice: 'yes' });
|
||||
await until(() => f.ended(d.id));
|
||||
await f.broker.giteaIdle();
|
||||
assert.equal(f.ended(d.id).action, 'grant.applied');
|
||||
});
|
||||
|
||||
test('C2-21 L10: project kind or library level changing after raise refuses decision-mismatch', async (t) => {
|
||||
for (const change of ['kind', 'level']) {
|
||||
const f = await setup(t, { business: (b) => {
|
||||
b.definitions.coder.credentials = [{ service: 'gitea', scopes: ['read:user', 'write:repository'] }];
|
||||
if (change === 'level') libraryBusiness(b);
|
||||
} });
|
||||
const d = await f.raise();
|
||||
const next = structuredClone(f.business);
|
||||
if (change === 'kind') next.projects.app.kind = 'library';
|
||||
else {
|
||||
next.definitions['chief-of-staff'] = structuredClone(next.definitions.research);
|
||||
next.roles.coder.definition = 'chief-of-staff';
|
||||
}
|
||||
f.broker.reloadBusiness('demo', next);
|
||||
f.call('human', 'decision.resolve', { id: d.id, choice: 'yes' });
|
||||
await until(() => f.ended(d.id));
|
||||
await f.broker.giteaIdle();
|
||||
assert.equal(f.ended(d.id).code, 'decision-mismatch');
|
||||
assert.deepEqual(f.gitea.calls(), ['GET /api/v1/version', 'GET /api/v1/user', 'GET /api/v1/token', 'GET /api/v1/user/repos']);
|
||||
assert.ok(f.gitea.requests.every((r) => r.method === 'GET' && !/\/admin\/users|\/tokens|\/collaborators/.test(r.path)));
|
||||
assert.equal(existsSync(f.tokenFile), false);
|
||||
assert.equal(f.record(), undefined);
|
||||
}
|
||||
});
|
||||
|
||||
test('C2-21 L4: creative write requires push true but ignores unrelated permission keys', async (t) => {
|
||||
for (const push of [false, true]) {
|
||||
const f = await setup(t, { business: (b) => libraryBusiness(b) });
|
||||
f.gitea.rule((e) => e.as === USER && e.path.startsWith('/api/v1/user/repos?'), { status: 200, total: 1, json: [{
|
||||
full_name: 'acme/app', owner: { login: 'acme' }, parent: null,
|
||||
permissions: { admin: false, push, pull: true, issues: true },
|
||||
}] });
|
||||
const { end } = await f.approve();
|
||||
assert.equal(end.action, push ? 'grant.applied' : 'grant.failed');
|
||||
if (!push) assert.equal(end.code, 'gitea-reach-wider');
|
||||
}
|
||||
});
|
||||
|
||||
// mos-1590: the L10 fixture must not hide the no-declared-scopes refusal.
|
||||
test('C2-21: a library grant with no declared scopes refuses gitea-not-declared at raise', async (t) => {
|
||||
const f = await setup(t, { business: (b) => {
|
||||
libraryBusiness(b);
|
||||
delete b.definitions.research.credentials[0].scopes;
|
||||
} });
|
||||
await rejects(f.raise(), 'gitea-not-declared');
|
||||
assert.equal(f.store.get('SELECT count(*) AS n FROM decisions').n, 0);
|
||||
assert.equal(f.gitea.requests.length, 0);
|
||||
});
|
||||
@@ -1,8 +1,7 @@
|
||||
// C2-03b (#1791, design 7.2): the credential the library fetch runs with. The chief's grant store file
|
||||
// once a chief has Gitea grant evidence, slot library-gitea before that, and a refusal otherwise.
|
||||
// sage-4100 corrects sage-2947/dewey-0864 for the mint only: before C2-21, the real write-grant
|
||||
// path must refuse the chief. T7/T9 assert that refusal, then seed a read-only fake grant. C2-21's
|
||||
// real chief read executor replaces that seed when it lands. Revocation and fire use the real verbs.
|
||||
// C2-21 replaces sage-4100's read fixture seed: T7/T9 raise the real chief library grant, the human
|
||||
// resolves yes and the executor mints it. Revocation and fire keep using the same real verbs.
|
||||
// The choice function's other checks (listed, a fire under way, held, two chiefs, another project)
|
||||
// use fixtures: the evidence and the gitea_grants row go in by SQL, as gitea-grant.test.mjs does for
|
||||
// decisions. Tokens are made up or the fake's, and only ever compared.
|
||||
@@ -75,23 +74,23 @@ function world(t, options) {
|
||||
|
||||
// The business for the real paths. `boss` is launch.by: instance.fire is the launch.by role's verb and
|
||||
// refuses its own name (organization-instance-referenced), so the fired chief is the one listed
|
||||
// chief-of-staff instance, fired by boss. The grant targets project app; books is the library.
|
||||
// chief-of-staff instance, fired by boss. The real read grant targets the library project books.
|
||||
function verbDoc(url, root) {
|
||||
return {
|
||||
id: 'acme', human: 'jason', arbiters: { technical: 'boss', delivery: 'boss' },
|
||||
roles: {
|
||||
boss: { definition: 'pm', authority: chiefAuthority },
|
||||
// sage-4097 / mos-1584: fixture projection carries the chief's own Gitea store reference.
|
||||
chief: { definition: 'chief-of-staff', ...none, credentials: { gitea: giteaReference(join(root, 'secrets', 'agents'), 'acme', 'chief', '2099-01-01') }, identity: { name: 'Chief', email: '[email protected]', gitea: { url, user: 'acme-chief' } }, publish: ['app'] },
|
||||
chief: { definition: 'chief-of-staff', ...none, credentials: { gitea: giteaReference(join(root, 'secrets', 'agents'), 'acme', 'chief', '2099-01-01') }, identity: { name: 'Chief', email: '[email protected]', gitea: { url, user: 'acme-chief' } }, publish: ['books'] },
|
||||
coder: { definition: 'coder', ...none },
|
||||
},
|
||||
definitions: {
|
||||
'chief-of-staff': { roleVersion: 2, credentials: [{ service: 'gitea' }] },
|
||||
'chief-of-staff': { roleVersion: 2, credentials: [{ service: 'gitea', scopes: ['read:repository', 'read:user'] }] },
|
||||
pm: { roleVersion: 2, credentials: [] },
|
||||
coder: { roleVersion: 2, credentials: [] },
|
||||
},
|
||||
// sage-4054 / mos-1272: runtime validates every code project's absolute checkout root.
|
||||
projects: { app: { root: join(root, 'app-checkout'), remote: `${url}/acme/app` }, books: { ...mappingEntry(), user: 'books-bot' } },
|
||||
projects: { app: { root: join(root, 'app-checkout'), remote: `${url}/acme/app` }, books: { ...mappingEntry(), remote: `${url}/acme/books`, user: 'books-bot' } },
|
||||
launch: { by: 'boss', instances: ['chief', 'coder'] },
|
||||
};
|
||||
}
|
||||
@@ -108,6 +107,7 @@ async function verbWorld(t, opts = {}) {
|
||||
gitea = await fakeGiteaApi(t, join(dir, 'tls'));
|
||||
gitea.addUser('acme');
|
||||
gitea.addRepo('acme', 'app');
|
||||
gitea.addRepo('acme', 'books');
|
||||
}
|
||||
const adminDir = join(dir, 'admin');
|
||||
mkdirSync(adminDir, { mode: 0o700 });
|
||||
@@ -150,33 +150,22 @@ async function verbWorld(t, opts = {}) {
|
||||
const human = broker.bindHuman({ business: 'acme', human: 'jason', via: 'cli', outsideAgent: true });
|
||||
const events = () => store.all(`SELECT body FROM events WHERE business='acme' AND ${GRANT_EVIDENCE} ORDER BY seq`).map((e) => JSON.parse(e.body));
|
||||
const row = () => store.get("SELECT 1 FROM gitea_grants WHERE business='acme' AND agent='chief'");
|
||||
const seedChiefReadGrant = async () => {
|
||||
// sage-4100 / C2-21: the real chief read-grant executor replaces this fixture seed.
|
||||
// The current executor mints write scopes and must keep refusing the chief (sage-2921).
|
||||
await assert.rejects(broker.request(chief, {
|
||||
const mintChiefReadGrant = async () => {
|
||||
const decision = await broker.request(chief, {
|
||||
verb: 'decision.raise',
|
||||
args: {
|
||||
action: 'credential.mint', question: 'Grant chief Gitea?', options, recommendation: 'yes', blocking: false,
|
||||
grant: { method: 'gitea', agent: 'chief', target: { projects: ['app'] }, rotateBy: dayFrom(30) },
|
||||
action: 'credential.mint', question: 'Grant chief library read?', options, recommendation: 'yes', blocking: false,
|
||||
grant: { method: 'gitea', agent: 'chief', target: { projects: ['books'] }, rotateBy: dayFrom(30) },
|
||||
},
|
||||
}), { code: 'gitea-not-declared' });
|
||||
const rotateBy = dayFrom(30), user = 'acme-chief', nonce = randomBytes(8).toString('hex');
|
||||
const decision = grantDecision(store, { business: 'acme', grant: { method: 'gitea', agent: 'chief', target: { projects: ['app'] }, rotateBy } });
|
||||
const { user: account } = gitea.addUser(user, { full_name: `Chief [mosaic ${nonce}]` });
|
||||
const token = gitea.addToken(user, 'mosaic-chief-read-' + decision, ['read:repository', 'read:user']);
|
||||
assert.deepEqual(token.scopes, ['read:repository', 'read:user'], 'fixture grants read scopes only (sage-4100)');
|
||||
const keys = giteaKeys({ credentials, storeRoot });
|
||||
keys.register('acme', 'chief', rotateBy);
|
||||
// No exported grant-record writer exists; use the executor's credential-file writer and direct
|
||||
// store inserts for the fixture-only records (sage-4100), with the fake's actual user/token IDs.
|
||||
keys.issue('acme', 'chief', token.sha1, rotateBy, () => store.transaction(() => {
|
||||
store.run('INSERT INTO gitea_users(business,agent,user,id,nonce,decision) VALUES(?,?,?,?,?,?)', 'acme', 'chief', user, account.id, nonce, decision);
|
||||
store.run('INSERT INTO gitea_grants(business,agent,user,token_id,token_name,projects,rotate_by,decision) VALUES(?,?,?,?,?,?,?,?)',
|
||||
'acme', 'chief', user, token.id, token.name, JSON.stringify(['app']), rotateBy, decision);
|
||||
writeGrantEvent(store, AT, 'acme', { action: 'grant.applied', method: 'gitea', agent: 'chief', grantDecision: decision, user,
|
||||
userId: account.id, tokenId: token.id, permission: 'read' }, 'chief');
|
||||
}));
|
||||
assert.ok(row(), 'with its gitea_grants row');
|
||||
});
|
||||
broker.request(human, { verb: 'decision.resolve', args: { id: decision.id, choice: 'yes' } });
|
||||
await until(() => events().some((e) => e.grantDecision === decision.id && ['grant.applied', 'grant.failed'].includes(e.action)));
|
||||
await broker.giteaIdle();
|
||||
const end = events().find((e) => e.grantDecision === decision.id && ['grant.applied', 'grant.failed'].includes(e.action));
|
||||
assert.equal(end.action, 'grant.applied', JSON.stringify(end));
|
||||
assert.deepEqual(end.repositories, [{ project: 'books', repository: `${gitea.url}/acme/books`, level: 'read' }]);
|
||||
assert.deepEqual(gitea.tokens('acme-chief')[0].scopes, ['read:repository', 'read:user']);
|
||||
assert.ok(row(), 'real grant wrote its gitea_grants row');
|
||||
};
|
||||
const revoke = async () => {
|
||||
await broker.request(human, { verb: 'grants.revoke', args: { service: 'gitea', agent: 'chief', reason: 'test' } });
|
||||
@@ -195,7 +184,7 @@ async function verbWorld(t, opts = {}) {
|
||||
broker.request(chief, { verb: 'role.release' });
|
||||
broker.request(boss, { verb: 'role.release' });
|
||||
};
|
||||
return { gitea, doc, store, broker, storeRoot, row, seedChiefReadGrant, revoke, fire, close, release, choose: () => broker.libraryFetchCredential('acme') };
|
||||
return { gitea, doc, store, broker, storeRoot, row, mintChiefReadGrant, revoke, fire, close, release, choose: () => broker.libraryFetchCredential('acme') };
|
||||
}
|
||||
|
||||
const REFUSED = { refused: 'library-fetch-no-grant', fix: LIBRARY_FETCH_FIX };
|
||||
@@ -216,13 +205,13 @@ test('C2-03b T6: the slot before any chief evidence, then the listed chief\'s li
|
||||
test('C2-03b T7: a chief revoked through grants.revoke or fired through instance.fire refuses, and the fallback stays ended', { timeout: 60000 }, async t => {
|
||||
const revoked = await verbWorld(t);
|
||||
assert.deepEqual(revoked.choose(), { kind: 'slot' });
|
||||
await revoked.seedChiefReadGrant();
|
||||
await revoked.mintChiefReadGrant();
|
||||
assert.deepEqual(revoked.choose(), { kind: 'chief', agent: 'chief', user: 'acme-chief' });
|
||||
await revoked.revoke();
|
||||
assert.deepEqual(revoked.choose(), REFUSED, 'revoked');
|
||||
|
||||
const fired = await verbWorld(t);
|
||||
await fired.seedChiefReadGrant();
|
||||
await fired.mintChiefReadGrant();
|
||||
assert.deepEqual(fired.choose(), { kind: 'chief', agent: 'chief', user: 'acme-chief' });
|
||||
await fired.fire();
|
||||
assert.equal(fired.doc.launch.instances.includes('chief'), false, 'delisted');
|
||||
@@ -283,7 +272,7 @@ test('C2-03b T9 runtime: the chief\'s store file and user, then a real revoke th
|
||||
mkdirSync(join(root, 'secrets', 'acme'), { recursive: true, mode: 0o700 });
|
||||
writeFileSync(slot, SLOT_TOKEN + '\n', { mode: 0o600 });
|
||||
const granted = await verbWorld(t, { dataRoot, storeRoot });
|
||||
await granted.seedChiefReadGrant();
|
||||
await granted.mintChiefReadGrant();
|
||||
granted.release();
|
||||
granted.close();
|
||||
const gitea = granted.gitea;
|
||||
|
||||
@@ -292,7 +292,9 @@ export function validateBusinessDocument(document, file, { rolesDir }) {
|
||||
for (const [instance, role] of Object.entries(roles)) {
|
||||
for (const project of role.publish ?? []) {
|
||||
if (!Object.hasOwn(projects, project)) refuse(`${file} roles.${instance}.publish names ${project}, which the business file doesn't declare`);
|
||||
if (projects[project].kind === 'library') refuse(`${file} roles.${instance}.publish names library project ${project}; library repositories cannot be published by agents`);
|
||||
// C2-21, sage-2916: retain the fence; only creative instances and the chief may hold library reach.
|
||||
if (projects[project].kind === 'library' && !CREATIVE_DEFINITIONS.includes(role.definition) && role.definition !== 'chief-of-staff')
|
||||
refuse(`${file} roles.${instance}.publish names library project ${project}; library publish requires a creative definition or chief-of-staff with gitea`);
|
||||
if (!projects[project].remote) refuse(`${file} roles.${instance}.publish names ${project}, which has no "remote"`);
|
||||
const problem = forkProblem(projects[project].remote, role.identity);
|
||||
if (problem) refuse(`${file} roles.${instance}.publish names ${project}: ${problem}`);
|
||||
|
||||
@@ -360,3 +360,28 @@ test("row 194 each bad keep, noHire or retired entry refuses", () => {
|
||||
refuses(() => check((d) => { d.retired = ["../x"]; }), /retired entry/);
|
||||
refuses(() => check((d) => { d.retired = {}; }), /retired must be an array/);
|
||||
});
|
||||
|
||||
// C2-21 B1-B4: catch a deleted/overbroad library publish fence while retaining identity/credential rules.
|
||||
test('C2-21: only creative definitions and the chief with gitea may publish a library project', async (t) => {
|
||||
const { mappingEntry } = await import('../../library/tests/helpers.mjs');
|
||||
for (const definition of [...CREATIVE_DEFINITIONS, 'chief-of-staff']) {
|
||||
const mutate = (d) => {
|
||||
d.projects.books = { ...mappingEntry(), remote: 'https://git.example/writer/books' };
|
||||
d.roles.chief.credentials = CREATIVE_GITEA;
|
||||
const agent = definition === 'chief-of-staff' ? 'chief' : 'bean';
|
||||
d.roles[agent] = { ...(d.roles[agent] ?? {}), definition, credentials: CREATIVE_GITEA,
|
||||
identity: { name: 'Library reader', email: '[email protected]', gitea: { url: 'https://git.example', user: 'acme-reader' } }, publish: ['books'] };
|
||||
};
|
||||
const business = check(mutate);
|
||||
assert.deepEqual(business.roles[definition === 'chief-of-staff' ? 'chief' : 'bean'].publish, ['books']);
|
||||
refuses(() => check((d) => { mutate(d); delete d.roles[definition === 'chief-of-staff' ? 'chief' : 'bean'].identity; }), /publish needs identity and a gitea definition/);
|
||||
}
|
||||
for (const agent of ['coder', 'reviewer', 'pm', 'cto']) {
|
||||
refuses(() => check((d) => {
|
||||
d.projects.books = { ...mappingEntry(), remote: 'https://git.example/writer/books' };
|
||||
d.roles.chief.credentials = CREATIVE_GITEA;
|
||||
d.roles[agent].identity = { name: 'Other', email: '[email protected]', gitea: { url: 'https://git.example', user: 'acme-other' } };
|
||||
d.roles[agent].publish = ['books'];
|
||||
}), /publish.*library.*creative.*chief/);
|
||||
}
|
||||
});
|
||||
@@ -477,3 +477,16 @@ test("row 322 F9: importer refusal keeps the error line rather than the Node ban
|
||||
assert.equal(existsSync(x.business), false);
|
||||
assert.equal(existsSync(x.personaDir), false);
|
||||
});
|
||||
|
||||
test('C2-21: library install supplies chief identity and books publish for its real read grant', (t) => {
|
||||
const x = world(t);
|
||||
const out = install(x);
|
||||
assert.equal(out.code, 0, out.out);
|
||||
const doc = JSON.parse(readFileSync(x.business, 'utf8'));
|
||||
assert.deepEqual(doc.roles.chief.publish, ['books']);
|
||||
assert.ok(doc.roles.chief.identity?.name);
|
||||
assert.ok(doc.roles.chief.identity?.email);
|
||||
assert.equal(doc.roles.chief.identity?.gitea?.url, new URL(doc.projects.books.remote).origin);
|
||||
assert.ok(doc.roles.chief.identity?.gitea?.user);
|
||||
assert.notEqual(doc.roles.chief.identity?.gitea?.user, 'writer');
|
||||
});
|
||||
@@ -13,8 +13,8 @@
|
||||
# activate, starts no unit, writes no unit file and nothing under /etc, and
|
||||
# never writes the library token, which is Jason's step 4.
|
||||
#
|
||||
# FILE is the business document written for the host from the go. The
|
||||
# script adds only roles.bean.vars and roles.chet.vars persona.name and
|
||||
# FILE is the business document written for the host from the go. For a library,
|
||||
# the script adds chief identity/publish and roles.bean.vars and roles.chet.vars persona.name and
|
||||
# persona.sha256, from the staging manifest, and checks the input against
|
||||
# the precheck's profile rules first: library needs a library project,
|
||||
# bus.tester off and bean and chet as companion roles with no persona var;
|
||||
@@ -109,6 +109,13 @@ if (profile === "factory") {
|
||||
} else {
|
||||
if (!library.length) stop("no-library-project", `${input} has no library project`);
|
||||
if (tester) stop("bus-tester-on", "the library profile needs bus.tester off");
|
||||
// C2-21: the chief mints its own read grant after install; no token or grant is created here.
|
||||
const chief = Object.values(doc.roles ?? {}).find((r) => r?.definition === "chief-of-staff");
|
||||
if (!chief) stop("chief-missing", "the library profile needs a chief-of-staff instance");
|
||||
const project = projects.books?.kind === "library" ? "books" : library[0];
|
||||
chief.identity ??= { name: "Chief of staff", email: `${id}[email protected]`,
|
||||
gitea: { url: new URL(projects[project].remote).origin, user: `${id}-chief` } };
|
||||
chief.publish = [project];
|
||||
for (const instance of ["bean", "chet"]) {
|
||||
const role = doc.roles?.[instance];
|
||||
if (role?.definition !== "companion") stop("companion-roles", `roles.${instance} must have definition companion`);
|
||||
|
||||
Reference in new issue
Block a user