Repository navigation
[structure-boundaries-v1] 3 — The broker, worker boundary, tool grants and router enforce role and path policies the Vinaya process supplies, so none names a role or a Vinaya path, with identical authorization and isolation #1727
Description
Tier: 1
Project: vinaya, cli
Type: refactor
Objectives
O1. The broker takes a protected-path policy, one function that answers whether a normalized touched path is protected, and keeps the path normalization and the push refusal; Vinaya's implementation holds today's protected administrative path list and is the one the surface-scope check also reads.
O2. The broker takes a launch-record verifier, the launch-record match and tee-recovery functions it calls today, implemented by the process's role dispatch, so the broker imports no dispatch code.
O3. The worker boundary takes a scratch-name policy, one function that answers whether a file name is an exempt scratch name for a given launched role, implemented by Vinaya with today's reviewer scratch rule, so the worker boundary imports no driver code.
O4. A role policy the process supplies carries every other role fact enforcement reads today: the closed role set and each role's tool grants (role-grants.ts), the Operator grant and the role names in the router's refusals (router.ts), the broker's role-to-worker mapping and its role vocabulary (broker.ts), and the worker boundary's session-name parsing, role directory names and concurrent reviewer sibling relationship (worker-boundary.ts); the enforcing files keep their logic and read these facts from the policy, and name no role.
O5. Building a broker, a worker boundary, a tool host or a router without its policies is refused with an error naming the missing policy, never defaulting to no protection or no exemption, and a test asserts each refusal.
O6. Every place that builds one of them obtains its policies from one process-side composition module; parity tests show each role's grants, an unknown role's refusal and every refusal message are the same as before; and the broker's refusal and recovery tests, the isolation contract test, the reviewer-isolation tests, the sandbox conformance suite and the surface-scope check tests pass unchanged in assertions.
O7. Each executable test this task moves, splits or adds has one architecture owner and one suite kind in the test inventory; moved or split tests keep all assertions, expanded cases, fixtures and setup and cleanup semantics, and their inventory entries and input paths are updated in the same change; compiler-resolved dependencies stay authoritative, and fixtures, schemas, doctrine, spawned programs and tree scans keep their modeled input edges or an explained conservative fallback; continuous integration schedules each required test exactly once per intended configuration under its actual runner, working directory and preload; internal tests import existing narrow owner modules, and dedicated compatibility tests exercise public package exports; no required test is deferred and no production behaviour changes. Generic policy enforcement and missing-policy tests live with the kernel owner and use synthetic role labels, never Vinaya's role vocabulary; Vinaya's role, grant, scratch-name and protected-path implementation tests live with the process; parity and integration coverage across the injected policies and every security refusal is kept. The pull request shows four selection plans for the moved area, each with selected test identities and reasons: an isolated implementation edit, a public boundary or policy edit, a fixture, schema or spawned-input edit, and an unrelated edit that selects none of the area's tests unless a stated conservative reason applies.
Documentation
apps/cli/src/lib/broker.ts— the push refusal and the launch-record recovery the policies replace inside it (O1, O2)apps/cli/src/lib/worker-boundary.ts— the reviewer scratch exemption the policy replaces inside it (O3)packages/aeg-core/src/protected-paths.ts— today's protected administrative path list, shared by the broker and the surface-scope check (O1)apps/cli/specs/isolation.md— the isolation guarantees the boundary keeps (O3, O5)
Planner's rationale
Boundary — Give the two enforcement points bounded internal interfaces so they can become kernel code: authorization and isolation logic stay where they are, and only the Vinaya-specific facts they read arrive through required policies. Vinaya's implementations live under harness/processes/vinaya-strict/integration/policies/, in one composition module that every builder calls. The protected-path policy's list is data the process owns, so the doctrine move retargets it to the doctrine's new paths in the same change that moves the doctrine. A legacy role enumeration kept only to read old Log records may stay in the kernel when its comment says so; active authorization facts may not. The policy functions keep today's exact semantics, including path normalization happening before the protected check and the reviewer and security scratch prefixes. Pinned files: apps/cli/src/lib/broker.ts, apps/cli/src/lib/worker-boundary.ts, apps/cli/src/lib/task-tools/role-grants.ts, apps/cli/src/lib/task-tools/router.ts, apps/cli/tests/lib/role-policy-parity.test.ts, harness/processes/vinaya-strict/integration/policies/vinaya-policies.ts, apps/cli/tests/lib/broker/policy-required.test.ts, apps/cli/tests/lib/worker-boundary-policy.test.ts. Out: the protected path list's contents, the scratch rule, the grants and the refusal messages do not change; the broker and boundary files move to the kernel in a later task; the importers of the two modules change only where they build one.
Sizing — One verification story: the security suites pass unchanged, and a build without a policy is refused. Eight pinned files, four new; one failure mode (a refusal or exemption differs). Passes all four tests.
Project(s) + blast radius — Project: vinaya, cli. The tool host and the agent launches build these two objects; their behaviour is re-verified by the unchanged security suites.
Dependency rationale — Depends-on: #1723, #1720 — the role dispatch that implements the launch-record verifier must already sit in the process. Conflicts-with: #1723, #1726 — the first moves the dispatch module the broker imports, and the configuration task edits shared CLI library files these builders read.
Traps to avoid — Do NOT give a policy a default; a missing policy is a refusal. Do NOT change the order of normalization and protection checks. Do NOT copy the protected path list; the surface-scope check and the broker read the one implementation. Do NOT let a file outside the process import the composition module except the builders.
doc-claims: claim anchors that cite the broker's or the boundary's lines are re-pointed if the lines move, so each still resolves.
reader-resolvable-prose: the new files' comments name policies and paths, never a forge number or planning label.
Suggested agent-class — high: authorization and isolation code where a silent default would weaken a guarantee.
Stop-and-escalate — Stop if a builder cannot reach the composition module without a kernel file importing the process. Stop if a security suite needs an assertion change to pass.
Docs to keep coherent — apps/cli/specs/isolation.md (the boundary now reads its exemption from a policy).
atta-labs/process-version-bump: files under the process folder change, so the definition's version in harness/processes/vinaya-strict/process.yaml is raised by one patch step in the same change, with no other edit to it.
Premises
- after [structure-move-v1] 6 — Vinaya's run control, role dispatch, task-contract commands and agent-file emitters live in the Vinaya process with their tests, with no behaviour change #1723:
apps/cli/src/lib/broker.tscontainslaunchRecordMatchesRun - after [structure-move-v1] 5 — The standalone driver and its tests live in the Vinaya process's dev-review loop, run by continuous integration as before, with no behaviour change #1720:
apps/cli/src/lib/worker-boundary.tscontainsisReviewerScratchName apps/cli/src/lib/task-tools/role-grants.tscontainsROLE_TOOL_GRANTSpackages/aeg-core/src/protected-paths.tscontainsexport const PROTECTED_ADMIN_PATHS = ['vinaya.config.json', '.vinaya/doc-owners', '.github/', 'aeg-root/'] as const
Surface
in: apps/cli/src, apps/cli/tests, apps/cli/specs, harness
out: packages, apps/log-server, apps/vinaya-portal, aeg-root
Parts
Part 1 (O1, O2) — The broker takes the protected-path policy and the launch-record verifier.
Part 2 (O3) — The worker boundary takes the scratch-name policy.
Part 3 (O4) — The role policy carries the grants, router, broker and boundary role facts.
Part 4 (O5) — Building any of them without its policies is refused, and tests assert it.
Part 5 (O6) — One composition module supplies the policies; parity tests and the security suites pass unchanged.
Part 6 (O7) — Test ownership, inputs and scheduling are updated with the move.
Test plan
cd apps/cli && bun test --timeout=30000 tests/lib/role-policy-parity.test.ts tests/lib/broker/policy-required.test.ts tests/lib/worker-boundary-policy.test.ts tests/isolation/isolation-contract.test.ts tests/sandbox-conformance/sandbox-conformance.test.ts → 0 fail
cd harness/kernel && bun test tests/architecture.test.ts → 0 fail
bun harness/kernel/tests/architecture-ratchet.ts --compare-base "$(git merge-base origin/main HEAD)" → exits 0
bunx turbo run typecheck --affected → exits 0
bun scripts/test-suites/inventory-check.ts → exits 0; every moved or new test classified once
Stop conditions
- A builder cannot reach the composition module without a kernel file importing the process.
- A security suite needs an assertion change to pass.
Origin
Principal-directed, 2026-10-29: the broker and worker-boundary crossings belong in this structural refactor through bounded internal policy interfaces that preserve existing authorization and isolation behaviour.
Activity
[-][structure-boundaries-v1] 3 — The broker and the worker boundary enforce policies the Vinaya process supplies, so neither names a role or a Vinaya path, with identical authorization and isolation[/-][+][structure-boundaries-v1] 3 — The broker, worker boundary, tool grants and router enforce role and path policies the Vinaya process supplies, so none names a role or a Vinaya path, with identical authorization and isolation[/+]daniboomerang commentedon Nov 2, 2026
Brief hash: 9e5d056e30a994d5448ec255a4d8b37a0df5f3a21dd9ddb9eee6dad47c0fb781
For: [model] (coding-agent CLI on a dev machine, dispatched locally, unattended)
Reason: Suggested agent-class — high: authorization and isolation code where a silent default would weaken a guarantee.
Owner: the Principal
Goal: The broker, worker boundary, tool grants and router enforce role and path policies the Vinaya process supplies, so none names a role or a Vinaya path, with identical authorization and isolation
Project: vinaya, cli
Tier: 1
Closes #1727
You are the AEG Developer. Run
bun apps/cli/src/index.ts doctrine --role developer --printand read its output first. Mandatory.Objectives
O1. The broker takes a protected-path policy, one function that answers whether a normalized touched path is protected, and keeps the path normalization and the push refusal; Vinaya's implementation holds today's protected administrative path list and is the one the surface-scope check also reads.
O2. The broker takes a launch-record verifier, the launch-record match and tee-recovery functions it calls today, implemented by the process's role dispatch, so the broker imports no dispatch code.
O3. The worker boundary takes a scratch-name policy, one function that answers whether a file name is an exempt scratch name for a given launched role, implemented by Vinaya with today's reviewer scratch rule, so the worker boundary imports no driver code.
O4. A role policy the process supplies carries every other role fact enforcement reads today: the closed role set and each role's tool grants (
role-grants.ts), the Operator grant and the role names in the router's refusals (router.ts), the broker's role-to-worker mapping and its role vocabulary (broker.ts), and the worker boundary's session-name parsing, role directory names and concurrent reviewer sibling relationship (worker-boundary.ts); the enforcing files keep their logic and read these facts from the policy, and name no role.O5. Building a broker, a worker boundary, a tool host or a router without its policies is refused with an error naming the missing policy, never defaulting to no protection or no exemption, and a test asserts each refusal.
O6. Every place that builds one of them obtains its policies from one process-side composition module; parity tests show each role's grants, an unknown role's refusal and every refusal message are the same as before; and the broker's refusal and recovery tests, the isolation contract test, the reviewer-isolation tests, the sandbox conformance suite and the surface-scope check tests pass unchanged in assertions.
O7. Each executable test this task moves, splits or adds has one architecture owner and one suite kind in the test inventory; moved or split tests keep all assertions, expanded cases, fixtures and setup and cleanup semantics, and their inventory entries and input paths are updated in the same change; compiler-resolved dependencies stay authoritative, and fixtures, schemas, doctrine, spawned programs and tree scans keep their modeled input edges or an explained conservative fallback; continuous integration schedules each required test exactly once per intended configuration under its actual runner, working directory and preload; internal tests import existing narrow owner modules, and dedicated compatibility tests exercise public package exports; no required test is deferred and no production behaviour changes. Generic policy enforcement and missing-policy tests live with the kernel owner and use synthetic role labels, never Vinaya's role vocabulary; Vinaya's role, grant, scratch-name and protected-path implementation tests live with the process; parity and integration coverage across the injected policies and every security refusal is kept. The pull request shows four selection plans for the moved area, each with selected test identities and reasons: an isolated implementation edit, a public boundary or policy edit, a fixture, schema or spawned-input edit, and an unrelated edit that selects none of the area's tests unless a stated conservative reason applies.
Documentation
apps/cli/src/lib/broker.ts— the push refusal and the launch-record recovery the policies replace inside it (O1, O2)apps/cli/src/lib/worker-boundary.ts— the reviewer scratch exemption the policy replaces inside it (O3)packages/aeg-core/src/protected-paths.ts— today's protected administrative path list, shared by the broker and the surface-scope check (O1)apps/cli/specs/isolation.md— the isolation guarantees the boundary keeps (O3, O5)Premises
Checked against the code when this task Issue was cut, and again against the default branch at dispatch — facts, not claims.
apps/cli/src/lib/broker.tscontainslaunchRecordMatchesRunapps/cli/src/lib/worker-boundary.tscontainsisReviewerScratchNameapps/cli/src/lib/task-tools/role-grants.tscontainsROLE_TOOL_GRANTSpackages/aeg-core/src/protected-paths.tscontainsexport const PROTECTED_ADMIN_PATHS = ['vinaya.config.json', '.vinaya/doc-owners', '.github/', 'aeg-root/'] as const2. Context — read before doing anything
structure-boundaries-v1, task 3, Issue [structure-boundaries-v1] 3 — The broker, worker boundary, tool grants and router enforce role and path policies the Vinaya process supplies, so none names a role or a Vinaya path, with identical authorization and isolation #1727. Branchtask/structure-boundaries-v1/3.Depends-on: #1723, #1720,Conflicts-with: #1723, #1726.1cadb745f62031077331eac3e7e3c8ccdbc85774— the checkout's HEAD equaled the remote default branch, and no pinned file below carried an uncommitted change, when these facts were read.harness/processes/vinaya-strict/integration/policies/, in one composition module that every builder calls. The protected-path policy's list is data the process owns, so the doctrine move retargets it to the doctrine's new paths in the same change that moves the doctrine. A legacy role enumeration kept only to read old Log records may stay in the kernel when its comment says so; active authorization facts may not. The policy functions keep today's exact semantics, including path normalization happening before the protected check and the reviewer and security scratch prefixes. Pinned files:apps/cli/src/lib/broker.ts,apps/cli/src/lib/worker-boundary.ts,apps/cli/src/lib/task-tools/role-grants.ts,apps/cli/src/lib/task-tools/router.ts,apps/cli/tests/lib/role-policy-parity.test.ts,harness/processes/vinaya-strict/integration/policies/vinaya-policies.ts,apps/cli/tests/lib/broker/policy-required.test.ts,apps/cli/tests/lib/worker-boundary-policy.test.ts. Out: the protected path list's contents, the scratch rule, the grants and the refusal messages do not change; the broker and boundary files move to the kernel in a later task; the importers of the two modules change only where they build one.doc-claims: claim anchors that cite the broker's or the boundary's lines are re-pointed if the lines move, so each still resolves.reader-resolvable-prose: the new files' comments name policies and paths, never a forge number or planning label.3. Technical dependencies
Dependency rationale —
Depends-on: #1723, #1720— the role dispatch that implements the launch-record verifier must already sit in the process.Conflicts-with: #1723, #1726— the first moves the dispatch module the broker imports, and the configuration task edits shared CLI library files these builders read.4. Technical surface map
Create:
Modify:
Out of surface: packages, apps/log-server, apps/vinaya-portal, aeg-root — a change there, or to any path no Surface
in:glob covers, is still published: the review-loop driver records each such path in the pull request body's beyond-Surface block, and the reviewers judge it like any change.Premise pins
Premise:
5. Pre-flight checks
Step 0 (mandatory, verbatim):
origin/main; branch suffix literal-matches the task id.bun apps/cli/src/index.ts task run), the driver has already run the dispatch-readiness gate plus, on this repository toolchain, its own fuller derivation from its own unsandboxed process before dispatching you (re-derived at render time: it was ready), and dispatches you only when that verdict is READY — a NOT READY verdict refuses the turn before it starts, so being dispatched is the verdict: look for no file and run neither script, whose ownghcall fails inside your sandbox. Working manually, with no driver behind you, fall back toroles/developer.md's own items 3/5/7 prose — each a complete, hand-run forge check in its own right.On any failure: STOP and report.
6. Numbered parts — publish once with
publish_changeswhen every Part is done, thenopen_pull_requestPart 1 (O1, O2): The broker takes the protected-path policy and the launch-record verifier.
Files (touches @attalabs/vinaya):
The pre-push hook runs the affected suite on your one push and refuses it on failure — do not run it yourself per Part.
Part 2 (O3): The worker boundary takes the scratch-name policy.
Part 3 (O4): The role policy carries the grants, router, broker and boundary role facts.
Part 4 (O5): Building any of them without its policies is refused, and tests assert it.
Part 5 (O6): One composition module supplies the policies; parity tests and the security suites pass unchanged.
Part 6 (O7): Test ownership, inputs and scheduling are updated with the move.
7. Documentation-update list
.vinaya/doc-ownersderivation matched zero bindings against this task's surface — no mechanically-derived doc updates. Confirm no additional doc artifact applies before treating this list as final.8. Verification before claiming done
run_checksand CI provide the authoritative results. Do not hand-run or paste static-gate commands. Working manually, run this repository's declared typecheck, lint/format, build, and documentation-gate commands yourself.bun apps/cli/src/index.ts doctrine --role developer --printgenuinely satisfied; use the controller'srun_checksresult, hooks, and CI as gate evidence.9. Test Plan
10. Stop conditions
Stop-and-escalate — Stop if a builder cannot reach the composition module without a kernel file importing the process. Stop if a security suite needs an assertion change to pass.
11. Constraints
12. Deliverable
[structure-boundaries-v1] 3 — The broker, worker boundary, tool grants and router enforce role and path policies the Vinaya process supplies, so none names a role or a Vinaya path, with identical authorization and isolationopen_pull_requesttool (title and body); publish your commits withpublish_changesand update the body withupdate_pull_request_body/refresh_evidence— you hold nogh/git pushcredential.publish_changes, when every Part is done; then open the pull request withopen_pull_request. Publish again only to answer review findings or a red gate — a refused hook, a failing check, a merge conflict.bun apps/cli/src/index.ts doctrine --template pr-report --print), withCloses #1727at the top of the header block. This brief stays frozen on the task Issue and never rides in the body.run_checksand CI run it.git diff origin/main...HEAD --statas context only; Surface is judged by the driver'ssurface-scopecheck, never by this hand-run diff. Token use needs no report — the Vinaya log records it.