From eee8ad776c0e621fe2d2b4532d94c7a9f8207546 Mon Sep 17 00:00:00 2001 From: Jason Woltje Date: Sat, 24 Oct 2026 05:59:43 -0500 Subject: [PATCH] row 285: align library and creative fixture credential data under B1 (mos-1581, mos-1582) --- BUILD-LOG.md | 2 + agents/magica/work/queue-285/BUILD.md | 1077 +++++++++++++++++ agents/magica/work/queue-285/b1-base-diff.txt | 365 ++++++ .../work/queue-285/b1-base-word-diff.txt | 698 +++++++++++ .../queue-285/b1-incremental-word-diff.txt | 452 +++++++ docs/SESSIONS.md | 2 + packages/bus/tests/reload.test.mjs | 4 +- packages/business/tests/business.test.mjs | 2 + packages/business/tests/cli.test.mjs | 2 +- .../business/tests/instance-settings.test.mjs | 2 +- .../tests/library-projects-protected.test.mjs | 1 + .../business/tests/library-seats.test.mjs | 3 + packages/business/tests/resolve.test.mjs | 2 +- packages/cli/tests/case2-install.test.mjs | 4 + packages/cli/tests/launcher.test.mjs | 2 +- packages/cli/tests/thread-launch.test.mjs | 4 + packages/cli/tests/trackers-boot.test.mjs | 5 +- .../tests/creative-hidden.test.mjs | 2 + packages/discord/tests/library-seat.test.mjs | 2 + 19 files changed, 2623 insertions(+), 8 deletions(-) create mode 100644 agents/magica/work/queue-285/b1-base-diff.txt create mode 100644 agents/magica/work/queue-285/b1-base-word-diff.txt create mode 100644 agents/magica/work/queue-285/b1-incremental-word-diff.txt diff --git a/BUILD-LOG.md b/BUILD-LOG.md index 9f182193c..9844b873b 100644 --- a/BUILD-LOG.md +++ b/BUILD-LOG.md @@ -5484,3 +5484,5 @@ remain; no live state, schema or bus policy change. 2026-10-24T10:32:02Z | Magica | Row285 / #1790 gate STOP (sage-4048) | Before: pushed66a800e15, no tests. After: own root/webui npm ci, short scratch TMPDIR, whole files serial timeout-k10; bus958/958 green (108 files), business partial80pass/4fail, first red old row323 case1-business-file tests57/86/95/105 because committed Case1 chief lacks required gitea ref. No more tests/mutants or source edits. Static diagnosis also finds old empty-credential assertions81/112/114, requiring a specific owner ruling. Sage notified magica-0002. No in-review move or green claim. 2026-10-24T10:42:28Z | Magica | Row285 / #1790 Amendment1 before/after | Before: unconditional chief gitea broke row323. After: chief-only library conditional plus named required-reference refusal, checkCredentialMap unchanged; new three-case test file; own helper/resolve fixture hunks restored to base, own chief/business/identity hunks reconciled under sage-4060. Case1 doc/test byte-identical. M10 planned against new no-library positive and old Case1 doc test. No tests run under held slot; not green/accepted. Residual base library fixture references need authority alignment before re-CLEAR. + +2026-10-24T10:59:43Z | Magica | Row285 / #1790 B1 before/after | Before: Amendment1 candidatebabae6adc with missing refs in base library/creative fixtures. After: 26 data-only credential insertions across13 direct-reader files; original callback lines preserved, one expressly allowed credentials?.vikunja setup guard. Every other byte reconstructs prior candidate exactly; no base assertion edits. Base and candidate put role set is pm,cto,coder,reviewer; chief lacks vikunja. Literal e0c6142f3 word diffs and known preexisting r2 hunks recorded. No tests, green claim or queue move; next whole gate +M10 waits for Sage CLEAR. diff --git a/agents/magica/work/queue-285/BUILD.md b/agents/magica/work/queue-285/BUILD.md index cabb30fde..04682c5e7 100644 --- a/agents/magica/work/queue-285/BUILD.md +++ b/agents/magica/work/queue-285/BUILD.md @@ -555,3 +555,1080 @@ No unapproved base test fixture or assertion has been changed. These are static not new gate failures. Resolve fixture authority before CLEAR to avoid a predictable old red. Verification status: NOT RUN; Rockerduck holds the slot. + +## B1 fixture preparation (2026-10-24T10:54:55Z, mos-1581, sage-4067) + +No tests/npm/queue verbs executed. First independent pass adds ten credential-data +lines in six files; no deletion, original byte change, assertion change or new import. +Each reference reuses the fixture's existing private dummy coder gitea ref, not a +new token or a credential store file. + +Library-seats companion fixtures statically require gitea under the unchanged +creative definition/exact-match rule (business.mjs checkCredentialMap): +- M7/M8: library seats come from library businesses, creative seats from their creative definitions +- M8: a business with no library project puts no seat in either set +Each creates companion bean with no reference, and loadSeatSets calls loadBusiness +before deciding whether any project is a library. Thus the second needs bean's +gitea too, while its chief still needs none. Both receive only a credential line. + +Direct-reader additions so far: business library-projects-protected, CLI +case2-install, control-board creative-hidden, Discord library-seat. Synthetic +broker/mirror/project objects that never call the business loader are unchanged. + +Pending exact-scope rulings (magica-0006/0007): inline fixture callbacks need +formatting splits to insert credential data; extra direct-reader creative +fixtures also lack gitea; trackers-boot's setup loop assumes every credential +map contains vikunja, so chief gitea needs a one-line setup guard correction. +No pending edit has been applied. + +Exact partial B1 diff against babae6adc (all additions are credential lines): + +diff --git a/packages/business/tests/business.test.mjs b/packages/business/tests/business.test.mjs +index b79576af8..f78789250 100644 +--- a/packages/business/tests/business.test.mjs ++++ b/packages/business/tests/business.test.mjs +@@ -15,6 +15,7 @@ test('C2-04: library mappings load without roots, remain frozen, and retain mapp + const root = tmp(); t.after(() => rmSync(root, { recursive: true, force: true })); + const doc = businessDoc(root), file = join(root, 'businesses/acme.json'); + doc.projects = { books: mappingEntry(), ...doc.projects }; ++ doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + const loaded = validateBusinessDocument(doc, file, { rolesDir: REPO_ROLES }); + const { checkMapping } = await import('../../library/src/mapping.mjs'); + assert.deepEqual(loaded.projects.books, checkMapping(mappingEntry())); +@@ -42,6 +43,7 @@ test('C2-04: an otherwise valid publish grant cannot name a library repo', async + const root = tmp(); t.after(() => rmSync(root, { recursive: true, force: true })); + const doc = businessDoc(root); + doc.projects.books = { ...mappingEntry(), remote: 'https://git.example/writer/books' }; ++ doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + doc.roles.coder.identity = { name: 'Coder', email: 'coder@example.invalid', gitea: { url: 'https://git.example', user: 'acme-coder' } }; + doc.roles.coder.publish = ['books']; + refuses(() => validateBusinessDocument(doc, join(root, 'businesses/acme.json'), { rolesDir: REPO_ROLES }), /publish.*library/); +diff --git a/packages/business/tests/library-projects-protected.test.mjs b/packages/business/tests/library-projects-protected.test.mjs +index 51672bd7b..d4d09d94f 100644 +--- a/packages/business/tests/library-projects-protected.test.mjs ++++ b/packages/business/tests/library-projects-protected.test.mjs +@@ -14,6 +14,7 @@ function fixture(t) { + books.universes.hybrid = { kind: 'hybrid', name: 'An invented crossover', + sources: ['harbor', 'second'], books: ['Hybrid Book'], playground: [], constraints: [], notes: 'Fixture only' }; + doc.projects = { books, ...doc.projects }; ++ doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + const dir = join(root, 'config'), file = writeJson(join(dir, 'businesses/acme.json'), doc); + const writer = organizationWriter({ dir, rolesDir: rolesCopy(root) }); + // Give both snapshots an existing ordinary committed receipt to preserve. +diff --git a/packages/business/tests/library-seats.test.mjs b/packages/business/tests/library-seats.test.mjs +index 3cd0f86cf..da76fdbc7 100644 +--- a/packages/business/tests/library-seats.test.mjs ++++ b/packages/business/tests/library-seats.test.mjs +@@ -27,7 +27,9 @@ test("M7/M8: library seats come from library businesses, creative seats from the + const { root, dir } = fixture(t); + const doc = businessDoc(root, "acme"); + doc.projects = { books: mappingEntry(), ...doc.projects }; ++ doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + doc.roles.bean = { definition: "companion" }; ++ doc.roles.bean.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + writeJson(join(dir, "businesses", "acme.json"), doc); + const found = sets(dir); + assert.deepEqual([...found.librarySeats].sort(), ["bean", "chief", "coder", "cto", "pm", "reviewer"]); +@@ -39,6 +41,7 @@ test("M8: a business with no library project puts no seat in either set", (t) => + const { root, dir } = fixture(t); + const doc = businessDoc(root, "other"); + doc.roles.bean = { definition: "companion" }; ++ doc.roles.bean.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + writeJson(join(dir, "businesses", "other.json"), doc); + const found = sets(dir); + assert.equal(found.librarySeats.size, 0); +diff --git a/packages/cli/tests/case2-install.test.mjs b/packages/cli/tests/case2-install.test.mjs +index cc1142510..50eb3c2a8 100644 +--- a/packages/cli/tests/case2-install.test.mjs ++++ b/packages/cli/tests/case2-install.test.mjs +@@ -51,6 +51,7 @@ function world(t, { profile = "library" } = {}) { + for (const r of Object.values(doc.roles)) r.vars = { ...r.vars, harness: "pi" }; + if (profile === "library") { + doc.projects.books = { ...mappingEntry(), user: "writer" }; ++ doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + doc.roles.bean = { definition: "companion", vars: { harness: "pi" } }; + doc.roles.chet = { definition: "companion", vars: { harness: "pi" } }; + } else doc.vars["bus.tester"] = true; +@@ -212,6 +213,7 @@ test("the factory install refuses a library project and bus.tester off, before a + const x = world(t, { profile: "factory" }); + const doc = JSON.parse(readFileSync(x.input, "utf8")); + doc.projects.books = { ...mappingEntry(), user: "writer" }; ++ doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + writeJson(x.input, doc); + refused(install(x, { profile: "factory" }), /profile-library-business/); + delete doc.projects.books; +diff --git a/packages/control-board/tests/creative-hidden.test.mjs b/packages/control-board/tests/creative-hidden.test.mjs +index c2abd348e..6ea3ecbea 100644 +--- a/packages/control-board/tests/creative-hidden.test.mjs ++++ b/packages/control-board/tests/creative-hidden.test.mjs +@@ -20,6 +20,7 @@ function fixture(t) { + const dir = join(root, "config"); + const doc = businessDoc(root, "acme"); + doc.projects = { books: mappingEntry(), ...doc.projects }; ++ doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + doc.roles.bean = { definition: "companion" }; + writeJson(join(dir, "businesses", "acme.json"), doc); + const threadDir = join(root, "data", "threads", "acme", "t1", "session"); +diff --git a/packages/discord/tests/library-seat.test.mjs b/packages/discord/tests/library-seat.test.mjs +index 315f69698..e8a3e3caa 100644 +--- a/packages/discord/tests/library-seat.test.mjs ++++ b/packages/discord/tests/library-seat.test.mjs +@@ -31,6 +31,7 @@ function setup(overrides = {}) { + function business(s, { library, role = "sage", definition = "companion" }) { + const doc = businessDoc(s.root, "acme"); + if (library) doc.projects = { books: mappingEntry(), ...doc.projects }; ++ if (library) doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + doc.roles[role] = { definition }; + writeJson(join(s.root, "businesses", "acme.json"), doc); + } + +Exact partial full-file base diff against e0c6142f3 follows. business.test also +contains the previously approved row285 hunks; those are unchanged by B1. The +other five files show only the credential additions. + +diff --git a/packages/business/tests/business.test.mjs b/packages/business/tests/business.test.mjs +index 9d4eda831..f78789250 100644 +--- a/packages/business/tests/business.test.mjs ++++ b/packages/business/tests/business.test.mjs +@@ -6,6 +6,7 @@ import { join } from "node:path"; + import { pathToFileURL } from "node:url"; + import { BusinessError, businessFilePath, configDir, loadBusiness, validateBusinessDocument } from "../src/index.mjs"; + import { THREAD_IDLE_SECONDS } from "../src/business.mjs"; ++import { CREATIVE_DEFINITIONS } from "../src/vocabulary.mjs"; + import { launchDoc, businessDoc, REPO, REPO_ROLES, rolesCopy, tmp, writeJson } from "./helpers.mjs"; + + test('C2-04: library mappings load without roots, remain frozen, and retain mapping refusals', async t => { +@@ -14,6 +15,7 @@ test('C2-04: library mappings load without roots, remain frozen, and retain mapp + const root = tmp(); t.after(() => rmSync(root, { recursive: true, force: true })); + const doc = businessDoc(root), file = join(root, 'businesses/acme.json'); + doc.projects = { books: mappingEntry(), ...doc.projects }; ++ doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + const loaded = validateBusinessDocument(doc, file, { rolesDir: REPO_ROLES }); + const { checkMapping } = await import('../../library/src/mapping.mjs'); + assert.deepEqual(loaded.projects.books, checkMapping(mappingEntry())); +@@ -41,6 +43,7 @@ test('C2-04: an otherwise valid publish grant cannot name a library repo', async + const root = tmp(); t.after(() => rmSync(root, { recursive: true, force: true })); + const doc = businessDoc(root); + doc.projects.books = { ...mappingEntry(), remote: 'https://git.example/writer/books' }; ++ doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + doc.roles.coder.identity = { name: 'Coder', email: 'coder@example.invalid', gitea: { url: 'https://git.example', user: 'acme-coder' } }; + doc.roles.coder.publish = ['books']; + refuses(() => validateBusinessDocument(doc, join(root, 'businesses/acme.json'), { rolesDir: REPO_ROLES }), /publish.*library/); +@@ -273,28 +276,58 @@ test("the shipped example refuses as written and validates once filled in", () = + assert.equal(id, 15); + }); + ++// Row 285 (C2-08b, #1790, mos-1330): a creative definition declares gitea, so its instance names ++// the reference. The store form needs no token file, and the bus reads a missing one as no token. ++const CREATIVE_GITEA = Object.freeze({ gitea: Object.freeze({ store: true, rotateBy: "2099-01-01" }) }); ++ + // Row 213 (C2-08, #1714), deviation D1: the persona vars sit on a creative instance only, and + // both or neither. The var checks themselves are vars.mjs's. + test("persona vars: agent layer, creative definition only, both or neither", () => { + const sha = "a".repeat(64); + const persona = { "persona.name": "Bean", "persona.sha256": sha }; +- const business = check((d) => { d.roles.bean = { definition: "companion", vars: { ...persona } }; }); ++ const business = check((d) => { d.roles.bean = { definition: "companion", credentials: CREATIVE_GITEA, vars: { ...persona } }; }); + assert.deepEqual({ ...business.roles.bean.vars }, persona); +- assert.doesNotThrow(() => check((d) => { d.roles.bean = { definition: "ideas" }; })); ++ assert.doesNotThrow(() => check((d) => { d.roles.bean = { definition: "ideas", credentials: CREATIVE_GITEA }; })); + refuses(() => check((d) => { d.vars["persona.name"] = "Bean"; }), /persona\.name can't be set at the business layer/); + refuses(() => check((d) => { d.roles.pm.vars = { ...d.roles.pm.vars, ...persona }; }), + /roles\.pm\.vars: persona vars need a creative definition \(pm isn't one\)/); + refuses(() => check((d) => { d.roles.chief.vars = { ...persona }; }), /chief-of-staff isn't one/); + for (const key of Object.keys(persona)) { +- refuses(() => check((d) => { d.roles.bean = { definition: "research", vars: { [key]: persona[key] } }; }), ++ refuses(() => check((d) => { d.roles.bean = { definition: "research", credentials: CREATIVE_GITEA, vars: { [key]: persona[key] } }; }), + /roles\.bean\.vars: persona\.name and persona\.sha256 are set together or not at all/); + } + for (const [key, value] of [["persona.name", "x".repeat(41)], ["persona.name", "Be\nan"], ["persona.name", "Be\u0007an"], ["persona.name", "Be\u2028an"], ["persona.name", "Be\u2029an"], ["persona.name", 7], + ["persona.sha256", "A".repeat(64)], ["persona.sha256", sha.slice(1)], ["persona.sha256", `${sha}0`]]) { +- refuses(() => check((d) => { d.roles.bean = { definition: "companion", vars: { ...persona, [key]: value } }; }), new RegExp(`variable ${key.replace(".", "\\.")}`), 2); ++ refuses(() => check((d) => { d.roles.bean = { definition: "companion", credentials: CREATIVE_GITEA, vars: { ...persona, [key]: value } }; }), new RegExp(`variable ${key.replace(".", "\\.")}`), 2); ++ } ++}); ++ ++// Row 285 (C2-08b, #1790, mos-1330): the chief and each creative definition declare gitea, so an ++// instance without the reference refuses. Amendment 1 (mos-1580, sage-4060): the chief ++// requires its reference only in a library business; creative definitions always require it. ++test("row 285: a chief or creative instance without credentials.gitea refuses at load", async () => { ++ const { mappingEntry } = await import("../../library/tests/helpers.mjs"); ++ refuses(() => check((d) => { d.projects.books = mappingEntry(); delete d.roles.chief.credentials; }), ++ /chief-library-gitea-required: .*roles\.chief\.credentials/); ++ for (const definition of CREATIVE_DEFINITIONS) { ++ refuses(() => check((d) => { d.roles.bean = { definition }; }), ++ /roles\.bean\.credentials must reference exactly the services its role definition needs \(gitea; got none\)/); ++ assert.doesNotThrow(() => check((d) => { d.roles.bean = { definition, credentials: CREATIVE_GITEA }; }), definition); + } + }); + ++// Row 285 (C2-08b, brief): a creative instance may carry identity and publish, under the rules at ++// business.mjs:155-166, which the grant path reads. The grant itself still refuses it (#giteaRole). ++test("row 285: a creative instance with identity and publish loads", () => { ++ const identity = { name: "Bean", email: "bean@example.test", gitea: { url: "https://git.example", user: "acme-bean" } }; ++ const business = check((d) => { ++ d.projects.stack.remote = "https://git.example/acme/stack"; ++ d.roles.bean = { definition: "companion", credentials: CREATIVE_GITEA, identity, publish: ["stack"] }; ++ }); ++ assert.deepEqual({ ...business.roles.bean.identity, gitea: { ...business.roles.bean.identity.gitea } }, identity); ++ assert.deepEqual([...business.roles.bean.publish], ["stack"]); ++}); ++ + // Row 194 (#1692, D2.5, T17): the per-agent forbid and the retired list. + test("row 194 keep, noHire and retired validate, and a file without them loads as before", () => { + const plain = check(); +diff --git a/packages/business/tests/library-projects-protected.test.mjs b/packages/business/tests/library-projects-protected.test.mjs +index 51672bd7b..d4d09d94f 100644 +--- a/packages/business/tests/library-projects-protected.test.mjs ++++ b/packages/business/tests/library-projects-protected.test.mjs +@@ -14,6 +14,7 @@ function fixture(t) { + books.universes.hybrid = { kind: 'hybrid', name: 'An invented crossover', + sources: ['harbor', 'second'], books: ['Hybrid Book'], playground: [], constraints: [], notes: 'Fixture only' }; + doc.projects = { books, ...doc.projects }; ++ doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + const dir = join(root, 'config'), file = writeJson(join(dir, 'businesses/acme.json'), doc); + const writer = organizationWriter({ dir, rolesDir: rolesCopy(root) }); + // Give both snapshots an existing ordinary committed receipt to preserve. +diff --git a/packages/business/tests/library-seats.test.mjs b/packages/business/tests/library-seats.test.mjs +index 3cd0f86cf..da76fdbc7 100644 +--- a/packages/business/tests/library-seats.test.mjs ++++ b/packages/business/tests/library-seats.test.mjs +@@ -27,7 +27,9 @@ test("M7/M8: library seats come from library businesses, creative seats from the + const { root, dir } = fixture(t); + const doc = businessDoc(root, "acme"); + doc.projects = { books: mappingEntry(), ...doc.projects }; ++ doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + doc.roles.bean = { definition: "companion" }; ++ doc.roles.bean.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + writeJson(join(dir, "businesses", "acme.json"), doc); + const found = sets(dir); + assert.deepEqual([...found.librarySeats].sort(), ["bean", "chief", "coder", "cto", "pm", "reviewer"]); +@@ -39,6 +41,7 @@ test("M8: a business with no library project puts no seat in either set", (t) => + const { root, dir } = fixture(t); + const doc = businessDoc(root, "other"); + doc.roles.bean = { definition: "companion" }; ++ doc.roles.bean.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + writeJson(join(dir, "businesses", "other.json"), doc); + const found = sets(dir); + assert.equal(found.librarySeats.size, 0); +diff --git a/packages/cli/tests/case2-install.test.mjs b/packages/cli/tests/case2-install.test.mjs +index cc1142510..50eb3c2a8 100644 +--- a/packages/cli/tests/case2-install.test.mjs ++++ b/packages/cli/tests/case2-install.test.mjs +@@ -51,6 +51,7 @@ function world(t, { profile = "library" } = {}) { + for (const r of Object.values(doc.roles)) r.vars = { ...r.vars, harness: "pi" }; + if (profile === "library") { + doc.projects.books = { ...mappingEntry(), user: "writer" }; ++ doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + doc.roles.bean = { definition: "companion", vars: { harness: "pi" } }; + doc.roles.chet = { definition: "companion", vars: { harness: "pi" } }; + } else doc.vars["bus.tester"] = true; +@@ -212,6 +213,7 @@ test("the factory install refuses a library project and bus.tester off, before a + const x = world(t, { profile: "factory" }); + const doc = JSON.parse(readFileSync(x.input, "utf8")); + doc.projects.books = { ...mappingEntry(), user: "writer" }; ++ doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + writeJson(x.input, doc); + refused(install(x, { profile: "factory" }), /profile-library-business/); + delete doc.projects.books; +diff --git a/packages/control-board/tests/creative-hidden.test.mjs b/packages/control-board/tests/creative-hidden.test.mjs +index c2abd348e..6ea3ecbea 100644 +--- a/packages/control-board/tests/creative-hidden.test.mjs ++++ b/packages/control-board/tests/creative-hidden.test.mjs +@@ -20,6 +20,7 @@ function fixture(t) { + const dir = join(root, "config"); + const doc = businessDoc(root, "acme"); + doc.projects = { books: mappingEntry(), ...doc.projects }; ++ doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + doc.roles.bean = { definition: "companion" }; + writeJson(join(dir, "businesses", "acme.json"), doc); + const threadDir = join(root, "data", "threads", "acme", "t1", "session"); +diff --git a/packages/discord/tests/library-seat.test.mjs b/packages/discord/tests/library-seat.test.mjs +index 315f69698..e8a3e3caa 100644 +--- a/packages/discord/tests/library-seat.test.mjs ++++ b/packages/discord/tests/library-seat.test.mjs +@@ -31,6 +31,7 @@ function setup(overrides = {}) { + function business(s, { library, role = "sage", definition = "companion" }) { + const doc = businessDoc(s.root, "acme"); + if (library) doc.projects = { books: mappingEntry(), ...doc.projects }; ++ if (library) doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + doc.roles[role] = { definition }; + writeJson(join(s.root, "businesses", "acme.json"), doc); + } + +## Completed B1 data build (2026-10-24T10:59:07Z, mos-1581/1582, sage-4069/4071) + +This supersedes the partial preparation above. No tests/install/queue command run. +Same-line callbacks remain one line, with every original token in order. The only +non-credential edit is the expressly approved trackers-boot setup guard. + +Creative direct readers and their statically traced refusal without credential data: +- packages/control-board/tests/creative-hidden.test.mjs: companion bean; + loadSeatSets -> loadBusiness -> BusinessError exit2, roles.bean.credentials + must reference exactly ... (gitea; got none), wrapped as conversation-business-unreadable. +- packages/discord/tests/library-seat.test.mjs: role-param companion sage, both + library/no-library variants; same BusinessError for roles.sage.credentials, + wrapped by discord CLI as discord-business-unreadable (exit2). +- packages/cli/tests/case2-install.test.mjs: companion bean and chet in library profile; + same BusinessError for roles.bean.credentials (then chet), surfaced by installer + as refused: validate (exit1). The parser has no separate BusinessError.code. +- packages/cli/tests/thread-launch.test.mjs: companion bean in fromSession-refusal, + companion bean + continuity chet in distinct-runs, companion bean in selected-peer + termination; same BusinessError exact-match exit2, wrapped as CliError by bootConfig. +- packages/business/tests/library-seats.test.mjs: both named cases listed above, + same unwrapped BusinessError exact-match exit2 for roles.bean.credentials. +These are static call-chain conclusions, not claims that held-slot tests executed. + +### Tracker put() role-set preservation (mos-1582 B) + +Base e0c6142f3 businessDoc creates exactly pm, cto, coder, reviewer, chief. +The tracker fixture changes project mappings, IDs and vars, not role membership. +Base credential services / old guard reaching put(): +pm: gitea+vikunja -> yes +cto: gitea+vikunja -> yes +coder: gitea+vikunja -> yes +reviewer: gitea+vikunja -> yes +chief: none -> no +Candidate services / new credentials?.vikunja guard reaching put(): +pm: gitea+vikunja -> yes +cto: gitea+vikunja -> yes +coder: gitea+vikunja -> yes +reviewer: gitea+vikunja -> yes +chief: gitea -> no (has no vikunja credential) + +Both sets are exactly [pm, cto, coder, reviewer], and the original put() file/token +arguments and body are byte-identical. The separate tracker.sync put remains unchanged. +The new guard skips only absent vikunja credentials; a present but invalid vikunja +reference still reaches the existing setup call and fails as before. + +### Byte reconstruction and assertion preservation + +For each changed test file, statically remove exactly the added credential statements +and reverse only the allowed tracker guard. The result equals babae6adc byte-for-byte. +This proves every preexisting assertion and other source byte is untouched by B1: +packages/bus/tests/reload.test.mjs: 2 credential insertions; inverse reconstruction matches babae6adc exactly +packages/business/tests/business.test.mjs: 2 credential insertions; inverse reconstruction matches babae6adc exactly +packages/business/tests/cli.test.mjs: 1 credential insertions; inverse reconstruction matches babae6adc exactly +packages/business/tests/instance-settings.test.mjs: 1 credential insertions; inverse reconstruction matches babae6adc exactly +packages/business/tests/library-projects-protected.test.mjs: 1 credential insertions; inverse reconstruction matches babae6adc exactly +packages/business/tests/library-seats.test.mjs: 3 credential insertions; inverse reconstruction matches babae6adc exactly +packages/business/tests/resolve.test.mjs: 1 credential insertions; inverse reconstruction matches babae6adc exactly +packages/cli/tests/case2-install.test.mjs: 4 credential insertions; inverse reconstruction matches babae6adc exactly +packages/cli/tests/launcher.test.mjs: 1 credential insertions; inverse reconstruction matches babae6adc exactly +packages/cli/tests/thread-launch.test.mjs: 4 credential insertions; inverse reconstruction matches babae6adc exactly +packages/cli/tests/trackers-boot.test.mjs: 2 credential insertions; inverse reconstruction matches babae6adc exactly after reversing the sole mos-1582 guard change +packages/control-board/tests/creative-hidden.test.mjs: 2 credential insertions; inverse reconstruction matches babae6adc exactly +packages/discord/tests/library-seat.test.mjs: 2 credential insertions; inverse reconstruction matches babae6adc exactly + +### Exact full-base word diffs + +The following are the requested literal git diff --word-diff=porcelain e0c6142f3 +outputs for every B1 base file. B1 introduces zero removed tokens except the +expressly approved tracker guard. Two files also retain earlier approved r2 hunks: +business.test's preexisting ideas credential insertion replaces the punctuation +token "ideas" with "ideas",; instance-settings retains the mos-1332 gitea service +expectation and mos-1330 creative fixture insertions. Those were present unchanged +at babae6adc before B1; they are not unreported B1 deletions. The incremental +word-diff in b1-incremental-word-diff.txt isolates this, and the inverse byte proof +above checks all other bytes. Full line diff is also retained as b1-base-diff.txt. + +Command: git diff --no-color --word-diff=porcelain e0c6142f3 -- packages/bus/tests/reload.test.mjs +diff --git a/packages/bus/tests/reload.test.mjs b/packages/bus/tests/reload.test.mjs +index 091b37b26..342d578d0 100644 +--- a/packages/bus/tests/reload.test.mjs ++++ b/packages/bus/tests/reload.test.mjs +@@ -194,7 +194,7 @@ test('T14 a file whose project root differs from the boot copy is not reloaded, + }); +~ + +~ + test('T14 a file whose library mapping differs from the boot copy is not reloaded, because the mirror is ensured at start', async (t) => { +~ + const f = await setup(t, doc => { ++doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + doc.projects.books = mappingEntry(); }); +~ + const doc = JSON.parse(readFileSync(f.file, 'utf8')); +~ + doc.projects.books.branch = 'other'; +~ + writeFileSync(f.file, JSON.stringify(doc, null, 2) + '\n', { mode: 0o600 }); +~ +@@ -202,7 +202,7 @@ test('T14 a file whose library mapping differs from the boot copy is not reloade + assert.equal(added.restart, true); +~ + assert.equal(f.reloads.length, 0); +~ + // The same library entry reloads. +~ + const g = await setup(t, doc => { ++doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + doc.projects.books = mappingEntry(); }); +~ + assert.equal(g.ask('instances.set', { revision: g.revision(), instance: 'sec-1', op: 'add', definition: 'security-reviewer' }).restart, false); +~ + assert.equal(g.reloads.length, 1); +~ + }); +~ + +Command: git diff --no-color --word-diff=porcelain e0c6142f3 -- packages/business/tests/business.test.mjs +diff --git a/packages/business/tests/business.test.mjs b/packages/business/tests/business.test.mjs +index 9d4eda831..f78789250 100644 +--- a/packages/business/tests/business.test.mjs ++++ b/packages/business/tests/business.test.mjs +@@ -6,6 +6,7 @@ import { join } from "node:path"; + import { pathToFileURL } from "node:url"; +~ + import { BusinessError, businessFilePath, configDir, loadBusiness, validateBusinessDocument } from "../src/index.mjs"; +~ + import { THREAD_IDLE_SECONDS } from "../src/business.mjs"; +~ ++import { CREATIVE_DEFINITIONS } from "../src/vocabulary.mjs"; +~ + import { launchDoc, businessDoc, REPO, REPO_ROLES, rolesCopy, tmp, writeJson } from "./helpers.mjs"; +~ + +~ + test('C2-04: library mappings load without roots, remain frozen, and retain mapping refusals', async t => { +~ +@@ -14,6 +15,7 @@ test('C2-04: library mappings load without roots, remain frozen, and retain mapp + const root = tmp(); t.after(() => rmSync(root, { recursive: true, force: true })); +~ + const doc = businessDoc(root), file = join(root, 'businesses/acme.json'); +~ + doc.projects = { books: mappingEntry(), ...doc.projects }; +~ + ++doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; +~ + const loaded = validateBusinessDocument(doc, file, { rolesDir: REPO_ROLES }); +~ + const { checkMapping } = await import('../../library/src/mapping.mjs'); +~ + assert.deepEqual(loaded.projects.books, checkMapping(mappingEntry())); +~ +@@ -41,6 +43,7 @@ test('C2-04: an otherwise valid publish grant cannot name a library repo', async + const root = tmp(); t.after(() => rmSync(root, { recursive: true, force: true })); +~ + const doc = businessDoc(root); +~ + doc.projects.books = { ...mappingEntry(), remote: 'https://git.example/writer/books' }; +~ + ++doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; +~ + doc.roles.coder.identity = { name: 'Coder', email: 'coder@example.invalid', gitea: { url: 'https://git.example', user: 'acme-coder' } }; +~ + doc.roles.coder.publish = ['books']; +~ + refuses(() => validateBusinessDocument(doc, join(root, 'businesses/acme.json'), { rolesDir: REPO_ROLES }), /publish.*library/); +~ +@@ -273,28 +276,58 @@ test("the shipped example refuses as written and validates once filled in", () = + assert.equal(id, 15); +~ + }); +~ + +~ ++// Row 285 (C2-08b, #1790, mos-1330): a creative definition declares gitea, so its instance names +~ ++// the reference. The store form needs no token file, and the bus reads a missing one as no token. +~ ++const CREATIVE_GITEA = Object.freeze({ gitea: Object.freeze({ store: true, rotateBy: "2099-01-01" }) }); +~ +~ + // Row 213 (C2-08, #1714), deviation D1: the persona vars sit on a creative instance only, and +~ + // both or neither. The var checks themselves are vars.mjs's. +~ + test("persona vars: agent layer, creative definition only, both or neither", () => { +~ + const sha = "a".repeat(64); +~ + const persona = { "persona.name": "Bean", "persona.sha256": sha }; +~ + const business = check((d) => { d.roles.bean = { definition: "companion", ++credentials: CREATIVE_GITEA, + vars: { ...persona } }; }); +~ + assert.deepEqual({ ...business.roles.bean.vars }, persona); +~ + assert.doesNotThrow(() => check((d) => { d.roles.bean = { definition: +-"ideas" ++"ideas", credentials: CREATIVE_GITEA + }; })); +~ + refuses(() => check((d) => { d.vars["persona.name"] = "Bean"; }), /persona\.name can't be set at the business layer/); +~ + refuses(() => check((d) => { d.roles.pm.vars = { ...d.roles.pm.vars, ...persona }; }), +~ + /roles\.pm\.vars: persona vars need a creative definition \(pm isn't one\)/); +~ + refuses(() => check((d) => { d.roles.chief.vars = { ...persona }; }), /chief-of-staff isn't one/); +~ + for (const key of Object.keys(persona)) { +~ + refuses(() => check((d) => { d.roles.bean = { definition: "research", ++credentials: CREATIVE_GITEA, + vars: { [key]: persona[key] } }; }), +~ + /roles\.bean\.vars: persona\.name and persona\.sha256 are set together or not at all/); +~ + } +~ + for (const [key, value] of [["persona.name", "x".repeat(41)], ["persona.name", "Be\nan"], ["persona.name", "Be\u0007an"], ["persona.name", "Be\u2028an"], ["persona.name", "Be\u2029an"], ["persona.name", 7], +~ + ["persona.sha256", "A".repeat(64)], ["persona.sha256", sha.slice(1)], ["persona.sha256", `${sha}0`]]) { +~ + refuses(() => check((d) => { d.roles.bean = { definition: "companion", ++credentials: CREATIVE_GITEA, + vars: { ...persona, [key]: value } }; }), new RegExp(`variable ${key.replace(".", "\\.")}`), 2); +~ + ++} +~ ++}); +~ +~ ++// Row 285 (C2-08b, #1790, mos-1330): the chief and each creative definition declare gitea, so an +~ ++// instance without the reference refuses. Amendment 1 (mos-1580, sage-4060): the chief +~ ++// requires its reference only in a library business; creative definitions always require it. +~ ++test("row 285: a chief or creative instance without credentials.gitea refuses at load", async () => { +~ ++ const { mappingEntry } = await import("../../library/tests/helpers.mjs"); +~ ++ refuses(() => check((d) => { d.projects.books = mappingEntry(); delete d.roles.chief.credentials; }), +~ ++ /chief-library-gitea-required: .*roles\.chief\.credentials/); +~ ++ for (const definition of CREATIVE_DEFINITIONS) { +~ ++ refuses(() => check((d) => { d.roles.bean = { definition }; }), +~ ++ /roles\.bean\.credentials must reference exactly the services its role definition needs \(gitea; got none\)/); +~ ++ assert.doesNotThrow(() => check((d) => { d.roles.bean = { definition, credentials: CREATIVE_GITEA }; }), definition); +~ + } +~ + }); +~ + +~ ++// Row 285 (C2-08b, brief): a creative instance may carry identity and publish, under the rules at +~ ++// business.mjs:155-166, which the grant path reads. The grant itself still refuses it (#giteaRole). +~ ++test("row 285: a creative instance with identity and publish loads", () => { +~ ++ const identity = { name: "Bean", email: "bean@example.test", gitea: { url: "https://git.example", user: "acme-bean" } }; +~ ++ const business = check((d) => { +~ ++ d.projects.stack.remote = "https://git.example/acme/stack"; +~ ++ d.roles.bean = { definition: "companion", credentials: CREATIVE_GITEA, identity, publish: ["stack"] }; +~ ++ }); +~ ++ assert.deepEqual({ ...business.roles.bean.identity, gitea: { ...business.roles.bean.identity.gitea } }, identity); +~ ++ assert.deepEqual([...business.roles.bean.publish], ["stack"]); +~ ++}); +~ +~ + // Row 194 (#1692, D2.5, T17): the per-agent forbid and the retired list. +~ + test("row 194 keep, noHire and retired validate, and a file without them loads as before", () => { +~ + const plain = check(); +~ + +Command: git diff --no-color --word-diff=porcelain e0c6142f3 -- packages/business/tests/cli.test.mjs +diff --git a/packages/business/tests/cli.test.mjs b/packages/business/tests/cli.test.mjs +index 3aa1d52ee..beb367f8c 100644 +--- a/packages/business/tests/cli.test.mjs ++++ b/packages/business/tests/cli.test.mjs +@@ -11,7 +11,7 @@ const CLI = join(REPO, "packages", "business", "src", "cli.mjs"); + test('C2-04: business validate and resolve skip a library before code projects and credential checks', async t => { +~ + const { mappingEntry } = await import('../../library/tests/helpers.mjs'); +~ + const { rmSync } = await import('node:fs'); +~ + const s = setup(doc => { ++doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + doc.projects = { books: mappingEntry(), ...doc.projects }; }); +~ + t.after(() => rmSync(s.root, { recursive: true, force: true })); +~ + writeJson(projectFilePath(s.doc.projects.stack.root), { projectVersion: 1, id: 'stack', vars: { 'tracker.project': 3 } }); +~ + const checked = run(s, 'validate', 'acme'); +~ + +Command: git diff --no-color --word-diff=porcelain e0c6142f3 -- packages/business/tests/instance-settings.test.mjs +diff --git a/packages/business/tests/instance-settings.test.mjs b/packages/business/tests/instance-settings.test.mjs +index e85366562..54c22e0e0 100644 +--- a/packages/business/tests/instance-settings.test.mjs ++++ b/packages/business/tests/instance-settings.test.mjs +@@ -9,7 +9,7 @@ import { mkdirSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync } + import { join } from "node:path"; +~ + import { instanceSettings } from "../src/instance-settings.mjs"; +~ + import { projectFilePath } from "../src/project.mjs"; +~ + import { businessDoc, ++FAR_DATE, + REPO, rolesCopy, systemConfig, systemFor, tmp, writeJson } from "./helpers.mjs"; +~ + +~ + const CLI = join(REPO, "packages", "business", "src", "cli.mjs"); +~ + const roots = []; +~ +@@ -22,7 +22,7 @@ const LEVELS = { pi: ["off", "minimal", "low", "medium", "high", "xhigh", "max"] + +~ + test('C2-04: instance settings skip a library while checking a later code project', async () => { +~ + const { mappingEntry } = await import('../../library/tests/helpers.mjs'); +~ + const f = fixture(doc => { ++doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + doc.projects = { books: mappingEntry(), ...doc.projects }; }); +~ + writeJson(projectFilePath(f.doc.projects.stack.root), { projectVersion: 1, id: 'stack', vars: { 'tracker.project': 3 } }); +~ + const before = f.settings.read('acme'); +~ + f.settings.set('acme', { revision: before.revision, instance: 'gate-215', op: 'add', definition: 'merge-gate' }, 'jason'); +~ +@@ -286,7 +286,8 @@ test("row 213 a creative instance is the human's: add, vars and definition chang + writeFileSync(join(f.root, "config", "personas", "acme", "bean.md"), text, { mode: 0o600 }); +~ + const persona = { "persona.name": "Bean", "persona.sha256": createHash("sha256").update(text).digest("hex") }; +~ + const view = f.settings.read("acme"); +~ + ++// Row 285 (mos-1332 item 4): the companion definition declares gitea. +~ + assert.deepEqual(view.definitions.companion, { services: +-[], ++["gitea"], + accepted: true }); +~ + const add = { instance: "bean", op: "add", definition: "companion", vars: persona }; +~ + for (const actor of ["chief-of-staff.chief", "tester"]) { +~ + assert.throws(() => f.settings.set("acme", { revision: view.revision, ...add }, actor), e => e.code === "organization-creative-human-only", actor); +~ +@@ -331,8 +332,8 @@ test("row 213 a creative instance is the human's: add, vars and definition chang + // every actor, the human included. The listing's endProtected is that same check for its reader. +~ + test("row 256 T1-T3, T5 the human deletes a creative instance, no other actor does, and the assistant stays", () => { +~ + const f = fixture(doc => { +~ + doc.roles.bean = { definition: +-"companion" ++"companion", credentials: { gitea: { store: true, rotateBy: FAR_DATE } } + }; +~ + doc.roles.muse = { definition: +-"ideas" ++"ideas", credentials: { gitea: { store: true, rotateBy: FAR_DATE } } + }; +~ + doc.roles.assistant = { definition: "assistant", holder: "mos" }; +~ + }); +~ + const del = (instance, actor) => () => f.settings.set("acme", { revision: f.settings.read("acme").revision, instance, op: "delete" }, actor); +~ +@@ -377,7 +378,7 @@ test("row 256 T1-T3, T5 the human deletes a creative instance, no other actor do + // instance can't be kept, launch.by or an arbiter, so the check it reaches is the last-listed one. +~ + test("row 256 T4 the human's delete of the only listed instance, a creative one, is the last-listed refusal", () => { +~ + const f = fixture(doc => { +~ + doc.roles.bean = { definition: +-"companion" ++"companion", credentials: { gitea: { store: true, rotateBy: FAR_DATE } } + }; +~ + doc.launch.instances = ["bean"]; +~ + }); +~ + const bytes = readFileSync(f.file, "utf8"); +~ + +Command: git diff --no-color --word-diff=porcelain e0c6142f3 -- packages/business/tests/library-projects-protected.test.mjs +diff --git a/packages/business/tests/library-projects-protected.test.mjs b/packages/business/tests/library-projects-protected.test.mjs +index 51672bd7b..d4d09d94f 100644 +--- a/packages/business/tests/library-projects-protected.test.mjs ++++ b/packages/business/tests/library-projects-protected.test.mjs +@@ -14,6 +14,7 @@ function fixture(t) { + books.universes.hybrid = { kind: 'hybrid', name: 'An invented crossover', +~ + sources: ['harbor', 'second'], books: ['Hybrid Book'], playground: [], constraints: [], notes: 'Fixture only' }; +~ + doc.projects = { books, ...doc.projects }; +~ + ++doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; +~ + const dir = join(root, 'config'), file = writeJson(join(dir, 'businesses/acme.json'), doc); +~ + const writer = organizationWriter({ dir, rolesDir: rolesCopy(root) }); +~ + // Give both snapshots an existing ordinary committed receipt to preserve. +~ + +Command: git diff --no-color --word-diff=porcelain e0c6142f3 -- packages/business/tests/library-seats.test.mjs +diff --git a/packages/business/tests/library-seats.test.mjs b/packages/business/tests/library-seats.test.mjs +index 3cd0f86cf..da76fdbc7 100644 +--- a/packages/business/tests/library-seats.test.mjs ++++ b/packages/business/tests/library-seats.test.mjs +@@ -27,7 +27,9 @@ test("M7/M8: library seats come from library businesses, creative seats from the + const { root, dir } = fixture(t); +~ + const doc = businessDoc(root, "acme"); +~ + doc.projects = { books: mappingEntry(), ...doc.projects }; +~ + ++doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; +~ + doc.roles.bean = { definition: "companion" }; +~ + ++doc.roles.bean.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; +~ + writeJson(join(dir, "businesses", "acme.json"), doc); +~ + const found = sets(dir); +~ + assert.deepEqual([...found.librarySeats].sort(), ["bean", "chief", "coder", "cto", "pm", "reviewer"]); +~ +@@ -39,6 +41,7 @@ test("M8: a business with no library project puts no seat in either set", (t) => + const { root, dir } = fixture(t); +~ + const doc = businessDoc(root, "other"); +~ + doc.roles.bean = { definition: "companion" }; +~ + ++doc.roles.bean.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; +~ + writeJson(join(dir, "businesses", "other.json"), doc); +~ + const found = sets(dir); +~ + assert.equal(found.librarySeats.size, 0); +~ + +Command: git diff --no-color --word-diff=porcelain e0c6142f3 -- packages/business/tests/resolve.test.mjs +diff --git a/packages/business/tests/resolve.test.mjs b/packages/business/tests/resolve.test.mjs +index b1e50f349..2fa106053 100644 +--- a/packages/business/tests/resolve.test.mjs ++++ b/packages/business/tests/resolve.test.mjs +@@ -31,7 +31,7 @@ const resolve = (s, instance) => resolveInstance({ system: s.system, business: s + test('C2-04: a library first in projects does not hide a later project layer', async t => { +~ + const { mappingEntry } = await import('../../library/tests/helpers.mjs'); +~ + const { rmSync } = await import('node:fs'); +~ + const s = setup(doc => { ++doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + doc.projects = { books: mappingEntry(), ...doc.projects }; }, { vars: { 'tracker.project': 7 } }); +~ + t.after(() => rmSync(s.root, { recursive: true, force: true })); +~ + const result = resolve(s, 'coder'); +~ + assert.equal(result.project, 'stack'); +~ + +Command: git diff --no-color --word-diff=porcelain e0c6142f3 -- packages/cli/tests/case2-install.test.mjs +diff --git a/packages/cli/tests/case2-install.test.mjs b/packages/cli/tests/case2-install.test.mjs +index cc1142510..51b606bda 100644 +--- a/packages/cli/tests/case2-install.test.mjs ++++ b/packages/cli/tests/case2-install.test.mjs +@@ -51,8 +51,11 @@ function world(t, { profile = "library" } = {}) { + for (const r of Object.values(doc.roles)) r.vars = { ...r.vars, harness: "pi" }; +~ + if (profile === "library") { +~ + doc.projects.books = { ...mappingEntry(), user: "writer" }; +~ + ++doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; +~ + doc.roles.bean = { definition: "companion", vars: { harness: "pi" } }; +~ + ++doc.roles.bean.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; +~ + doc.roles.chet = { definition: "companion", vars: { harness: "pi" } }; +~ + ++doc.roles.chet.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; +~ + } else doc.vars["bus.tester"] = true; +~ + const input = writeJson(join(root, "input", "acme.json"), doc); +~ + const staging = join(root, "staging"); +~ +@@ -212,6 +215,7 @@ test("the factory install refuses a library project and bus.tester off, before a + const x = world(t, { profile: "factory" }); +~ + const doc = JSON.parse(readFileSync(x.input, "utf8")); +~ + doc.projects.books = { ...mappingEntry(), user: "writer" }; +~ + ++doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; +~ + writeJson(x.input, doc); +~ + refused(install(x, { profile: "factory" }), /profile-library-business/); +~ + delete doc.projects.books; +~ + +Command: git diff --no-color --word-diff=porcelain e0c6142f3 -- packages/cli/tests/launcher.test.mjs +diff --git a/packages/cli/tests/launcher.test.mjs b/packages/cli/tests/launcher.test.mjs +index baabd843e..eeca18eb0 100644 +--- a/packages/cli/tests/launcher.test.mjs ++++ b/packages/cli/tests/launcher.test.mjs +@@ -1,4 +1,4 @@ + import { ++FAR_DATE, + launchDoc } from "../../business/tests/helpers.mjs"; +~ + // The S6 launcher inside a real bus host: real broker child, real runners +~ + // in the bwrap sandbox, the fake adapter from packages/harness standing in for the +~ + // harness. The chief of staff launches a coder through its `launch` tool (row 119: the +~ +@@ -32,7 +32,7 @@ const skip = sandboxProblem() ?? false; + +~ + test('C2-04: launcher workspace clones code but never the library', { skip, timeout: 30000 }, async t => { +~ + const { mappingEntry } = await import('../../library/tests/helpers.mjs'); +~ + const f = await setup(t, doc => { ++doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + doc.projects = { books: mappingEntry(), ...doc.projects }; return doc; }); +~ + await f.launcher.launchPm(); +~ + const workspace = workspaceDir(f.f.dataRoot, 'acme', 'chief'); +~ + assert.equal(existsSync(join(workspace, 'stack', '.git')), true); +~ +@@ -766,7 +766,7 @@ test("row 64 a role skill that isn't enabled refuses skill-not-enabled, and one + test("row 213 a creative instance's persona is read from /personas: off its digest the launch fails with the mismatch as its reason", { timeout: 30000 }, async (t) => { +~ + const text = "Warm. PERSONA-MARKER-d2\n"; +~ + const given = prepare(t, (doc) => { +~ + doc.roles.bean = { definition: "companion", ++credentials: { gitea: { store: true, rotateBy: FAR_DATE } }, + vars: { harness: "pi", model: "claude-sonnet-5-5", thinking: "high", "persona.name": "Bean", "persona.sha256": createHash("sha256").update(text).digest("hex") } }; +~ + doc.launch.instances.push("bean"); +~ + return doc; +~ + }); +~ + +Command: git diff --no-color --word-diff=porcelain e0c6142f3 -- packages/cli/tests/thread-launch.test.mjs +diff --git a/packages/cli/tests/thread-launch.test.mjs b/packages/cli/tests/thread-launch.test.mjs +index fa3adb866..3e313e49d 100644 +--- a/packages/cli/tests/thread-launch.test.mjs ++++ b/packages/cli/tests/thread-launch.test.mjs +@@ -400,6 +400,7 @@ test("REQ-THREAD-1 REQ-LAUNCH-1 the chief can't launch the assistant outside a t + test('C2-09a: fromSession refuses a creative instance outside a thread without a sandbox', { timeout: 30000 }, async t => { +~ + const prepared = prepare(t, { edit: doc => { +~ + doc.roles.bean = { ...doc.roles.assistant, definition: 'companion' }; +~ + ++doc.roles.bean.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; +~ + doc.launch.instances.push('bean'); +~ + return doc; +~ + } }); +~ +@@ -465,7 +466,9 @@ test('C2-09a: creative thread messages start distinct runs and keep each instanc + const vars = { ...doc.roles.assistant.vars }; +~ + delete doc.roles.assistant; +~ + doc.roles.bean = { definition: 'companion', vars: { ...vars } }; +~ + ++doc.roles.bean.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; +~ + doc.roles.chet = { definition: 'continuity', vars: { ...vars } }; +~ + ++doc.roles.chet.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; +~ + doc.launch.instances = ['coder', 'bean', 'chet']; +~ + return doc; +~ + } }); +~ +@@ -501,6 +504,7 @@ test('C2-09a: creative thread messages start distinct runs and keep each instanc + test('C2-09a: terminating one creative thread does not signal an unselected peer of the same instance', { skip, timeout: 90000 }, async t => { +~ + const prepared = prepare(t, { sonnet: 2, idle: 60, edit: doc => { +~ + doc.roles.bean = { ...doc.roles.assistant, definition: 'companion' }; +~ + ++doc.roles.bean.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; +~ + delete doc.roles.assistant; +~ + doc.launch.instances = ['coder', 'bean']; +~ + return doc; +~ + +Command: git diff --no-color --word-diff=porcelain e0c6142f3 -- packages/cli/tests/trackers-boot.test.mjs +diff --git a/packages/cli/tests/trackers-boot.test.mjs b/packages/cli/tests/trackers-boot.test.mjs +index 8d1a407c6..e7f50c947 100644 +--- a/packages/cli/tests/trackers-boot.test.mjs ++++ b/packages/cli/tests/trackers-boot.test.mjs +@@ -14,7 +14,7 @@ import { fixture, tmp } from "./helpers.mjs"; + +~ + test('C2-04: a library-only business with tracker.baseUrl boots without a project tracker', async t => { +~ + const { mappingEntry } = await import('../../library/tests/helpers.mjs'); +~ + const f = fixture(tmp(t), 'acme', doc => { ++doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + doc.projects = { books: mappingEntry() }; return doc; }); +~ + const warnings = []; +~ + const boot = bootConfig({ system: loadSystem({ env: f.env }), businessId: 'acme', env: f.env, warn: text => warnings.push(text) }); +~ + assert.equal(boot.trackers, undefined); +~ +@@ -40,6 +40,7 @@ test("bootConfig trackers reach the S3 adapter in the real broker child, which g + +~ + const f = fixture(root, "acme", (doc) => { +~ + doc.projects = { books: mappingEntry(), ...doc.projects }; +~ + ++doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; +~ + doc.vars["tracker.baseUrl"] = srv.url; +~ + doc.tracker.sync.botId = bots.sync; +~ + // The chief of staff (row 119) has no tracker bot. +~ +@@ -51,7 +52,7 @@ test("bootConfig trackers reach the S3 adapter in the real broker child, which g + chmodSync(file, 0o600); +~ + }; +~ + put(f.doc.tracker.sync.credentials.vikunja.file, fake.token(bots.sync, SCOPES.sync)); +~ + for (const r of Object.keys(f.doc.roles)) if +-(f.doc.roles[r].credentials) ++(f.doc.roles[r].credentials?.vikunja) + put(f.doc.roles[r].credentials.vikunja.file, fake.token(bots[r], r === "pm" ? SCOPES.pm : SCOPES.worker)); +~ + writeJson(join(root, "project", ".mosaic", "project.json"), { projectVersion: 1, id: "stack", vars: { "tracker.project": project } }); +~ + +~ + mkdirSync(f.dataRoot, { recursive: true, mode: 0o700 }); +~ + +Command: git diff --no-color --word-diff=porcelain e0c6142f3 -- packages/control-board/tests/creative-hidden.test.mjs +diff --git a/packages/control-board/tests/creative-hidden.test.mjs b/packages/control-board/tests/creative-hidden.test.mjs +index c2abd348e..12e16866e 100644 +--- a/packages/control-board/tests/creative-hidden.test.mjs ++++ b/packages/control-board/tests/creative-hidden.test.mjs +@@ -20,7 +20,9 @@ function fixture(t) { + const dir = join(root, "config"); +~ + const doc = businessDoc(root, "acme"); +~ + doc.projects = { books: mappingEntry(), ...doc.projects }; +~ + ++doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; +~ + doc.roles.bean = { definition: "companion" }; +~ + ++doc.roles.bean.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; +~ + writeJson(join(dir, "businesses", "acme.json"), doc); +~ + const threadDir = join(root, "data", "threads", "acme", "t1", "session"); +~ + mkdirSync(threadDir, { recursive: true }); +~ + +Command: git diff --no-color --word-diff=porcelain e0c6142f3 -- packages/discord/tests/library-seat.test.mjs +diff --git a/packages/discord/tests/library-seat.test.mjs b/packages/discord/tests/library-seat.test.mjs +index 315f69698..a7ac869e5 100644 +--- a/packages/discord/tests/library-seat.test.mjs ++++ b/packages/discord/tests/library-seat.test.mjs +@@ -31,7 +31,9 @@ function setup(overrides = {}) { + function business(s, { library, role = "sage", definition = "companion" }) { +~ + const doc = businessDoc(s.root, "acme"); +~ + if (library) doc.projects = { books: mappingEntry(), ...doc.projects }; +~ + ++if (library) doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; +~ + doc.roles[role] = { definition }; +~ + ++doc.roles[role].credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; +~ + writeJson(join(s.root, "businesses", "acme.json"), doc); +~ + } +~ + +~ + +Results: NOT RUN. Candidate is a pushed build for the next CLEAR, not accepted or green. diff --git a/agents/magica/work/queue-285/b1-base-diff.txt b/agents/magica/work/queue-285/b1-base-diff.txt new file mode 100644 index 000000000..4beb84ad6 --- /dev/null +++ b/agents/magica/work/queue-285/b1-base-diff.txt @@ -0,0 +1,365 @@ +diff --git a/packages/bus/tests/reload.test.mjs b/packages/bus/tests/reload.test.mjs +index 091b37b26..342d578d0 100644 +--- a/packages/bus/tests/reload.test.mjs ++++ b/packages/bus/tests/reload.test.mjs +@@ -194,7 +194,7 @@ test('T14 a file whose project root differs from the boot copy is not reloaded, + }); + + test('T14 a file whose library mapping differs from the boot copy is not reloaded, because the mirror is ensured at start', async (t) => { +- const f = await setup(t, doc => { doc.projects.books = mappingEntry(); }); ++ const f = await setup(t, doc => { doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; doc.projects.books = mappingEntry(); }); + const doc = JSON.parse(readFileSync(f.file, 'utf8')); + doc.projects.books.branch = 'other'; + writeFileSync(f.file, JSON.stringify(doc, null, 2) + '\n', { mode: 0o600 }); +@@ -202,7 +202,7 @@ test('T14 a file whose library mapping differs from the boot copy is not reloade + assert.equal(added.restart, true); + assert.equal(f.reloads.length, 0); + // The same library entry reloads. +- const g = await setup(t, doc => { doc.projects.books = mappingEntry(); }); ++ const g = await setup(t, doc => { doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; doc.projects.books = mappingEntry(); }); + assert.equal(g.ask('instances.set', { revision: g.revision(), instance: 'sec-1', op: 'add', definition: 'security-reviewer' }).restart, false); + assert.equal(g.reloads.length, 1); + }); +diff --git a/packages/business/tests/business.test.mjs b/packages/business/tests/business.test.mjs +index 9d4eda831..f78789250 100644 +--- a/packages/business/tests/business.test.mjs ++++ b/packages/business/tests/business.test.mjs +@@ -6,6 +6,7 @@ import { join } from "node:path"; + import { pathToFileURL } from "node:url"; + import { BusinessError, businessFilePath, configDir, loadBusiness, validateBusinessDocument } from "../src/index.mjs"; + import { THREAD_IDLE_SECONDS } from "../src/business.mjs"; ++import { CREATIVE_DEFINITIONS } from "../src/vocabulary.mjs"; + import { launchDoc, businessDoc, REPO, REPO_ROLES, rolesCopy, tmp, writeJson } from "./helpers.mjs"; + + test('C2-04: library mappings load without roots, remain frozen, and retain mapping refusals', async t => { +@@ -14,6 +15,7 @@ test('C2-04: library mappings load without roots, remain frozen, and retain mapp + const root = tmp(); t.after(() => rmSync(root, { recursive: true, force: true })); + const doc = businessDoc(root), file = join(root, 'businesses/acme.json'); + doc.projects = { books: mappingEntry(), ...doc.projects }; ++ doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + const loaded = validateBusinessDocument(doc, file, { rolesDir: REPO_ROLES }); + const { checkMapping } = await import('../../library/src/mapping.mjs'); + assert.deepEqual(loaded.projects.books, checkMapping(mappingEntry())); +@@ -41,6 +43,7 @@ test('C2-04: an otherwise valid publish grant cannot name a library repo', async + const root = tmp(); t.after(() => rmSync(root, { recursive: true, force: true })); + const doc = businessDoc(root); + doc.projects.books = { ...mappingEntry(), remote: 'https://git.example/writer/books' }; ++ doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + doc.roles.coder.identity = { name: 'Coder', email: 'coder@example.invalid', gitea: { url: 'https://git.example', user: 'acme-coder' } }; + doc.roles.coder.publish = ['books']; + refuses(() => validateBusinessDocument(doc, join(root, 'businesses/acme.json'), { rolesDir: REPO_ROLES }), /publish.*library/); +@@ -273,28 +276,58 @@ test("the shipped example refuses as written and validates once filled in", () = + assert.equal(id, 15); + }); + ++// Row 285 (C2-08b, #1790, mos-1330): a creative definition declares gitea, so its instance names ++// the reference. The store form needs no token file, and the bus reads a missing one as no token. ++const CREATIVE_GITEA = Object.freeze({ gitea: Object.freeze({ store: true, rotateBy: "2099-01-01" }) }); ++ + // Row 213 (C2-08, #1714), deviation D1: the persona vars sit on a creative instance only, and + // both or neither. The var checks themselves are vars.mjs's. + test("persona vars: agent layer, creative definition only, both or neither", () => { + const sha = "a".repeat(64); + const persona = { "persona.name": "Bean", "persona.sha256": sha }; +- const business = check((d) => { d.roles.bean = { definition: "companion", vars: { ...persona } }; }); ++ const business = check((d) => { d.roles.bean = { definition: "companion", credentials: CREATIVE_GITEA, vars: { ...persona } }; }); + assert.deepEqual({ ...business.roles.bean.vars }, persona); +- assert.doesNotThrow(() => check((d) => { d.roles.bean = { definition: "ideas" }; })); ++ assert.doesNotThrow(() => check((d) => { d.roles.bean = { definition: "ideas", credentials: CREATIVE_GITEA }; })); + refuses(() => check((d) => { d.vars["persona.name"] = "Bean"; }), /persona\.name can't be set at the business layer/); + refuses(() => check((d) => { d.roles.pm.vars = { ...d.roles.pm.vars, ...persona }; }), + /roles\.pm\.vars: persona vars need a creative definition \(pm isn't one\)/); + refuses(() => check((d) => { d.roles.chief.vars = { ...persona }; }), /chief-of-staff isn't one/); + for (const key of Object.keys(persona)) { +- refuses(() => check((d) => { d.roles.bean = { definition: "research", vars: { [key]: persona[key] } }; }), ++ refuses(() => check((d) => { d.roles.bean = { definition: "research", credentials: CREATIVE_GITEA, vars: { [key]: persona[key] } }; }), + /roles\.bean\.vars: persona\.name and persona\.sha256 are set together or not at all/); + } + for (const [key, value] of [["persona.name", "x".repeat(41)], ["persona.name", "Be\nan"], ["persona.name", "Be\u0007an"], ["persona.name", "Be\u2028an"], ["persona.name", "Be\u2029an"], ["persona.name", 7], + ["persona.sha256", "A".repeat(64)], ["persona.sha256", sha.slice(1)], ["persona.sha256", `${sha}0`]]) { +- refuses(() => check((d) => { d.roles.bean = { definition: "companion", vars: { ...persona, [key]: value } }; }), new RegExp(`variable ${key.replace(".", "\\.")}`), 2); ++ refuses(() => check((d) => { d.roles.bean = { definition: "companion", credentials: CREATIVE_GITEA, vars: { ...persona, [key]: value } }; }), new RegExp(`variable ${key.replace(".", "\\.")}`), 2); ++ } ++}); ++ ++// Row 285 (C2-08b, #1790, mos-1330): the chief and each creative definition declare gitea, so an ++// instance without the reference refuses. Amendment 1 (mos-1580, sage-4060): the chief ++// requires its reference only in a library business; creative definitions always require it. ++test("row 285: a chief or creative instance without credentials.gitea refuses at load", async () => { ++ const { mappingEntry } = await import("../../library/tests/helpers.mjs"); ++ refuses(() => check((d) => { d.projects.books = mappingEntry(); delete d.roles.chief.credentials; }), ++ /chief-library-gitea-required: .*roles\.chief\.credentials/); ++ for (const definition of CREATIVE_DEFINITIONS) { ++ refuses(() => check((d) => { d.roles.bean = { definition }; }), ++ /roles\.bean\.credentials must reference exactly the services its role definition needs \(gitea; got none\)/); ++ assert.doesNotThrow(() => check((d) => { d.roles.bean = { definition, credentials: CREATIVE_GITEA }; }), definition); + } + }); + ++// Row 285 (C2-08b, brief): a creative instance may carry identity and publish, under the rules at ++// business.mjs:155-166, which the grant path reads. The grant itself still refuses it (#giteaRole). ++test("row 285: a creative instance with identity and publish loads", () => { ++ const identity = { name: "Bean", email: "bean@example.test", gitea: { url: "https://git.example", user: "acme-bean" } }; ++ const business = check((d) => { ++ d.projects.stack.remote = "https://git.example/acme/stack"; ++ d.roles.bean = { definition: "companion", credentials: CREATIVE_GITEA, identity, publish: ["stack"] }; ++ }); ++ assert.deepEqual({ ...business.roles.bean.identity, gitea: { ...business.roles.bean.identity.gitea } }, identity); ++ assert.deepEqual([...business.roles.bean.publish], ["stack"]); ++}); ++ + // Row 194 (#1692, D2.5, T17): the per-agent forbid and the retired list. + test("row 194 keep, noHire and retired validate, and a file without them loads as before", () => { + const plain = check(); +diff --git a/packages/business/tests/cli.test.mjs b/packages/business/tests/cli.test.mjs +index 3aa1d52ee..beb367f8c 100644 +--- a/packages/business/tests/cli.test.mjs ++++ b/packages/business/tests/cli.test.mjs +@@ -11,7 +11,7 @@ const CLI = join(REPO, "packages", "business", "src", "cli.mjs"); + test('C2-04: business validate and resolve skip a library before code projects and credential checks', async t => { + const { mappingEntry } = await import('../../library/tests/helpers.mjs'); + const { rmSync } = await import('node:fs'); +- const s = setup(doc => { doc.projects = { books: mappingEntry(), ...doc.projects }; }); ++ const s = setup(doc => { doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; doc.projects = { books: mappingEntry(), ...doc.projects }; }); + t.after(() => rmSync(s.root, { recursive: true, force: true })); + writeJson(projectFilePath(s.doc.projects.stack.root), { projectVersion: 1, id: 'stack', vars: { 'tracker.project': 3 } }); + const checked = run(s, 'validate', 'acme'); +diff --git a/packages/business/tests/instance-settings.test.mjs b/packages/business/tests/instance-settings.test.mjs +index e85366562..54c22e0e0 100644 +--- a/packages/business/tests/instance-settings.test.mjs ++++ b/packages/business/tests/instance-settings.test.mjs +@@ -9,7 +9,7 @@ import { mkdirSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync } + import { join } from "node:path"; + import { instanceSettings } from "../src/instance-settings.mjs"; + import { projectFilePath } from "../src/project.mjs"; +-import { businessDoc, REPO, rolesCopy, systemConfig, systemFor, tmp, writeJson } from "./helpers.mjs"; ++import { businessDoc, FAR_DATE, REPO, rolesCopy, systemConfig, systemFor, tmp, writeJson } from "./helpers.mjs"; + + const CLI = join(REPO, "packages", "business", "src", "cli.mjs"); + const roots = []; +@@ -22,7 +22,7 @@ const LEVELS = { pi: ["off", "minimal", "low", "medium", "high", "xhigh", "max"] + + test('C2-04: instance settings skip a library while checking a later code project', async () => { + const { mappingEntry } = await import('../../library/tests/helpers.mjs'); +- const f = fixture(doc => { doc.projects = { books: mappingEntry(), ...doc.projects }; }); ++ const f = fixture(doc => { doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; doc.projects = { books: mappingEntry(), ...doc.projects }; }); + writeJson(projectFilePath(f.doc.projects.stack.root), { projectVersion: 1, id: 'stack', vars: { 'tracker.project': 3 } }); + const before = f.settings.read('acme'); + f.settings.set('acme', { revision: before.revision, instance: 'gate-215', op: 'add', definition: 'merge-gate' }, 'jason'); +@@ -286,7 +286,8 @@ test("row 213 a creative instance is the human's: add, vars and definition chang + writeFileSync(join(f.root, "config", "personas", "acme", "bean.md"), text, { mode: 0o600 }); + const persona = { "persona.name": "Bean", "persona.sha256": createHash("sha256").update(text).digest("hex") }; + const view = f.settings.read("acme"); +- assert.deepEqual(view.definitions.companion, { services: [], accepted: true }); ++ // Row 285 (mos-1332 item 4): the companion definition declares gitea. ++ assert.deepEqual(view.definitions.companion, { services: ["gitea"], accepted: true }); + const add = { instance: "bean", op: "add", definition: "companion", vars: persona }; + for (const actor of ["chief-of-staff.chief", "tester"]) { + assert.throws(() => f.settings.set("acme", { revision: view.revision, ...add }, actor), e => e.code === "organization-creative-human-only", actor); +@@ -331,8 +332,8 @@ test("row 213 a creative instance is the human's: add, vars and definition chang + // every actor, the human included. The listing's endProtected is that same check for its reader. + test("row 256 T1-T3, T5 the human deletes a creative instance, no other actor does, and the assistant stays", () => { + const f = fixture(doc => { +- doc.roles.bean = { definition: "companion" }; +- doc.roles.muse = { definition: "ideas" }; ++ doc.roles.bean = { definition: "companion", credentials: { gitea: { store: true, rotateBy: FAR_DATE } } }; ++ doc.roles.muse = { definition: "ideas", credentials: { gitea: { store: true, rotateBy: FAR_DATE } } }; + doc.roles.assistant = { definition: "assistant", holder: "mos" }; + }); + const del = (instance, actor) => () => f.settings.set("acme", { revision: f.settings.read("acme").revision, instance, op: "delete" }, actor); +@@ -377,7 +378,7 @@ test("row 256 T1-T3, T5 the human deletes a creative instance, no other actor do + // instance can't be kept, launch.by or an arbiter, so the check it reaches is the last-listed one. + test("row 256 T4 the human's delete of the only listed instance, a creative one, is the last-listed refusal", () => { + const f = fixture(doc => { +- doc.roles.bean = { definition: "companion" }; ++ doc.roles.bean = { definition: "companion", credentials: { gitea: { store: true, rotateBy: FAR_DATE } } }; + doc.launch.instances = ["bean"]; + }); + const bytes = readFileSync(f.file, "utf8"); +diff --git a/packages/business/tests/library-projects-protected.test.mjs b/packages/business/tests/library-projects-protected.test.mjs +index 51672bd7b..d4d09d94f 100644 +--- a/packages/business/tests/library-projects-protected.test.mjs ++++ b/packages/business/tests/library-projects-protected.test.mjs +@@ -14,6 +14,7 @@ function fixture(t) { + books.universes.hybrid = { kind: 'hybrid', name: 'An invented crossover', + sources: ['harbor', 'second'], books: ['Hybrid Book'], playground: [], constraints: [], notes: 'Fixture only' }; + doc.projects = { books, ...doc.projects }; ++ doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + const dir = join(root, 'config'), file = writeJson(join(dir, 'businesses/acme.json'), doc); + const writer = organizationWriter({ dir, rolesDir: rolesCopy(root) }); + // Give both snapshots an existing ordinary committed receipt to preserve. +diff --git a/packages/business/tests/library-seats.test.mjs b/packages/business/tests/library-seats.test.mjs +index 3cd0f86cf..da76fdbc7 100644 +--- a/packages/business/tests/library-seats.test.mjs ++++ b/packages/business/tests/library-seats.test.mjs +@@ -27,7 +27,9 @@ test("M7/M8: library seats come from library businesses, creative seats from the + const { root, dir } = fixture(t); + const doc = businessDoc(root, "acme"); + doc.projects = { books: mappingEntry(), ...doc.projects }; ++ doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + doc.roles.bean = { definition: "companion" }; ++ doc.roles.bean.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + writeJson(join(dir, "businesses", "acme.json"), doc); + const found = sets(dir); + assert.deepEqual([...found.librarySeats].sort(), ["bean", "chief", "coder", "cto", "pm", "reviewer"]); +@@ -39,6 +41,7 @@ test("M8: a business with no library project puts no seat in either set", (t) => + const { root, dir } = fixture(t); + const doc = businessDoc(root, "other"); + doc.roles.bean = { definition: "companion" }; ++ doc.roles.bean.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + writeJson(join(dir, "businesses", "other.json"), doc); + const found = sets(dir); + assert.equal(found.librarySeats.size, 0); +diff --git a/packages/business/tests/resolve.test.mjs b/packages/business/tests/resolve.test.mjs +index b1e50f349..2fa106053 100644 +--- a/packages/business/tests/resolve.test.mjs ++++ b/packages/business/tests/resolve.test.mjs +@@ -31,7 +31,7 @@ const resolve = (s, instance) => resolveInstance({ system: s.system, business: s + test('C2-04: a library first in projects does not hide a later project layer', async t => { + const { mappingEntry } = await import('../../library/tests/helpers.mjs'); + const { rmSync } = await import('node:fs'); +- const s = setup(doc => { doc.projects = { books: mappingEntry(), ...doc.projects }; }, { vars: { 'tracker.project': 7 } }); ++ const s = setup(doc => { doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; doc.projects = { books: mappingEntry(), ...doc.projects }; }, { vars: { 'tracker.project': 7 } }); + t.after(() => rmSync(s.root, { recursive: true, force: true })); + const result = resolve(s, 'coder'); + assert.equal(result.project, 'stack'); +diff --git a/packages/cli/tests/case2-install.test.mjs b/packages/cli/tests/case2-install.test.mjs +index cc1142510..51b606bda 100644 +--- a/packages/cli/tests/case2-install.test.mjs ++++ b/packages/cli/tests/case2-install.test.mjs +@@ -51,8 +51,11 @@ function world(t, { profile = "library" } = {}) { + for (const r of Object.values(doc.roles)) r.vars = { ...r.vars, harness: "pi" }; + if (profile === "library") { + doc.projects.books = { ...mappingEntry(), user: "writer" }; ++ doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + doc.roles.bean = { definition: "companion", vars: { harness: "pi" } }; ++ doc.roles.bean.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + doc.roles.chet = { definition: "companion", vars: { harness: "pi" } }; ++ doc.roles.chet.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + } else doc.vars["bus.tester"] = true; + const input = writeJson(join(root, "input", "acme.json"), doc); + const staging = join(root, "staging"); +@@ -212,6 +215,7 @@ test("the factory install refuses a library project and bus.tester off, before a + const x = world(t, { profile: "factory" }); + const doc = JSON.parse(readFileSync(x.input, "utf8")); + doc.projects.books = { ...mappingEntry(), user: "writer" }; ++ doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + writeJson(x.input, doc); + refused(install(x, { profile: "factory" }), /profile-library-business/); + delete doc.projects.books; +diff --git a/packages/cli/tests/launcher.test.mjs b/packages/cli/tests/launcher.test.mjs +index baabd843e..eeca18eb0 100644 +--- a/packages/cli/tests/launcher.test.mjs ++++ b/packages/cli/tests/launcher.test.mjs +@@ -1,4 +1,4 @@ +-import { launchDoc } from "../../business/tests/helpers.mjs"; ++import { FAR_DATE, launchDoc } from "../../business/tests/helpers.mjs"; + // The S6 launcher inside a real bus host: real broker child, real runners + // in the bwrap sandbox, the fake adapter from packages/harness standing in for the + // harness. The chief of staff launches a coder through its `launch` tool (row 119: the +@@ -32,7 +32,7 @@ const skip = sandboxProblem() ?? false; + + test('C2-04: launcher workspace clones code but never the library', { skip, timeout: 30000 }, async t => { + const { mappingEntry } = await import('../../library/tests/helpers.mjs'); +- const f = await setup(t, doc => { doc.projects = { books: mappingEntry(), ...doc.projects }; return doc; }); ++ const f = await setup(t, doc => { doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; doc.projects = { books: mappingEntry(), ...doc.projects }; return doc; }); + await f.launcher.launchPm(); + const workspace = workspaceDir(f.f.dataRoot, 'acme', 'chief'); + assert.equal(existsSync(join(workspace, 'stack', '.git')), true); +@@ -766,7 +766,7 @@ test("row 64 a role skill that isn't enabled refuses skill-not-enabled, and one + test("row 213 a creative instance's persona is read from /personas: off its digest the launch fails with the mismatch as its reason", { timeout: 30000 }, async (t) => { + const text = "Warm. PERSONA-MARKER-d2\n"; + const given = prepare(t, (doc) => { +- doc.roles.bean = { definition: "companion", vars: { harness: "pi", model: "claude-sonnet-5-5", thinking: "high", "persona.name": "Bean", "persona.sha256": createHash("sha256").update(text).digest("hex") } }; ++ doc.roles.bean = { definition: "companion", credentials: { gitea: { store: true, rotateBy: FAR_DATE } }, vars: { harness: "pi", model: "claude-sonnet-5-5", thinking: "high", "persona.name": "Bean", "persona.sha256": createHash("sha256").update(text).digest("hex") } }; + doc.launch.instances.push("bean"); + return doc; + }); +diff --git a/packages/cli/tests/thread-launch.test.mjs b/packages/cli/tests/thread-launch.test.mjs +index fa3adb866..3e313e49d 100644 +--- a/packages/cli/tests/thread-launch.test.mjs ++++ b/packages/cli/tests/thread-launch.test.mjs +@@ -400,6 +400,7 @@ test("REQ-THREAD-1 REQ-LAUNCH-1 the chief can't launch the assistant outside a t + test('C2-09a: fromSession refuses a creative instance outside a thread without a sandbox', { timeout: 30000 }, async t => { + const prepared = prepare(t, { edit: doc => { + doc.roles.bean = { ...doc.roles.assistant, definition: 'companion' }; ++ doc.roles.bean.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + doc.launch.instances.push('bean'); + return doc; + } }); +@@ -465,7 +466,9 @@ test('C2-09a: creative thread messages start distinct runs and keep each instanc + const vars = { ...doc.roles.assistant.vars }; + delete doc.roles.assistant; + doc.roles.bean = { definition: 'companion', vars: { ...vars } }; ++ doc.roles.bean.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + doc.roles.chet = { definition: 'continuity', vars: { ...vars } }; ++ doc.roles.chet.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + doc.launch.instances = ['coder', 'bean', 'chet']; + return doc; + } }); +@@ -501,6 +504,7 @@ test('C2-09a: creative thread messages start distinct runs and keep each instanc + test('C2-09a: terminating one creative thread does not signal an unselected peer of the same instance', { skip, timeout: 90000 }, async t => { + const prepared = prepare(t, { sonnet: 2, idle: 60, edit: doc => { + doc.roles.bean = { ...doc.roles.assistant, definition: 'companion' }; ++ doc.roles.bean.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + delete doc.roles.assistant; + doc.launch.instances = ['coder', 'bean']; + return doc; +diff --git a/packages/cli/tests/trackers-boot.test.mjs b/packages/cli/tests/trackers-boot.test.mjs +index 8d1a407c6..e7f50c947 100644 +--- a/packages/cli/tests/trackers-boot.test.mjs ++++ b/packages/cli/tests/trackers-boot.test.mjs +@@ -14,7 +14,7 @@ import { fixture, tmp } from "./helpers.mjs"; + + test('C2-04: a library-only business with tracker.baseUrl boots without a project tracker', async t => { + const { mappingEntry } = await import('../../library/tests/helpers.mjs'); +- const f = fixture(tmp(t), 'acme', doc => { doc.projects = { books: mappingEntry() }; return doc; }); ++ const f = fixture(tmp(t), 'acme', doc => { doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; doc.projects = { books: mappingEntry() }; return doc; }); + const warnings = []; + const boot = bootConfig({ system: loadSystem({ env: f.env }), businessId: 'acme', env: f.env, warn: text => warnings.push(text) }); + assert.equal(boot.trackers, undefined); +@@ -40,6 +40,7 @@ test("bootConfig trackers reach the S3 adapter in the real broker child, which g + + const f = fixture(root, "acme", (doc) => { + doc.projects = { books: mappingEntry(), ...doc.projects }; ++ doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + doc.vars["tracker.baseUrl"] = srv.url; + doc.tracker.sync.botId = bots.sync; + // The chief of staff (row 119) has no tracker bot. +@@ -51,7 +52,7 @@ test("bootConfig trackers reach the S3 adapter in the real broker child, which g + chmodSync(file, 0o600); + }; + put(f.doc.tracker.sync.credentials.vikunja.file, fake.token(bots.sync, SCOPES.sync)); +- for (const r of Object.keys(f.doc.roles)) if (f.doc.roles[r].credentials) put(f.doc.roles[r].credentials.vikunja.file, fake.token(bots[r], r === "pm" ? SCOPES.pm : SCOPES.worker)); ++ for (const r of Object.keys(f.doc.roles)) if (f.doc.roles[r].credentials?.vikunja) put(f.doc.roles[r].credentials.vikunja.file, fake.token(bots[r], r === "pm" ? SCOPES.pm : SCOPES.worker)); + writeJson(join(root, "project", ".mosaic", "project.json"), { projectVersion: 1, id: "stack", vars: { "tracker.project": project } }); + + mkdirSync(f.dataRoot, { recursive: true, mode: 0o700 }); +diff --git a/packages/control-board/tests/creative-hidden.test.mjs b/packages/control-board/tests/creative-hidden.test.mjs +index c2abd348e..12e16866e 100644 +--- a/packages/control-board/tests/creative-hidden.test.mjs ++++ b/packages/control-board/tests/creative-hidden.test.mjs +@@ -20,7 +20,9 @@ function fixture(t) { + const dir = join(root, "config"); + const doc = businessDoc(root, "acme"); + doc.projects = { books: mappingEntry(), ...doc.projects }; ++ doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + doc.roles.bean = { definition: "companion" }; ++ doc.roles.bean.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + writeJson(join(dir, "businesses", "acme.json"), doc); + const threadDir = join(root, "data", "threads", "acme", "t1", "session"); + mkdirSync(threadDir, { recursive: true }); +diff --git a/packages/discord/tests/library-seat.test.mjs b/packages/discord/tests/library-seat.test.mjs +index 315f69698..a7ac869e5 100644 +--- a/packages/discord/tests/library-seat.test.mjs ++++ b/packages/discord/tests/library-seat.test.mjs +@@ -31,7 +31,9 @@ function setup(overrides = {}) { + function business(s, { library, role = "sage", definition = "companion" }) { + const doc = businessDoc(s.root, "acme"); + if (library) doc.projects = { books: mappingEntry(), ...doc.projects }; ++ if (library) doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + doc.roles[role] = { definition }; ++ doc.roles[role].credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + writeJson(join(s.root, "businesses", "acme.json"), doc); + } + diff --git a/agents/magica/work/queue-285/b1-base-word-diff.txt b/agents/magica/work/queue-285/b1-base-word-diff.txt new file mode 100644 index 000000000..3d6dc01b4 --- /dev/null +++ b/agents/magica/work/queue-285/b1-base-word-diff.txt @@ -0,0 +1,698 @@ +Command: git diff --no-color --word-diff=porcelain e0c6142f3 -- packages/bus/tests/reload.test.mjs +diff --git a/packages/bus/tests/reload.test.mjs b/packages/bus/tests/reload.test.mjs +index 091b37b26..342d578d0 100644 +--- a/packages/bus/tests/reload.test.mjs ++++ b/packages/bus/tests/reload.test.mjs +@@ -194,7 +194,7 @@ test('T14 a file whose project root differs from the boot copy is not reloaded, + }); +~ + +~ + test('T14 a file whose library mapping differs from the boot copy is not reloaded, because the mirror is ensured at start', async (t) => { +~ + const f = await setup(t, doc => { ++doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + doc.projects.books = mappingEntry(); }); +~ + const doc = JSON.parse(readFileSync(f.file, 'utf8')); +~ + doc.projects.books.branch = 'other'; +~ + writeFileSync(f.file, JSON.stringify(doc, null, 2) + '\n', { mode: 0o600 }); +~ +@@ -202,7 +202,7 @@ test('T14 a file whose library mapping differs from the boot copy is not reloade + assert.equal(added.restart, true); +~ + assert.equal(f.reloads.length, 0); +~ + // The same library entry reloads. +~ + const g = await setup(t, doc => { ++doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + doc.projects.books = mappingEntry(); }); +~ + assert.equal(g.ask('instances.set', { revision: g.revision(), instance: 'sec-1', op: 'add', definition: 'security-reviewer' }).restart, false); +~ + assert.equal(g.reloads.length, 1); +~ + }); +~ + +Command: git diff --no-color --word-diff=porcelain e0c6142f3 -- packages/business/tests/business.test.mjs +diff --git a/packages/business/tests/business.test.mjs b/packages/business/tests/business.test.mjs +index 9d4eda831..f78789250 100644 +--- a/packages/business/tests/business.test.mjs ++++ b/packages/business/tests/business.test.mjs +@@ -6,6 +6,7 @@ import { join } from "node:path"; + import { pathToFileURL } from "node:url"; +~ + import { BusinessError, businessFilePath, configDir, loadBusiness, validateBusinessDocument } from "../src/index.mjs"; +~ + import { THREAD_IDLE_SECONDS } from "../src/business.mjs"; +~ ++import { CREATIVE_DEFINITIONS } from "../src/vocabulary.mjs"; +~ + import { launchDoc, businessDoc, REPO, REPO_ROLES, rolesCopy, tmp, writeJson } from "./helpers.mjs"; +~ + +~ + test('C2-04: library mappings load without roots, remain frozen, and retain mapping refusals', async t => { +~ +@@ -14,6 +15,7 @@ test('C2-04: library mappings load without roots, remain frozen, and retain mapp + const root = tmp(); t.after(() => rmSync(root, { recursive: true, force: true })); +~ + const doc = businessDoc(root), file = join(root, 'businesses/acme.json'); +~ + doc.projects = { books: mappingEntry(), ...doc.projects }; +~ + ++doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; +~ + const loaded = validateBusinessDocument(doc, file, { rolesDir: REPO_ROLES }); +~ + const { checkMapping } = await import('../../library/src/mapping.mjs'); +~ + assert.deepEqual(loaded.projects.books, checkMapping(mappingEntry())); +~ +@@ -41,6 +43,7 @@ test('C2-04: an otherwise valid publish grant cannot name a library repo', async + const root = tmp(); t.after(() => rmSync(root, { recursive: true, force: true })); +~ + const doc = businessDoc(root); +~ + doc.projects.books = { ...mappingEntry(), remote: 'https://git.example/writer/books' }; +~ + ++doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; +~ + doc.roles.coder.identity = { name: 'Coder', email: 'coder@example.invalid', gitea: { url: 'https://git.example', user: 'acme-coder' } }; +~ + doc.roles.coder.publish = ['books']; +~ + refuses(() => validateBusinessDocument(doc, join(root, 'businesses/acme.json'), { rolesDir: REPO_ROLES }), /publish.*library/); +~ +@@ -273,28 +276,58 @@ test("the shipped example refuses as written and validates once filled in", () = + assert.equal(id, 15); +~ + }); +~ + +~ ++// Row 285 (C2-08b, #1790, mos-1330): a creative definition declares gitea, so its instance names +~ ++// the reference. The store form needs no token file, and the bus reads a missing one as no token. +~ ++const CREATIVE_GITEA = Object.freeze({ gitea: Object.freeze({ store: true, rotateBy: "2099-01-01" }) }); +~ +~ + // Row 213 (C2-08, #1714), deviation D1: the persona vars sit on a creative instance only, and +~ + // both or neither. The var checks themselves are vars.mjs's. +~ + test("persona vars: agent layer, creative definition only, both or neither", () => { +~ + const sha = "a".repeat(64); +~ + const persona = { "persona.name": "Bean", "persona.sha256": sha }; +~ + const business = check((d) => { d.roles.bean = { definition: "companion", ++credentials: CREATIVE_GITEA, + vars: { ...persona } }; }); +~ + assert.deepEqual({ ...business.roles.bean.vars }, persona); +~ + assert.doesNotThrow(() => check((d) => { d.roles.bean = { definition: +-"ideas" ++"ideas", credentials: CREATIVE_GITEA + }; })); +~ + refuses(() => check((d) => { d.vars["persona.name"] = "Bean"; }), /persona\.name can't be set at the business layer/); +~ + refuses(() => check((d) => { d.roles.pm.vars = { ...d.roles.pm.vars, ...persona }; }), +~ + /roles\.pm\.vars: persona vars need a creative definition \(pm isn't one\)/); +~ + refuses(() => check((d) => { d.roles.chief.vars = { ...persona }; }), /chief-of-staff isn't one/); +~ + for (const key of Object.keys(persona)) { +~ + refuses(() => check((d) => { d.roles.bean = { definition: "research", ++credentials: CREATIVE_GITEA, + vars: { [key]: persona[key] } }; }), +~ + /roles\.bean\.vars: persona\.name and persona\.sha256 are set together or not at all/); +~ + } +~ + for (const [key, value] of [["persona.name", "x".repeat(41)], ["persona.name", "Be\nan"], ["persona.name", "Be\u0007an"], ["persona.name", "Be\u2028an"], ["persona.name", "Be\u2029an"], ["persona.name", 7], +~ + ["persona.sha256", "A".repeat(64)], ["persona.sha256", sha.slice(1)], ["persona.sha256", `${sha}0`]]) { +~ + refuses(() => check((d) => { d.roles.bean = { definition: "companion", ++credentials: CREATIVE_GITEA, + vars: { ...persona, [key]: value } }; }), new RegExp(`variable ${key.replace(".", "\\.")}`), 2); +~ + ++} +~ ++}); +~ +~ ++// Row 285 (C2-08b, #1790, mos-1330): the chief and each creative definition declare gitea, so an +~ ++// instance without the reference refuses. Amendment 1 (mos-1580, sage-4060): the chief +~ ++// requires its reference only in a library business; creative definitions always require it. +~ ++test("row 285: a chief or creative instance without credentials.gitea refuses at load", async () => { +~ ++ const { mappingEntry } = await import("../../library/tests/helpers.mjs"); +~ ++ refuses(() => check((d) => { d.projects.books = mappingEntry(); delete d.roles.chief.credentials; }), +~ ++ /chief-library-gitea-required: .*roles\.chief\.credentials/); +~ ++ for (const definition of CREATIVE_DEFINITIONS) { +~ ++ refuses(() => check((d) => { d.roles.bean = { definition }; }), +~ ++ /roles\.bean\.credentials must reference exactly the services its role definition needs \(gitea; got none\)/); +~ ++ assert.doesNotThrow(() => check((d) => { d.roles.bean = { definition, credentials: CREATIVE_GITEA }; }), definition); +~ + } +~ + }); +~ + +~ ++// Row 285 (C2-08b, brief): a creative instance may carry identity and publish, under the rules at +~ ++// business.mjs:155-166, which the grant path reads. The grant itself still refuses it (#giteaRole). +~ ++test("row 285: a creative instance with identity and publish loads", () => { +~ ++ const identity = { name: "Bean", email: "bean@example.test", gitea: { url: "https://git.example", user: "acme-bean" } }; +~ ++ const business = check((d) => { +~ ++ d.projects.stack.remote = "https://git.example/acme/stack"; +~ ++ d.roles.bean = { definition: "companion", credentials: CREATIVE_GITEA, identity, publish: ["stack"] }; +~ ++ }); +~ ++ assert.deepEqual({ ...business.roles.bean.identity, gitea: { ...business.roles.bean.identity.gitea } }, identity); +~ ++ assert.deepEqual([...business.roles.bean.publish], ["stack"]); +~ ++}); +~ +~ + // Row 194 (#1692, D2.5, T17): the per-agent forbid and the retired list. +~ + test("row 194 keep, noHire and retired validate, and a file without them loads as before", () => { +~ + const plain = check(); +~ + +Command: git diff --no-color --word-diff=porcelain e0c6142f3 -- packages/business/tests/cli.test.mjs +diff --git a/packages/business/tests/cli.test.mjs b/packages/business/tests/cli.test.mjs +index 3aa1d52ee..beb367f8c 100644 +--- a/packages/business/tests/cli.test.mjs ++++ b/packages/business/tests/cli.test.mjs +@@ -11,7 +11,7 @@ const CLI = join(REPO, "packages", "business", "src", "cli.mjs"); + test('C2-04: business validate and resolve skip a library before code projects and credential checks', async t => { +~ + const { mappingEntry } = await import('../../library/tests/helpers.mjs'); +~ + const { rmSync } = await import('node:fs'); +~ + const s = setup(doc => { ++doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + doc.projects = { books: mappingEntry(), ...doc.projects }; }); +~ + t.after(() => rmSync(s.root, { recursive: true, force: true })); +~ + writeJson(projectFilePath(s.doc.projects.stack.root), { projectVersion: 1, id: 'stack', vars: { 'tracker.project': 3 } }); +~ + const checked = run(s, 'validate', 'acme'); +~ + +Command: git diff --no-color --word-diff=porcelain e0c6142f3 -- packages/business/tests/instance-settings.test.mjs +diff --git a/packages/business/tests/instance-settings.test.mjs b/packages/business/tests/instance-settings.test.mjs +index e85366562..54c22e0e0 100644 +--- a/packages/business/tests/instance-settings.test.mjs ++++ b/packages/business/tests/instance-settings.test.mjs +@@ -9,7 +9,7 @@ import { mkdirSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync } + import { join } from "node:path"; +~ + import { instanceSettings } from "../src/instance-settings.mjs"; +~ + import { projectFilePath } from "../src/project.mjs"; +~ + import { businessDoc, ++FAR_DATE, + REPO, rolesCopy, systemConfig, systemFor, tmp, writeJson } from "./helpers.mjs"; +~ + +~ + const CLI = join(REPO, "packages", "business", "src", "cli.mjs"); +~ + const roots = []; +~ +@@ -22,7 +22,7 @@ const LEVELS = { pi: ["off", "minimal", "low", "medium", "high", "xhigh", "max"] + +~ + test('C2-04: instance settings skip a library while checking a later code project', async () => { +~ + const { mappingEntry } = await import('../../library/tests/helpers.mjs'); +~ + const f = fixture(doc => { ++doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + doc.projects = { books: mappingEntry(), ...doc.projects }; }); +~ + writeJson(projectFilePath(f.doc.projects.stack.root), { projectVersion: 1, id: 'stack', vars: { 'tracker.project': 3 } }); +~ + const before = f.settings.read('acme'); +~ + f.settings.set('acme', { revision: before.revision, instance: 'gate-215', op: 'add', definition: 'merge-gate' }, 'jason'); +~ +@@ -286,7 +286,8 @@ test("row 213 a creative instance is the human's: add, vars and definition chang + writeFileSync(join(f.root, "config", "personas", "acme", "bean.md"), text, { mode: 0o600 }); +~ + const persona = { "persona.name": "Bean", "persona.sha256": createHash("sha256").update(text).digest("hex") }; +~ + const view = f.settings.read("acme"); +~ + ++// Row 285 (mos-1332 item 4): the companion definition declares gitea. +~ + assert.deepEqual(view.definitions.companion, { services: +-[], ++["gitea"], + accepted: true }); +~ + const add = { instance: "bean", op: "add", definition: "companion", vars: persona }; +~ + for (const actor of ["chief-of-staff.chief", "tester"]) { +~ + assert.throws(() => f.settings.set("acme", { revision: view.revision, ...add }, actor), e => e.code === "organization-creative-human-only", actor); +~ +@@ -331,8 +332,8 @@ test("row 213 a creative instance is the human's: add, vars and definition chang + // every actor, the human included. The listing's endProtected is that same check for its reader. +~ + test("row 256 T1-T3, T5 the human deletes a creative instance, no other actor does, and the assistant stays", () => { +~ + const f = fixture(doc => { +~ + doc.roles.bean = { definition: +-"companion" ++"companion", credentials: { gitea: { store: true, rotateBy: FAR_DATE } } + }; +~ + doc.roles.muse = { definition: +-"ideas" ++"ideas", credentials: { gitea: { store: true, rotateBy: FAR_DATE } } + }; +~ + doc.roles.assistant = { definition: "assistant", holder: "mos" }; +~ + }); +~ + const del = (instance, actor) => () => f.settings.set("acme", { revision: f.settings.read("acme").revision, instance, op: "delete" }, actor); +~ +@@ -377,7 +378,7 @@ test("row 256 T1-T3, T5 the human deletes a creative instance, no other actor do + // instance can't be kept, launch.by or an arbiter, so the check it reaches is the last-listed one. +~ + test("row 256 T4 the human's delete of the only listed instance, a creative one, is the last-listed refusal", () => { +~ + const f = fixture(doc => { +~ + doc.roles.bean = { definition: +-"companion" ++"companion", credentials: { gitea: { store: true, rotateBy: FAR_DATE } } + }; +~ + doc.launch.instances = ["bean"]; +~ + }); +~ + const bytes = readFileSync(f.file, "utf8"); +~ + +Command: git diff --no-color --word-diff=porcelain e0c6142f3 -- packages/business/tests/library-projects-protected.test.mjs +diff --git a/packages/business/tests/library-projects-protected.test.mjs b/packages/business/tests/library-projects-protected.test.mjs +index 51672bd7b..d4d09d94f 100644 +--- a/packages/business/tests/library-projects-protected.test.mjs ++++ b/packages/business/tests/library-projects-protected.test.mjs +@@ -14,6 +14,7 @@ function fixture(t) { + books.universes.hybrid = { kind: 'hybrid', name: 'An invented crossover', +~ + sources: ['harbor', 'second'], books: ['Hybrid Book'], playground: [], constraints: [], notes: 'Fixture only' }; +~ + doc.projects = { books, ...doc.projects }; +~ + ++doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; +~ + const dir = join(root, 'config'), file = writeJson(join(dir, 'businesses/acme.json'), doc); +~ + const writer = organizationWriter({ dir, rolesDir: rolesCopy(root) }); +~ + // Give both snapshots an existing ordinary committed receipt to preserve. +~ + +Command: git diff --no-color --word-diff=porcelain e0c6142f3 -- packages/business/tests/library-seats.test.mjs +diff --git a/packages/business/tests/library-seats.test.mjs b/packages/business/tests/library-seats.test.mjs +index 3cd0f86cf..da76fdbc7 100644 +--- a/packages/business/tests/library-seats.test.mjs ++++ b/packages/business/tests/library-seats.test.mjs +@@ -27,7 +27,9 @@ test("M7/M8: library seats come from library businesses, creative seats from the + const { root, dir } = fixture(t); +~ + const doc = businessDoc(root, "acme"); +~ + doc.projects = { books: mappingEntry(), ...doc.projects }; +~ + ++doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; +~ + doc.roles.bean = { definition: "companion" }; +~ + ++doc.roles.bean.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; +~ + writeJson(join(dir, "businesses", "acme.json"), doc); +~ + const found = sets(dir); +~ + assert.deepEqual([...found.librarySeats].sort(), ["bean", "chief", "coder", "cto", "pm", "reviewer"]); +~ +@@ -39,6 +41,7 @@ test("M8: a business with no library project puts no seat in either set", (t) => + const { root, dir } = fixture(t); +~ + const doc = businessDoc(root, "other"); +~ + doc.roles.bean = { definition: "companion" }; +~ + ++doc.roles.bean.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; +~ + writeJson(join(dir, "businesses", "other.json"), doc); +~ + const found = sets(dir); +~ + assert.equal(found.librarySeats.size, 0); +~ + +Command: git diff --no-color --word-diff=porcelain e0c6142f3 -- packages/business/tests/resolve.test.mjs +diff --git a/packages/business/tests/resolve.test.mjs b/packages/business/tests/resolve.test.mjs +index b1e50f349..2fa106053 100644 +--- a/packages/business/tests/resolve.test.mjs ++++ b/packages/business/tests/resolve.test.mjs +@@ -31,7 +31,7 @@ const resolve = (s, instance) => resolveInstance({ system: s.system, business: s + test('C2-04: a library first in projects does not hide a later project layer', async t => { +~ + const { mappingEntry } = await import('../../library/tests/helpers.mjs'); +~ + const { rmSync } = await import('node:fs'); +~ + const s = setup(doc => { ++doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + doc.projects = { books: mappingEntry(), ...doc.projects }; }, { vars: { 'tracker.project': 7 } }); +~ + t.after(() => rmSync(s.root, { recursive: true, force: true })); +~ + const result = resolve(s, 'coder'); +~ + assert.equal(result.project, 'stack'); +~ + +Command: git diff --no-color --word-diff=porcelain e0c6142f3 -- packages/cli/tests/case2-install.test.mjs +diff --git a/packages/cli/tests/case2-install.test.mjs b/packages/cli/tests/case2-install.test.mjs +index cc1142510..51b606bda 100644 +--- a/packages/cli/tests/case2-install.test.mjs ++++ b/packages/cli/tests/case2-install.test.mjs +@@ -51,8 +51,11 @@ function world(t, { profile = "library" } = {}) { + for (const r of Object.values(doc.roles)) r.vars = { ...r.vars, harness: "pi" }; +~ + if (profile === "library") { +~ + doc.projects.books = { ...mappingEntry(), user: "writer" }; +~ + ++doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; +~ + doc.roles.bean = { definition: "companion", vars: { harness: "pi" } }; +~ + ++doc.roles.bean.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; +~ + doc.roles.chet = { definition: "companion", vars: { harness: "pi" } }; +~ + ++doc.roles.chet.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; +~ + } else doc.vars["bus.tester"] = true; +~ + const input = writeJson(join(root, "input", "acme.json"), doc); +~ + const staging = join(root, "staging"); +~ +@@ -212,6 +215,7 @@ test("the factory install refuses a library project and bus.tester off, before a + const x = world(t, { profile: "factory" }); +~ + const doc = JSON.parse(readFileSync(x.input, "utf8")); +~ + doc.projects.books = { ...mappingEntry(), user: "writer" }; +~ + ++doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; +~ + writeJson(x.input, doc); +~ + refused(install(x, { profile: "factory" }), /profile-library-business/); +~ + delete doc.projects.books; +~ + +Command: git diff --no-color --word-diff=porcelain e0c6142f3 -- packages/cli/tests/launcher.test.mjs +diff --git a/packages/cli/tests/launcher.test.mjs b/packages/cli/tests/launcher.test.mjs +index baabd843e..eeca18eb0 100644 +--- a/packages/cli/tests/launcher.test.mjs ++++ b/packages/cli/tests/launcher.test.mjs +@@ -1,4 +1,4 @@ + import { ++FAR_DATE, + launchDoc } from "../../business/tests/helpers.mjs"; +~ + // The S6 launcher inside a real bus host: real broker child, real runners +~ + // in the bwrap sandbox, the fake adapter from packages/harness standing in for the +~ + // harness. The chief of staff launches a coder through its `launch` tool (row 119: the +~ +@@ -32,7 +32,7 @@ const skip = sandboxProblem() ?? false; + +~ + test('C2-04: launcher workspace clones code but never the library', { skip, timeout: 30000 }, async t => { +~ + const { mappingEntry } = await import('../../library/tests/helpers.mjs'); +~ + const f = await setup(t, doc => { ++doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + doc.projects = { books: mappingEntry(), ...doc.projects }; return doc; }); +~ + await f.launcher.launchPm(); +~ + const workspace = workspaceDir(f.f.dataRoot, 'acme', 'chief'); +~ + assert.equal(existsSync(join(workspace, 'stack', '.git')), true); +~ +@@ -766,7 +766,7 @@ test("row 64 a role skill that isn't enabled refuses skill-not-enabled, and one + test("row 213 a creative instance's persona is read from /personas: off its digest the launch fails with the mismatch as its reason", { timeout: 30000 }, async (t) => { +~ + const text = "Warm. PERSONA-MARKER-d2\n"; +~ + const given = prepare(t, (doc) => { +~ + doc.roles.bean = { definition: "companion", ++credentials: { gitea: { store: true, rotateBy: FAR_DATE } }, + vars: { harness: "pi", model: "claude-sonnet-5-5", thinking: "high", "persona.name": "Bean", "persona.sha256": createHash("sha256").update(text).digest("hex") } }; +~ + doc.launch.instances.push("bean"); +~ + return doc; +~ + }); +~ + +Command: git diff --no-color --word-diff=porcelain e0c6142f3 -- packages/cli/tests/thread-launch.test.mjs +diff --git a/packages/cli/tests/thread-launch.test.mjs b/packages/cli/tests/thread-launch.test.mjs +index fa3adb866..3e313e49d 100644 +--- a/packages/cli/tests/thread-launch.test.mjs ++++ b/packages/cli/tests/thread-launch.test.mjs +@@ -400,6 +400,7 @@ test("REQ-THREAD-1 REQ-LAUNCH-1 the chief can't launch the assistant outside a t + test('C2-09a: fromSession refuses a creative instance outside a thread without a sandbox', { timeout: 30000 }, async t => { +~ + const prepared = prepare(t, { edit: doc => { +~ + doc.roles.bean = { ...doc.roles.assistant, definition: 'companion' }; +~ + ++doc.roles.bean.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; +~ + doc.launch.instances.push('bean'); +~ + return doc; +~ + } }); +~ +@@ -465,7 +466,9 @@ test('C2-09a: creative thread messages start distinct runs and keep each instanc + const vars = { ...doc.roles.assistant.vars }; +~ + delete doc.roles.assistant; +~ + doc.roles.bean = { definition: 'companion', vars: { ...vars } }; +~ + ++doc.roles.bean.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; +~ + doc.roles.chet = { definition: 'continuity', vars: { ...vars } }; +~ + ++doc.roles.chet.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; +~ + doc.launch.instances = ['coder', 'bean', 'chet']; +~ + return doc; +~ + } }); +~ +@@ -501,6 +504,7 @@ test('C2-09a: creative thread messages start distinct runs and keep each instanc + test('C2-09a: terminating one creative thread does not signal an unselected peer of the same instance', { skip, timeout: 90000 }, async t => { +~ + const prepared = prepare(t, { sonnet: 2, idle: 60, edit: doc => { +~ + doc.roles.bean = { ...doc.roles.assistant, definition: 'companion' }; +~ + ++doc.roles.bean.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; +~ + delete doc.roles.assistant; +~ + doc.launch.instances = ['coder', 'bean']; +~ + return doc; +~ + +Command: git diff --no-color --word-diff=porcelain e0c6142f3 -- packages/cli/tests/trackers-boot.test.mjs +diff --git a/packages/cli/tests/trackers-boot.test.mjs b/packages/cli/tests/trackers-boot.test.mjs +index 8d1a407c6..e7f50c947 100644 +--- a/packages/cli/tests/trackers-boot.test.mjs ++++ b/packages/cli/tests/trackers-boot.test.mjs +@@ -14,7 +14,7 @@ import { fixture, tmp } from "./helpers.mjs"; + +~ + test('C2-04: a library-only business with tracker.baseUrl boots without a project tracker', async t => { +~ + const { mappingEntry } = await import('../../library/tests/helpers.mjs'); +~ + const f = fixture(tmp(t), 'acme', doc => { ++doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + doc.projects = { books: mappingEntry() }; return doc; }); +~ + const warnings = []; +~ + const boot = bootConfig({ system: loadSystem({ env: f.env }), businessId: 'acme', env: f.env, warn: text => warnings.push(text) }); +~ + assert.equal(boot.trackers, undefined); +~ +@@ -40,6 +40,7 @@ test("bootConfig trackers reach the S3 adapter in the real broker child, which g + +~ + const f = fixture(root, "acme", (doc) => { +~ + doc.projects = { books: mappingEntry(), ...doc.projects }; +~ + ++doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; +~ + doc.vars["tracker.baseUrl"] = srv.url; +~ + doc.tracker.sync.botId = bots.sync; +~ + // The chief of staff (row 119) has no tracker bot. +~ +@@ -51,7 +52,7 @@ test("bootConfig trackers reach the S3 adapter in the real broker child, which g + chmodSync(file, 0o600); +~ + }; +~ + put(f.doc.tracker.sync.credentials.vikunja.file, fake.token(bots.sync, SCOPES.sync)); +~ + for (const r of Object.keys(f.doc.roles)) if +-(f.doc.roles[r].credentials) ++(f.doc.roles[r].credentials?.vikunja) + put(f.doc.roles[r].credentials.vikunja.file, fake.token(bots[r], r === "pm" ? SCOPES.pm : SCOPES.worker)); +~ + writeJson(join(root, "project", ".mosaic", "project.json"), { projectVersion: 1, id: "stack", vars: { "tracker.project": project } }); +~ + +~ + mkdirSync(f.dataRoot, { recursive: true, mode: 0o700 }); +~ + +Command: git diff --no-color --word-diff=porcelain e0c6142f3 -- packages/control-board/tests/creative-hidden.test.mjs +diff --git a/packages/control-board/tests/creative-hidden.test.mjs b/packages/control-board/tests/creative-hidden.test.mjs +index c2abd348e..12e16866e 100644 +--- a/packages/control-board/tests/creative-hidden.test.mjs ++++ b/packages/control-board/tests/creative-hidden.test.mjs +@@ -20,7 +20,9 @@ function fixture(t) { + const dir = join(root, "config"); +~ + const doc = businessDoc(root, "acme"); +~ + doc.projects = { books: mappingEntry(), ...doc.projects }; +~ + ++doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; +~ + doc.roles.bean = { definition: "companion" }; +~ + ++doc.roles.bean.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; +~ + writeJson(join(dir, "businesses", "acme.json"), doc); +~ + const threadDir = join(root, "data", "threads", "acme", "t1", "session"); +~ + mkdirSync(threadDir, { recursive: true }); +~ + +Command: git diff --no-color --word-diff=porcelain e0c6142f3 -- packages/discord/tests/library-seat.test.mjs +diff --git a/packages/discord/tests/library-seat.test.mjs b/packages/discord/tests/library-seat.test.mjs +index 315f69698..a7ac869e5 100644 +--- a/packages/discord/tests/library-seat.test.mjs ++++ b/packages/discord/tests/library-seat.test.mjs +@@ -31,7 +31,9 @@ function setup(overrides = {}) { + function business(s, { library, role = "sage", definition = "companion" }) { +~ + const doc = businessDoc(s.root, "acme"); +~ + if (library) doc.projects = { books: mappingEntry(), ...doc.projects }; +~ + ++if (library) doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; +~ + doc.roles[role] = { definition }; +~ + ++doc.roles[role].credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; +~ + writeJson(join(s.root, "businesses", "acme.json"), doc); +~ + } +~ + +~ diff --git a/agents/magica/work/queue-285/b1-incremental-word-diff.txt b/agents/magica/work/queue-285/b1-incremental-word-diff.txt new file mode 100644 index 000000000..8fbd5ffae --- /dev/null +++ b/agents/magica/work/queue-285/b1-incremental-word-diff.txt @@ -0,0 +1,452 @@ +Command: git diff --no-color --word-diff=porcelain babae6adc -- packages/bus/tests/reload.test.mjs +diff --git a/packages/bus/tests/reload.test.mjs b/packages/bus/tests/reload.test.mjs +index 091b37b26..342d578d0 100644 +--- a/packages/bus/tests/reload.test.mjs ++++ b/packages/bus/tests/reload.test.mjs +@@ -194,7 +194,7 @@ test('T14 a file whose project root differs from the boot copy is not reloaded, + }); +~ + +~ + test('T14 a file whose library mapping differs from the boot copy is not reloaded, because the mirror is ensured at start', async (t) => { +~ + const f = await setup(t, doc => { ++doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + doc.projects.books = mappingEntry(); }); +~ + const doc = JSON.parse(readFileSync(f.file, 'utf8')); +~ + doc.projects.books.branch = 'other'; +~ + writeFileSync(f.file, JSON.stringify(doc, null, 2) + '\n', { mode: 0o600 }); +~ +@@ -202,7 +202,7 @@ test('T14 a file whose library mapping differs from the boot copy is not reloade + assert.equal(added.restart, true); +~ + assert.equal(f.reloads.length, 0); +~ + // The same library entry reloads. +~ + const g = await setup(t, doc => { ++doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + doc.projects.books = mappingEntry(); }); +~ + assert.equal(g.ask('instances.set', { revision: g.revision(), instance: 'sec-1', op: 'add', definition: 'security-reviewer' }).restart, false); +~ + assert.equal(g.reloads.length, 1); +~ + }); +~ + +Command: git diff --no-color --word-diff=porcelain babae6adc -- packages/business/tests/business.test.mjs +diff --git a/packages/business/tests/business.test.mjs b/packages/business/tests/business.test.mjs +index b79576af8..f78789250 100644 +--- a/packages/business/tests/business.test.mjs ++++ b/packages/business/tests/business.test.mjs +@@ -15,6 +15,7 @@ test('C2-04: library mappings load without roots, remain frozen, and retain mapp + const root = tmp(); t.after(() => rmSync(root, { recursive: true, force: true })); +~ + const doc = businessDoc(root), file = join(root, 'businesses/acme.json'); +~ + doc.projects = { books: mappingEntry(), ...doc.projects }; +~ + ++doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; +~ + const loaded = validateBusinessDocument(doc, file, { rolesDir: REPO_ROLES }); +~ + const { checkMapping } = await import('../../library/src/mapping.mjs'); +~ + assert.deepEqual(loaded.projects.books, checkMapping(mappingEntry())); +~ +@@ -42,6 +43,7 @@ test('C2-04: an otherwise valid publish grant cannot name a library repo', async + const root = tmp(); t.after(() => rmSync(root, { recursive: true, force: true })); +~ + const doc = businessDoc(root); +~ + doc.projects.books = { ...mappingEntry(), remote: 'https://git.example/writer/books' }; +~ + ++doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; +~ + doc.roles.coder.identity = { name: 'Coder', email: 'coder@example.invalid', gitea: { url: 'https://git.example', user: 'acme-coder' } }; +~ + doc.roles.coder.publish = ['books']; +~ + refuses(() => validateBusinessDocument(doc, join(root, 'businesses/acme.json'), { rolesDir: REPO_ROLES }), /publish.*library/); +~ + +Command: git diff --no-color --word-diff=porcelain babae6adc -- packages/business/tests/cli.test.mjs +diff --git a/packages/business/tests/cli.test.mjs b/packages/business/tests/cli.test.mjs +index 3aa1d52ee..beb367f8c 100644 +--- a/packages/business/tests/cli.test.mjs ++++ b/packages/business/tests/cli.test.mjs +@@ -11,7 +11,7 @@ const CLI = join(REPO, "packages", "business", "src", "cli.mjs"); + test('C2-04: business validate and resolve skip a library before code projects and credential checks', async t => { +~ + const { mappingEntry } = await import('../../library/tests/helpers.mjs'); +~ + const { rmSync } = await import('node:fs'); +~ + const s = setup(doc => { ++doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + doc.projects = { books: mappingEntry(), ...doc.projects }; }); +~ + t.after(() => rmSync(s.root, { recursive: true, force: true })); +~ + writeJson(projectFilePath(s.doc.projects.stack.root), { projectVersion: 1, id: 'stack', vars: { 'tracker.project': 3 } }); +~ + const checked = run(s, 'validate', 'acme'); +~ + +Command: git diff --no-color --word-diff=porcelain babae6adc -- packages/business/tests/instance-settings.test.mjs +diff --git a/packages/business/tests/instance-settings.test.mjs b/packages/business/tests/instance-settings.test.mjs +index 4b4860633..54c22e0e0 100644 +--- a/packages/business/tests/instance-settings.test.mjs ++++ b/packages/business/tests/instance-settings.test.mjs +@@ -22,7 +22,7 @@ const LEVELS = { pi: ["off", "minimal", "low", "medium", "high", "xhigh", "max"] + +~ + test('C2-04: instance settings skip a library while checking a later code project', async () => { +~ + const { mappingEntry } = await import('../../library/tests/helpers.mjs'); +~ + const f = fixture(doc => { ++doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + doc.projects = { books: mappingEntry(), ...doc.projects }; }); +~ + writeJson(projectFilePath(f.doc.projects.stack.root), { projectVersion: 1, id: 'stack', vars: { 'tracker.project': 3 } }); +~ + const before = f.settings.read('acme'); +~ + f.settings.set('acme', { revision: before.revision, instance: 'gate-215', op: 'add', definition: 'merge-gate' }, 'jason'); +~ + +Command: git diff --no-color --word-diff=porcelain babae6adc -- packages/business/tests/library-projects-protected.test.mjs +diff --git a/packages/business/tests/library-projects-protected.test.mjs b/packages/business/tests/library-projects-protected.test.mjs +index 51672bd7b..d4d09d94f 100644 +--- a/packages/business/tests/library-projects-protected.test.mjs ++++ b/packages/business/tests/library-projects-protected.test.mjs +@@ -14,6 +14,7 @@ function fixture(t) { + books.universes.hybrid = { kind: 'hybrid', name: 'An invented crossover', +~ + sources: ['harbor', 'second'], books: ['Hybrid Book'], playground: [], constraints: [], notes: 'Fixture only' }; +~ + doc.projects = { books, ...doc.projects }; +~ + ++doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; +~ + const dir = join(root, 'config'), file = writeJson(join(dir, 'businesses/acme.json'), doc); +~ + const writer = organizationWriter({ dir, rolesDir: rolesCopy(root) }); +~ + // Give both snapshots an existing ordinary committed receipt to preserve. +~ + +Command: git diff --no-color --word-diff=porcelain babae6adc -- packages/business/tests/library-seats.test.mjs +diff --git a/packages/business/tests/library-seats.test.mjs b/packages/business/tests/library-seats.test.mjs +index 3cd0f86cf..da76fdbc7 100644 +--- a/packages/business/tests/library-seats.test.mjs ++++ b/packages/business/tests/library-seats.test.mjs +@@ -27,7 +27,9 @@ test("M7/M8: library seats come from library businesses, creative seats from the + const { root, dir } = fixture(t); +~ + const doc = businessDoc(root, "acme"); +~ + doc.projects = { books: mappingEntry(), ...doc.projects }; +~ + ++doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; +~ + doc.roles.bean = { definition: "companion" }; +~ + ++doc.roles.bean.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; +~ + writeJson(join(dir, "businesses", "acme.json"), doc); +~ + const found = sets(dir); +~ + assert.deepEqual([...found.librarySeats].sort(), ["bean", "chief", "coder", "cto", "pm", "reviewer"]); +~ +@@ -39,6 +41,7 @@ test("M8: a business with no library project puts no seat in either set", (t) => + const { root, dir } = fixture(t); +~ + const doc = businessDoc(root, "other"); +~ + doc.roles.bean = { definition: "companion" }; +~ + ++doc.roles.bean.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; +~ + writeJson(join(dir, "businesses", "other.json"), doc); +~ + const found = sets(dir); +~ + assert.equal(found.librarySeats.size, 0); +~ + +Command: git diff --no-color --word-diff=porcelain babae6adc -- packages/business/tests/resolve.test.mjs +diff --git a/packages/business/tests/resolve.test.mjs b/packages/business/tests/resolve.test.mjs +index b1e50f349..2fa106053 100644 +--- a/packages/business/tests/resolve.test.mjs ++++ b/packages/business/tests/resolve.test.mjs +@@ -31,7 +31,7 @@ const resolve = (s, instance) => resolveInstance({ system: s.system, business: s + test('C2-04: a library first in projects does not hide a later project layer', async t => { +~ + const { mappingEntry } = await import('../../library/tests/helpers.mjs'); +~ + const { rmSync } = await import('node:fs'); +~ + const s = setup(doc => { ++doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + doc.projects = { books: mappingEntry(), ...doc.projects }; }, { vars: { 'tracker.project': 7 } }); +~ + t.after(() => rmSync(s.root, { recursive: true, force: true })); +~ + const result = resolve(s, 'coder'); +~ + assert.equal(result.project, 'stack'); +~ + +Command: git diff --no-color --word-diff=porcelain babae6adc -- packages/cli/tests/case2-install.test.mjs +diff --git a/packages/cli/tests/case2-install.test.mjs b/packages/cli/tests/case2-install.test.mjs +index cc1142510..51b606bda 100644 +--- a/packages/cli/tests/case2-install.test.mjs ++++ b/packages/cli/tests/case2-install.test.mjs +@@ -51,8 +51,11 @@ function world(t, { profile = "library" } = {}) { + for (const r of Object.values(doc.roles)) r.vars = { ...r.vars, harness: "pi" }; +~ + if (profile === "library") { +~ + doc.projects.books = { ...mappingEntry(), user: "writer" }; +~ + ++doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; +~ + doc.roles.bean = { definition: "companion", vars: { harness: "pi" } }; +~ + ++doc.roles.bean.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; +~ + doc.roles.chet = { definition: "companion", vars: { harness: "pi" } }; +~ + ++doc.roles.chet.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; +~ + } else doc.vars["bus.tester"] = true; +~ + const input = writeJson(join(root, "input", "acme.json"), doc); +~ + const staging = join(root, "staging"); +~ +@@ -212,6 +215,7 @@ test("the factory install refuses a library project and bus.tester off, before a + const x = world(t, { profile: "factory" }); +~ + const doc = JSON.parse(readFileSync(x.input, "utf8")); +~ + doc.projects.books = { ...mappingEntry(), user: "writer" }; +~ + ++doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; +~ + writeJson(x.input, doc); +~ + refused(install(x, { profile: "factory" }), /profile-library-business/); +~ + delete doc.projects.books; +~ + +Command: git diff --no-color --word-diff=porcelain babae6adc -- packages/cli/tests/launcher.test.mjs +diff --git a/packages/cli/tests/launcher.test.mjs b/packages/cli/tests/launcher.test.mjs +index 9e7518ee5..eeca18eb0 100644 +--- a/packages/cli/tests/launcher.test.mjs ++++ b/packages/cli/tests/launcher.test.mjs +@@ -32,7 +32,7 @@ const skip = sandboxProblem() ?? false; + +~ + test('C2-04: launcher workspace clones code but never the library', { skip, timeout: 30000 }, async t => { +~ + const { mappingEntry } = await import('../../library/tests/helpers.mjs'); +~ + const f = await setup(t, doc => { ++doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + doc.projects = { books: mappingEntry(), ...doc.projects }; return doc; }); +~ + await f.launcher.launchPm(); +~ + const workspace = workspaceDir(f.f.dataRoot, 'acme', 'chief'); +~ + assert.equal(existsSync(join(workspace, 'stack', '.git')), true); +~ + +Command: git diff --no-color --word-diff=porcelain babae6adc -- packages/cli/tests/thread-launch.test.mjs +diff --git a/packages/cli/tests/thread-launch.test.mjs b/packages/cli/tests/thread-launch.test.mjs +index fa3adb866..3e313e49d 100644 +--- a/packages/cli/tests/thread-launch.test.mjs ++++ b/packages/cli/tests/thread-launch.test.mjs +@@ -400,6 +400,7 @@ test("REQ-THREAD-1 REQ-LAUNCH-1 the chief can't launch the assistant outside a t + test('C2-09a: fromSession refuses a creative instance outside a thread without a sandbox', { timeout: 30000 }, async t => { +~ + const prepared = prepare(t, { edit: doc => { +~ + doc.roles.bean = { ...doc.roles.assistant, definition: 'companion' }; +~ + ++doc.roles.bean.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; +~ + doc.launch.instances.push('bean'); +~ + return doc; +~ + } }); +~ +@@ -465,7 +466,9 @@ test('C2-09a: creative thread messages start distinct runs and keep each instanc + const vars = { ...doc.roles.assistant.vars }; +~ + delete doc.roles.assistant; +~ + doc.roles.bean = { definition: 'companion', vars: { ...vars } }; +~ + ++doc.roles.bean.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; +~ + doc.roles.chet = { definition: 'continuity', vars: { ...vars } }; +~ + ++doc.roles.chet.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; +~ + doc.launch.instances = ['coder', 'bean', 'chet']; +~ + return doc; +~ + } }); +~ +@@ -501,6 +504,7 @@ test('C2-09a: creative thread messages start distinct runs and keep each instanc + test('C2-09a: terminating one creative thread does not signal an unselected peer of the same instance', { skip, timeout: 90000 }, async t => { +~ + const prepared = prepare(t, { sonnet: 2, idle: 60, edit: doc => { +~ + doc.roles.bean = { ...doc.roles.assistant, definition: 'companion' }; +~ + ++doc.roles.bean.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; +~ + delete doc.roles.assistant; +~ + doc.launch.instances = ['coder', 'bean']; +~ + return doc; +~ + +Command: git diff --no-color --word-diff=porcelain babae6adc -- packages/cli/tests/trackers-boot.test.mjs +diff --git a/packages/cli/tests/trackers-boot.test.mjs b/packages/cli/tests/trackers-boot.test.mjs +index 8d1a407c6..e7f50c947 100644 +--- a/packages/cli/tests/trackers-boot.test.mjs ++++ b/packages/cli/tests/trackers-boot.test.mjs +@@ -14,7 +14,7 @@ import { fixture, tmp } from "./helpers.mjs"; + +~ + test('C2-04: a library-only business with tracker.baseUrl boots without a project tracker', async t => { +~ + const { mappingEntry } = await import('../../library/tests/helpers.mjs'); +~ + const f = fixture(tmp(t), 'acme', doc => { ++doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; + doc.projects = { books: mappingEntry() }; return doc; }); +~ + const warnings = []; +~ + const boot = bootConfig({ system: loadSystem({ env: f.env }), businessId: 'acme', env: f.env, warn: text => warnings.push(text) }); +~ + assert.equal(boot.trackers, undefined); +~ +@@ -40,6 +40,7 @@ test("bootConfig trackers reach the S3 adapter in the real broker child, which g + +~ + const f = fixture(root, "acme", (doc) => { +~ + doc.projects = { books: mappingEntry(), ...doc.projects }; +~ + ++doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; +~ + doc.vars["tracker.baseUrl"] = srv.url; +~ + doc.tracker.sync.botId = bots.sync; +~ + // The chief of staff (row 119) has no tracker bot. +~ +@@ -51,7 +52,7 @@ test("bootConfig trackers reach the S3 adapter in the real broker child, which g + chmodSync(file, 0o600); +~ + }; +~ + put(f.doc.tracker.sync.credentials.vikunja.file, fake.token(bots.sync, SCOPES.sync)); +~ + for (const r of Object.keys(f.doc.roles)) if +-(f.doc.roles[r].credentials) ++(f.doc.roles[r].credentials?.vikunja) + put(f.doc.roles[r].credentials.vikunja.file, fake.token(bots[r], r === "pm" ? SCOPES.pm : SCOPES.worker)); +~ + writeJson(join(root, "project", ".mosaic", "project.json"), { projectVersion: 1, id: "stack", vars: { "tracker.project": project } }); +~ + +~ + mkdirSync(f.dataRoot, { recursive: true, mode: 0o700 }); +~ + +Command: git diff --no-color --word-diff=porcelain babae6adc -- packages/control-board/tests/creative-hidden.test.mjs +diff --git a/packages/control-board/tests/creative-hidden.test.mjs b/packages/control-board/tests/creative-hidden.test.mjs +index c2abd348e..12e16866e 100644 +--- a/packages/control-board/tests/creative-hidden.test.mjs ++++ b/packages/control-board/tests/creative-hidden.test.mjs +@@ -20,7 +20,9 @@ function fixture(t) { + const dir = join(root, "config"); +~ + const doc = businessDoc(root, "acme"); +~ + doc.projects = { books: mappingEntry(), ...doc.projects }; +~ + ++doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; +~ + doc.roles.bean = { definition: "companion" }; +~ + ++doc.roles.bean.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; +~ + writeJson(join(dir, "businesses", "acme.json"), doc); +~ + const threadDir = join(root, "data", "threads", "acme", "t1", "session"); +~ + mkdirSync(threadDir, { recursive: true }); +~ + +Command: git diff --no-color --word-diff=porcelain babae6adc -- packages/discord/tests/library-seat.test.mjs +diff --git a/packages/discord/tests/library-seat.test.mjs b/packages/discord/tests/library-seat.test.mjs +index 315f69698..a7ac869e5 100644 +--- a/packages/discord/tests/library-seat.test.mjs ++++ b/packages/discord/tests/library-seat.test.mjs +@@ -31,7 +31,9 @@ function setup(overrides = {}) { + function business(s, { library, role = "sage", definition = "companion" }) { +~ + const doc = businessDoc(s.root, "acme"); +~ + if (library) doc.projects = { books: mappingEntry(), ...doc.projects }; +~ + ++if (library) doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; +~ + doc.roles[role] = { definition }; +~ + ++doc.roles[role].credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; +~ + writeJson(join(s.root, "businesses", "acme.json"), doc); +~ + } +~ + +~ diff --git a/docs/SESSIONS.md b/docs/SESSIONS.md index d118c6f3a..29190e780 100644 --- a/docs/SESSIONS.md +++ b/docs/SESSIONS.md @@ -1633,3 +1633,5 @@ are never rewritten or removed; corrections are new entries. 2026-10-24T10:32:02Z | Magica | Row285 sage-4048 gate STOP | Own installs complete; bus958/958 zero skip/cancel; business partial80pass/4fail on old case1-business-file.test.mjs, missing chief gitea in committed Case1 document. Stopped before further files; Sage notified magica-0002, no source/old-test edits or mutants. Evidence in agents/magica/work/queue-285/gate. 2026-10-24T10:42:28Z | Magica | Row285 Amendment1 build | mos-1580/sage-4059/4060: chief gitea exact iff library, new three-case file and M10 dual kill plan. Restore own unconditional fixture hunks; Case1 doc/test base-identical. No tests under held slot; own-branch push before CLEAR authorized. Static residual library-fixture needs reported. + +2026-10-24T10:59:43Z | Magica | Row285 B1 fixture build | mos-1581/1582, sage-4069/4071: 26 credential-only insertions across13 direct-reader files; one allowed trackers-boot guard. Inverse reconstruction preserves every preexisting byte/assertion. Tracker put roles remain pm/cto/coder/reviewer, chief skipped without vikunja. Exact base/incremental word diffs in BUILD.md and b1-*.txt. No tests/npm/queue since HOLD; own-branch push before CLEAR. diff --git a/packages/bus/tests/reload.test.mjs b/packages/bus/tests/reload.test.mjs index 091b37b26..342d578d0 100644 --- a/packages/bus/tests/reload.test.mjs +++ b/packages/bus/tests/reload.test.mjs @@ -194,7 +194,7 @@ test('T14 a file whose project root differs from the boot copy is not reloaded, }); test('T14 a file whose library mapping differs from the boot copy is not reloaded, because the mirror is ensured at start', async (t) => { - const f = await setup(t, doc => { doc.projects.books = mappingEntry(); }); + const f = await setup(t, doc => { doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; doc.projects.books = mappingEntry(); }); const doc = JSON.parse(readFileSync(f.file, 'utf8')); doc.projects.books.branch = 'other'; writeFileSync(f.file, JSON.stringify(doc, null, 2) + '\n', { mode: 0o600 }); @@ -202,7 +202,7 @@ test('T14 a file whose library mapping differs from the boot copy is not reloade assert.equal(added.restart, true); assert.equal(f.reloads.length, 0); // The same library entry reloads. - const g = await setup(t, doc => { doc.projects.books = mappingEntry(); }); + const g = await setup(t, doc => { doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; doc.projects.books = mappingEntry(); }); assert.equal(g.ask('instances.set', { revision: g.revision(), instance: 'sec-1', op: 'add', definition: 'security-reviewer' }).restart, false); assert.equal(g.reloads.length, 1); }); diff --git a/packages/business/tests/business.test.mjs b/packages/business/tests/business.test.mjs index b79576af8..f78789250 100644 --- a/packages/business/tests/business.test.mjs +++ b/packages/business/tests/business.test.mjs @@ -15,6 +15,7 @@ test('C2-04: library mappings load without roots, remain frozen, and retain mapp const root = tmp(); t.after(() => rmSync(root, { recursive: true, force: true })); const doc = businessDoc(root), file = join(root, 'businesses/acme.json'); doc.projects = { books: mappingEntry(), ...doc.projects }; + doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; const loaded = validateBusinessDocument(doc, file, { rolesDir: REPO_ROLES }); const { checkMapping } = await import('../../library/src/mapping.mjs'); assert.deepEqual(loaded.projects.books, checkMapping(mappingEntry())); @@ -42,6 +43,7 @@ test('C2-04: an otherwise valid publish grant cannot name a library repo', async const root = tmp(); t.after(() => rmSync(root, { recursive: true, force: true })); const doc = businessDoc(root); doc.projects.books = { ...mappingEntry(), remote: 'https://git.example/writer/books' }; + doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; doc.roles.coder.identity = { name: 'Coder', email: 'coder@example.invalid', gitea: { url: 'https://git.example', user: 'acme-coder' } }; doc.roles.coder.publish = ['books']; refuses(() => validateBusinessDocument(doc, join(root, 'businesses/acme.json'), { rolesDir: REPO_ROLES }), /publish.*library/); diff --git a/packages/business/tests/cli.test.mjs b/packages/business/tests/cli.test.mjs index 3aa1d52ee..beb367f8c 100644 --- a/packages/business/tests/cli.test.mjs +++ b/packages/business/tests/cli.test.mjs @@ -11,7 +11,7 @@ const CLI = join(REPO, "packages", "business", "src", "cli.mjs"); test('C2-04: business validate and resolve skip a library before code projects and credential checks', async t => { const { mappingEntry } = await import('../../library/tests/helpers.mjs'); const { rmSync } = await import('node:fs'); - const s = setup(doc => { doc.projects = { books: mappingEntry(), ...doc.projects }; }); + const s = setup(doc => { doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; doc.projects = { books: mappingEntry(), ...doc.projects }; }); t.after(() => rmSync(s.root, { recursive: true, force: true })); writeJson(projectFilePath(s.doc.projects.stack.root), { projectVersion: 1, id: 'stack', vars: { 'tracker.project': 3 } }); const checked = run(s, 'validate', 'acme'); diff --git a/packages/business/tests/instance-settings.test.mjs b/packages/business/tests/instance-settings.test.mjs index 4b4860633..54c22e0e0 100644 --- a/packages/business/tests/instance-settings.test.mjs +++ b/packages/business/tests/instance-settings.test.mjs @@ -22,7 +22,7 @@ const LEVELS = { pi: ["off", "minimal", "low", "medium", "high", "xhigh", "max"] test('C2-04: instance settings skip a library while checking a later code project', async () => { const { mappingEntry } = await import('../../library/tests/helpers.mjs'); - const f = fixture(doc => { doc.projects = { books: mappingEntry(), ...doc.projects }; }); + const f = fixture(doc => { doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; doc.projects = { books: mappingEntry(), ...doc.projects }; }); writeJson(projectFilePath(f.doc.projects.stack.root), { projectVersion: 1, id: 'stack', vars: { 'tracker.project': 3 } }); const before = f.settings.read('acme'); f.settings.set('acme', { revision: before.revision, instance: 'gate-215', op: 'add', definition: 'merge-gate' }, 'jason'); diff --git a/packages/business/tests/library-projects-protected.test.mjs b/packages/business/tests/library-projects-protected.test.mjs index 51672bd7b..d4d09d94f 100644 --- a/packages/business/tests/library-projects-protected.test.mjs +++ b/packages/business/tests/library-projects-protected.test.mjs @@ -14,6 +14,7 @@ function fixture(t) { books.universes.hybrid = { kind: 'hybrid', name: 'An invented crossover', sources: ['harbor', 'second'], books: ['Hybrid Book'], playground: [], constraints: [], notes: 'Fixture only' }; doc.projects = { books, ...doc.projects }; + doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; const dir = join(root, 'config'), file = writeJson(join(dir, 'businesses/acme.json'), doc); const writer = organizationWriter({ dir, rolesDir: rolesCopy(root) }); // Give both snapshots an existing ordinary committed receipt to preserve. diff --git a/packages/business/tests/library-seats.test.mjs b/packages/business/tests/library-seats.test.mjs index 3cd0f86cf..da76fdbc7 100644 --- a/packages/business/tests/library-seats.test.mjs +++ b/packages/business/tests/library-seats.test.mjs @@ -27,7 +27,9 @@ test("M7/M8: library seats come from library businesses, creative seats from the const { root, dir } = fixture(t); const doc = businessDoc(root, "acme"); doc.projects = { books: mappingEntry(), ...doc.projects }; + doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; doc.roles.bean = { definition: "companion" }; + doc.roles.bean.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; writeJson(join(dir, "businesses", "acme.json"), doc); const found = sets(dir); assert.deepEqual([...found.librarySeats].sort(), ["bean", "chief", "coder", "cto", "pm", "reviewer"]); @@ -39,6 +41,7 @@ test("M8: a business with no library project puts no seat in either set", (t) => const { root, dir } = fixture(t); const doc = businessDoc(root, "other"); doc.roles.bean = { definition: "companion" }; + doc.roles.bean.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; writeJson(join(dir, "businesses", "other.json"), doc); const found = sets(dir); assert.equal(found.librarySeats.size, 0); diff --git a/packages/business/tests/resolve.test.mjs b/packages/business/tests/resolve.test.mjs index b1e50f349..2fa106053 100644 --- a/packages/business/tests/resolve.test.mjs +++ b/packages/business/tests/resolve.test.mjs @@ -31,7 +31,7 @@ const resolve = (s, instance) => resolveInstance({ system: s.system, business: s test('C2-04: a library first in projects does not hide a later project layer', async t => { const { mappingEntry } = await import('../../library/tests/helpers.mjs'); const { rmSync } = await import('node:fs'); - const s = setup(doc => { doc.projects = { books: mappingEntry(), ...doc.projects }; }, { vars: { 'tracker.project': 7 } }); + const s = setup(doc => { doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; doc.projects = { books: mappingEntry(), ...doc.projects }; }, { vars: { 'tracker.project': 7 } }); t.after(() => rmSync(s.root, { recursive: true, force: true })); const result = resolve(s, 'coder'); assert.equal(result.project, 'stack'); diff --git a/packages/cli/tests/case2-install.test.mjs b/packages/cli/tests/case2-install.test.mjs index cc1142510..51b606bda 100644 --- a/packages/cli/tests/case2-install.test.mjs +++ b/packages/cli/tests/case2-install.test.mjs @@ -51,8 +51,11 @@ function world(t, { profile = "library" } = {}) { for (const r of Object.values(doc.roles)) r.vars = { ...r.vars, harness: "pi" }; if (profile === "library") { doc.projects.books = { ...mappingEntry(), user: "writer" }; + doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; doc.roles.bean = { definition: "companion", vars: { harness: "pi" } }; + doc.roles.bean.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; doc.roles.chet = { definition: "companion", vars: { harness: "pi" } }; + doc.roles.chet.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; } else doc.vars["bus.tester"] = true; const input = writeJson(join(root, "input", "acme.json"), doc); const staging = join(root, "staging"); @@ -212,6 +215,7 @@ test("the factory install refuses a library project and bus.tester off, before a const x = world(t, { profile: "factory" }); const doc = JSON.parse(readFileSync(x.input, "utf8")); doc.projects.books = { ...mappingEntry(), user: "writer" }; + doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; writeJson(x.input, doc); refused(install(x, { profile: "factory" }), /profile-library-business/); delete doc.projects.books; diff --git a/packages/cli/tests/launcher.test.mjs b/packages/cli/tests/launcher.test.mjs index 9e7518ee5..eeca18eb0 100644 --- a/packages/cli/tests/launcher.test.mjs +++ b/packages/cli/tests/launcher.test.mjs @@ -32,7 +32,7 @@ const skip = sandboxProblem() ?? false; test('C2-04: launcher workspace clones code but never the library', { skip, timeout: 30000 }, async t => { const { mappingEntry } = await import('../../library/tests/helpers.mjs'); - const f = await setup(t, doc => { doc.projects = { books: mappingEntry(), ...doc.projects }; return doc; }); + const f = await setup(t, doc => { doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; doc.projects = { books: mappingEntry(), ...doc.projects }; return doc; }); await f.launcher.launchPm(); const workspace = workspaceDir(f.f.dataRoot, 'acme', 'chief'); assert.equal(existsSync(join(workspace, 'stack', '.git')), true); diff --git a/packages/cli/tests/thread-launch.test.mjs b/packages/cli/tests/thread-launch.test.mjs index fa3adb866..3e313e49d 100644 --- a/packages/cli/tests/thread-launch.test.mjs +++ b/packages/cli/tests/thread-launch.test.mjs @@ -400,6 +400,7 @@ test("REQ-THREAD-1 REQ-LAUNCH-1 the chief can't launch the assistant outside a t test('C2-09a: fromSession refuses a creative instance outside a thread without a sandbox', { timeout: 30000 }, async t => { const prepared = prepare(t, { edit: doc => { doc.roles.bean = { ...doc.roles.assistant, definition: 'companion' }; + doc.roles.bean.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; doc.launch.instances.push('bean'); return doc; } }); @@ -465,7 +466,9 @@ test('C2-09a: creative thread messages start distinct runs and keep each instanc const vars = { ...doc.roles.assistant.vars }; delete doc.roles.assistant; doc.roles.bean = { definition: 'companion', vars: { ...vars } }; + doc.roles.bean.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; doc.roles.chet = { definition: 'continuity', vars: { ...vars } }; + doc.roles.chet.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; doc.launch.instances = ['coder', 'bean', 'chet']; return doc; } }); @@ -501,6 +504,7 @@ test('C2-09a: creative thread messages start distinct runs and keep each instanc test('C2-09a: terminating one creative thread does not signal an unselected peer of the same instance', { skip, timeout: 90000 }, async t => { const prepared = prepare(t, { sonnet: 2, idle: 60, edit: doc => { doc.roles.bean = { ...doc.roles.assistant, definition: 'companion' }; + doc.roles.bean.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; delete doc.roles.assistant; doc.launch.instances = ['coder', 'bean']; return doc; diff --git a/packages/cli/tests/trackers-boot.test.mjs b/packages/cli/tests/trackers-boot.test.mjs index 8d1a407c6..e7f50c947 100644 --- a/packages/cli/tests/trackers-boot.test.mjs +++ b/packages/cli/tests/trackers-boot.test.mjs @@ -14,7 +14,7 @@ import { fixture, tmp } from "./helpers.mjs"; test('C2-04: a library-only business with tracker.baseUrl boots without a project tracker', async t => { const { mappingEntry } = await import('../../library/tests/helpers.mjs'); - const f = fixture(tmp(t), 'acme', doc => { doc.projects = { books: mappingEntry() }; return doc; }); + const f = fixture(tmp(t), 'acme', doc => { doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; doc.projects = { books: mappingEntry() }; return doc; }); const warnings = []; const boot = bootConfig({ system: loadSystem({ env: f.env }), businessId: 'acme', env: f.env, warn: text => warnings.push(text) }); assert.equal(boot.trackers, undefined); @@ -40,6 +40,7 @@ test("bootConfig trackers reach the S3 adapter in the real broker child, which g const f = fixture(root, "acme", (doc) => { doc.projects = { books: mappingEntry(), ...doc.projects }; + doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; doc.vars["tracker.baseUrl"] = srv.url; doc.tracker.sync.botId = bots.sync; // The chief of staff (row 119) has no tracker bot. @@ -51,7 +52,7 @@ test("bootConfig trackers reach the S3 adapter in the real broker child, which g chmodSync(file, 0o600); }; put(f.doc.tracker.sync.credentials.vikunja.file, fake.token(bots.sync, SCOPES.sync)); - for (const r of Object.keys(f.doc.roles)) if (f.doc.roles[r].credentials) put(f.doc.roles[r].credentials.vikunja.file, fake.token(bots[r], r === "pm" ? SCOPES.pm : SCOPES.worker)); + for (const r of Object.keys(f.doc.roles)) if (f.doc.roles[r].credentials?.vikunja) put(f.doc.roles[r].credentials.vikunja.file, fake.token(bots[r], r === "pm" ? SCOPES.pm : SCOPES.worker)); writeJson(join(root, "project", ".mosaic", "project.json"), { projectVersion: 1, id: "stack", vars: { "tracker.project": project } }); mkdirSync(f.dataRoot, { recursive: true, mode: 0o700 }); diff --git a/packages/control-board/tests/creative-hidden.test.mjs b/packages/control-board/tests/creative-hidden.test.mjs index c2abd348e..12e16866e 100644 --- a/packages/control-board/tests/creative-hidden.test.mjs +++ b/packages/control-board/tests/creative-hidden.test.mjs @@ -20,7 +20,9 @@ function fixture(t) { const dir = join(root, "config"); const doc = businessDoc(root, "acme"); doc.projects = { books: mappingEntry(), ...doc.projects }; + doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; doc.roles.bean = { definition: "companion" }; + doc.roles.bean.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; writeJson(join(dir, "businesses", "acme.json"), doc); const threadDir = join(root, "data", "threads", "acme", "t1", "session"); mkdirSync(threadDir, { recursive: true }); diff --git a/packages/discord/tests/library-seat.test.mjs b/packages/discord/tests/library-seat.test.mjs index 315f69698..a7ac869e5 100644 --- a/packages/discord/tests/library-seat.test.mjs +++ b/packages/discord/tests/library-seat.test.mjs @@ -31,7 +31,9 @@ function setup(overrides = {}) { function business(s, { library, role = "sage", definition = "companion" }) { const doc = businessDoc(s.root, "acme"); if (library) doc.projects = { books: mappingEntry(), ...doc.projects }; + if (library) doc.roles.chief.credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; doc.roles[role] = { definition }; + doc.roles[role].credentials = { gitea: { ...doc.roles.coder.credentials.gitea } }; writeJson(join(s.root, "businesses", "acme.json"), doc); }