Skip to content

[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

Closed

Description

@daniboomerang

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

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

  1. changed the title [-][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[/+] on Oct 29, 2026
  2. daniboomerang commented on Nov 2, 2026

    @daniboomerang
    MemberAuthor

    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 --print and 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.

    2. Context — read before doing anything

    • Tranche: 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. Branch task/structure-boundaries-v1/3. Depends-on: #1723, #1720, Conflicts-with: #1723, #1726.
    • Read 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 in full for the complete rationale — do not re-derive it.
    • Revision: rendered at 1cadb745f62031077331eac3e7e3c8ccdbc85774 — the checkout's HEAD equaled the remote default branch, and no pinned file below carried an uncommitted change, when these facts were read.
    • 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.
    • 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.

    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:

    • (none — every surface file already exists)

    Modify:

    • apps/cli/src
    • apps/cli/tests
    • apps/cli/specs
    • harness

    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:

    • apps/cli/src/lib/broker.ts sha256: cb9c1e760eb72534a7f39aca13f113a7aeab9e10e03a41ff5d828d2fd4cea261
    • apps/cli/src/lib/task-tools/role-grants.ts sha256: 57896cf7ab78198ceb603420fadcf847ff844161ebdc73bd515c7ad69a10db36
    • apps/cli/src/lib/task-tools/router.ts sha256: 8be5c93510da6ac20a4ecef280ed73fc4a92eab74edc12ec1e43631eb7f6c1b3
    • apps/cli/src/lib/worker-boundary.ts sha256: cd0c13008bee8f8ebbedb2f8d707192aba8e44cb54f952aebefab6a45295ed1e

    5. Pre-flight checks

    Step 0 (mandatory, verbatim):

    bun install --frozen-lockfile --silent
    
    1. Clean status; parent origin/main; branch suffix literal-matches the task id.
    2. Under the review loop (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 own gh call fails inside your sandbox. Working manually, with no driver behind you, fall back to roles/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_changes when every Part is done, then open_pull_request

    1. Part 1 (O1, O2): The broker takes the protected-path policy and the launch-record verifier.

      Files (touches @attalabs/vinaya):

      • apps/cli/src/lib/broker.ts
      • apps/cli/src/lib/task-tools/role-grants.ts
      • apps/cli/src/lib/task-tools/router.ts
      • apps/cli/src/lib/worker-boundary.ts

      The pre-push hook runs the affected suite on your one push and refuses it on failure — do not run it yourself per Part.

    2. Part 2 (O3): The worker boundary takes the scratch-name policy.

    3. Part 3 (O4): The role policy carries the grants, router, broker and boundary role facts.

    4. Part 4 (O5): Building any of them without its policies is refused, and tests assert it.

    5. Part 5 (O6): One composition module supplies the policies; parity tests and the security suites pass unchanged.

    6. Part 6 (O7): Test ownership, inputs and scheduling are updated with the move.

    7. Documentation-update list

    .vinaya/doc-owners derivation 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

    • Under the review loop, the controller runs the static gates: the commit hook applies safe format and lint fixes to staged files, typechecks affected packages, and runs the Vinaya checks; the pre-push hook runs the affected suite; run_checks and 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.
    • The pre-push hook runs the affected suite once, on your one push, and refuses it on failure; CI runs the full suite.
    • Every blast-radius consumer named in §4, re-verified by name.
    • The tier checklist in bun apps/cli/src/index.ts doctrine --role developer --print genuinely satisfied; use the controller's run_checks result, hooks, and CI as gate evidence.

    9. 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
    

    10. 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.

    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

    Autonomy: Do not stop to ask clarifying questions. For any ambiguity not covered by a Section 10 stop condition, choose the most reasonable option consistent with this brief, record the choice in the PR body at open, and continue. Halt only for the explicit Section 10 stop conditions — and when you halt, report the blocker in your turn result (the loop posts it); post no PR or Issue comment yourself, and do not wait interactively for input.

    12. Deliverable

    • PR title (exact): [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
    • Open the PR only via the open_pull_request tool (title and body); publish your commits with publish_changes and update the body with update_pull_request_body/refresh_evidence — you hold no gh/git push credential.
    • Publish once, with publish_changes, when every Part is done; then open the pull request with open_pull_request. Publish again only to answer review findings or a red gate — a refused hook, a failing check, a merge conflict.
    • PR body = the Developer's PR report (print it with bun apps/cli/src/index.ts doctrine --template pr-report --print), with Closes #1727 at the top of the header block. This brief stays frozen on the task Issue and never rides in the body.
    • Pre-open: tier checklist satisfied. The documentation gate is the controller's — run_checks and CI run it.
    • Include git diff origin/main...HEAD --stat as context only; Surface is judged by the driver's surface-scope check, never by this hand-run diff. Token use needs no report — the Vinaya log records it.
    • Then STOP. Review and Verification are separate invocations.
  3. self-assigned this
    on Nov 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions