--- r1/build.patch +++ r2/build.patch @@ -1,8 +1,8 @@ diff --git a/packages/bus/README.md b/packages/bus/README.md -index 4694e08b5..43b9b52a6 100644 +index 3bfd35a55..36965636c 100644 --- a/packages/bus/README.md +++ b/packages/bus/README.md -@@ -208,6 +208,13 @@ requires a strict task ref. `role.launch` is gated unless the business launch +@@ -209,6 +209,13 @@ requires a strict task ref. `role.launch` is gated unless the business launch block's `by` names the acting role and refused when launch state is revoked. S6 still owns launch allowlists, capacity and actual spawning. @@ -17,10 +17,10 @@ | Code | Field | Refused when | diff --git a/packages/bus/src/broker.mjs b/packages/bus/src/broker.mjs -index f10e11b37..d0da6270c 100644 +index ecb18f8b8..239af6921 100644 --- a/packages/bus/src/broker.mjs +++ b/packages/bus/src/broker.mjs -@@ -330,15 +330,20 @@ export class Broker { +@@ -336,15 +336,20 @@ export class Broker { // without the lists is unlimited, as before. The sender's lists are checked first, then the // target's, so a tester or the human sending to a limited role is refused unless listed. Every // path that stores a message from a role checks it: message.send, review.request, review.verdict. @@ -50,7 +50,7 @@ } #decision(s, id) { const d = this.#store.get('SELECT * FROM decisions WHERE id=? AND business=?', id, s.business); -@@ -837,6 +842,10 @@ export class Broker { +@@ -887,6 +892,10 @@ export class Broker { if (!['technical', 'delivery'].includes(domain)) fail('invalid-domain'); const proposedRoute = cls === 'gated' ? 'human' : cls === 'cross-role' ? b.arbiters[domain] : s.role; const route = cls === 'cross-role' && proposedRoute === s.role ? 'human' : proposedRoute; @@ -61,7 +61,7 @@ if (a.supersedes) { const old = this.#decision(s, a.supersedes); if (old.raised_by_run !== s.run || this.#closed(old.id)) fail('supersede-refused'); -@@ -938,6 +947,10 @@ export class Broker { +@@ -988,6 +997,10 @@ export class Broker { d.id, ); // Row 115 (#1609): a close by anyone but the raising run tells the raising role, routed like message.send's. @@ -72,7 +72,7 @@ if (op !== 'seen' && (s.human || s.run !== d.raised_by_run)) { const outcome = op === 'resolved' -@@ -988,7 +1001,7 @@ export class Broker { +@@ -1038,7 +1051,7 @@ export class Broker { if (a.to === 'tester') fail('unknown-role'); } if (a.to !== 'human' && a.to !== 'tester' && !Object.hasOwn(b.roles, a.to)) fail('unknown-role'); @@ -81,7 +81,7 @@ string(a.body, 32768); if ( a.class !== undefined && -@@ -1093,7 +1106,7 @@ export class Broker { +@@ -1143,7 +1156,7 @@ export class Broker { fail(older ? 'stale-commit' : 'unknown-commit'); } // REQ-ASSIST-2 has no approval exception: a review request is a message to the reviewer. @@ -90,7 +90,7 @@ const mid = randomUUID(); // Row 105 (#1599): the run holding the reviewer role, or null when nobody holds it and the // request waits for a claim. #reviewMessage routes on the same holder. -@@ -1136,7 +1149,7 @@ export class Broker { +@@ -1186,7 +1199,7 @@ export class Broker { ) fail('verdict-exists'); // The verdict is a message to the role that asked, so it meets that role's receive list.