PASS A1 example refuses as shipped :: mosaic-stack.json roles.pm.tracker.botId must be a positive integer (got 0)
PASS A2 bot ids fixed, dates still placeholders: refuses :: mosaic-stack.json roles.pm.credentials.gitea.rotateBy must be a date YYYY-MM-DD (got "YYYY-MM-DD")
PASS A3 dates fixed, bot ids 0: refuses :: mosaic-stack.json roles.pm.tracker.botId must be a positive integer (got 0)
PASS A4 both fixed: document validates (shape only; token files are a CLI check)
PASS B1 replace: project roles.<i> beats project beats business :: 20 from project:stack:roles.coder
PASS B2 limits.tools intersects business ∩ project ∩ agent ∩ ceiling :: ["read","write","bash"]
PASS B3 limits.network open can't widen role api-only :: api-only
PASS B4 no project: business value :: 60
PASS B5 default kept when unset
PASS B6 unknown key at business refuses :: ~/filbert-scratch/r36/probe/work/b-G17WAF/config/businesses/acme.json vars: unknown variable "tracker.pollseconds"
PASS B7 agent-only key (harness) at business refuses :: ~/filbert-scratch/r36/probe/work/b-UMkx0k/config/businesses/acme.json vars: variable harness can't be set at the business layer (allowed: agent)
PASS B8 business-only key at agent refuses :: ~/filbert-scratch/r36/probe/work/b-wJNvm9/config/businesses/acme.json roles.coder.vars: variable tracker.baseUrl can't be set at the agent layer (allowed: business)
PASS B9 system key at business refuses :: ~/filbert-scratch/r36/probe/work/b-mx86C2/config/businesses/acme.json vars: variable dataRoot can't be set at the business layer (allowed: system)
PASS B10 __proto__ key refuses :: ~/filbert-scratch/r36/probe/work/b-Jp95ie/config/businesses/acme.json vars: unknown variable "__proto__"
PASS B11 limits.network outside vocabulary refuses :: variable limits.network must be one of: none, api-only, open (got "lan")
PASS B12 limits.authority unknown action refuses :: variable limits.authority entry names an unknown action: "task.delete"
PASS B13 project file: agent key (model) at project vars refuses :: ~/filbert-scratch/r36/probe/work/b-DucpG1/project/.mosaic/project.json vars: variable model can't be set at the project layer (allowed: agent)
PASS B14 project roles.<i>: agent key refuses (project layer) :: ~/filbert-scratch/r36/probe/work/b-xuQ991/project/.mosaic/project.json roles.coder.vars: variable harness can't be set at the project layer (allowed: agent)
PASS B15 project roles names undeclared instance refuses :: project stack sets vars for role instance ghost, which business acme doesn't declare
PASS B16 project roles.constructor refuses :: project stack sets vars for role instance constructor, which business acme doesn't declare
PASS B17 instance constructor refuses :: business acme declares no role instance "constructor"
PASS B17 instance toString refuses :: business acme declares no role instance "toString"
PASS B17 instance __proto__ refuses :: business acme declares no role instance "__proto__"
PASS C1 pm within = definition ∩ business ∩ agent :: {"withinRole":["task.create","role.launch"],"crossRole":["task.scope.change"]}
PASS C2 pm cross keeps scope.change, drops priority.change
PASS C3 classify: dropped action is gated
PASS C4 classify: gated-only action is gated
PASS C5 limits can't grant an action outside the definition (review.verdict for pm)
PASS C6 reviewer keeps only listed actions :: ["review.verdict","message.send"]
PASS C7 classify unknown action refuses :: unknown action: "task.delete"
PASS C8 pm with launch.by=pm: role.launch within
PASS C9 no launch block: pm role.launch gated
PASS C10 launch.by=cto (no role.launch in cto definition) accepted :: cto.launch=set classify(cto, role.launch)=gated classify(pm, role.launch)=gated
PASS C11 limits.authority drops role.launch for launch.by :: launch=set classify=gated
PASS D1 checkCredentialRef on 9 token files: 0 opens :: refs=9 opens=[] problems=0
PASS D2 token reached through a symlinked parent inside a forbidden root refuses :: ["gitea file ~/filbert-scratch/r36/probe/work/b-hFyvw1/outside/t.token: inside ~/filbert-scratch/r36/probe/work/b-hFyvw1/fakerepo; token files live outside the repository and dataRoot"]
PASS D3 mode 640 refuses :: ["vikunja file ~/filbert-scratch/r36/probe/work/b-hFyvw1/fakerepo/sec/t.token: mode 640 gives group or other access; use 600"]
PASS D4 expired vikunja refuses
PASS D5 gitea rotateBy past only warns; env unset only warns :: ["gitea env GITEA_TOKEN: not set in this environment; the launcher must provide it","gitea env GITEA_TOKEN: rotation was due on 2000-01-01"]
PASS D0 hook sees the business file read :: 5
