Repository navigation
[structure-boundaries-v1] 2 — The configuration schema is a kernel schema plus a Vinaya-owned section, composed into the same validation, with identical results on every recorded configuration #1726
Description
Tier: 1
Project: vinaya, cli
Type: refactor
Objectives
O1. Before any code changes, a golden snapshot records, for every configuration in the corpus (the repository's own vinaya.config.json, every tracked vinaya.config*.json fixture, and every configuration value the existing configuration tests parse), whether the schema accepts it, the exact error text when it refuses, and the values every exported resolver returns when it accepts.
O2. The kernel owns the configuration schema and loader for the keys that name no process or role (checks and their environment declarations, runtime directory, logs, agents, managed files, continuous-integration setup, workspaces and the file, local-override and trust-anchor loading), under harness/kernel/config/schema/ and harness/kernel/config/loader/, and exports a process configuration section interface: the keys a section owns, its object fragment, its cross-key refinements and its resolvers.
O3. Vinaya's section, under harness/processes/vinaya-strict/integration/config/, owns the review policy, gate cutovers, prose rules, collision threshold, release actor, principals, tokens collection and command-emitter paths, with their refinements and resolvers.
O4. The composition of the kernel schema with Vinaya's section lives in the process, under harness/processes/vinaya-strict/integration/config/, and apps/cli/src/lib/config.ts becomes a re-export of it that keeps every export it has today under the same name and type, so none of its importers changes.
O5. Kernel code reads configuration only through a configuration provider the kernel defines: a composition root (the CLI entry point and each process runner entry) installs Vinaya's composed loader before any command or run starts, a kernel read with no installed provider is refused with an error naming it, and the files a later task moves into the kernel (run-paths.ts, log-sink.ts and every other kernel-bound importer of the configuration module) read through the provider from this task on.
O6. The golden snapshot is identical after the split, the exported configuration shape's key set is unchanged, and the existing configuration 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 schema, loader and provider tests live with the kernel owner, Vinaya section and resolver tests with the process, and the golden composition and the CLI export tests with the process integration consumer; the golden configurations and snapshot are recorded as input edges, and the full golden parity check stays required. 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/config.ts— the one schema, its refinements and resolvers this task splits (O2, O3, O4)apps/cli/tests/config.test.ts— the existing accept and refuse cases the golden corpus includes (O1, O5)docs/adr/0012-harness-and-processes-sit-at-the-repository-root.md— today's layout, superseded by the target-tree record whose kernel rule this split serves (O2)
Planner's rationale
Boundary — Split the one configuration schema along the kernel and process line with a bounded internal interface, not the public Loop contract. The golden snapshot is recorded first, from the unchanged schema, and committed; the split must reproduce it exactly, including the order and text of refinement errors. A refinement that reads both kernel and Vinaya keys belongs to the section and receives the kernel keys it reads. The composition sits in the process from this task on, and the CLI's configuration module only re-exports it, so its importers are untouched; kernel-bound importers switch to the provider in this task, so the kernel moves that follow never import the CLI or the process for configuration. Pinned files: apps/cli/src/lib/config.ts, apps/cli/tests/config.test.ts, apps/cli/tests/config-golden.test.ts, apps/cli/tests/fixtures/config-golden/snapshot.json, harness/kernel/config/schema/config-schema.ts, harness/kernel/config/loader/config-loader.ts, harness/kernel/config/schema/process-config-section.ts, harness/processes/vinaya-strict/integration/config/vinaya-config-section.ts, harness/processes/vinaya-strict/integration/config/compose.ts, harness/kernel/config/loader/config-provider.ts, apps/cli/src/lib/run-paths.ts, apps/cli/src/lib/log-sink.ts. Out: no key is added, removed or renamed; no default or error text changes; the importers of the configuration module, across the CLI, the process package and packages/aeg-core, are run and never edited, except the kernel-bound readers that switch to the provider.
Sizing — One verification story: the golden snapshot recorded before the split equals the one after it. Thirteen pinned files, eight new; one failure mode (a validation, message or resolved value differs). Passes all four tests.
Project(s) + blast radius — Project: vinaya, cli. Every CLI command reads configuration through the composition module, whose exports are unchanged, so consumers are re-verified by the golden snapshot rather than edited.
Dependency rationale — Depends-on: #1723 — the command-emitter paths the schema imports must already sit in the process. Conflicts-with: #1723 — that task rewrites the configuration module's emitter imports.
Traps to avoid — Do NOT record the snapshot after starting the split; record it from the unchanged schema in the first commit. Do NOT reorder refinements; zod reports issues in the order they are added. Do NOT let a kernel file import Vinaya's section; the composition module imports both. Do NOT name a role or a process in kernel files.
doc-claims: no claim anchor names a line this task changes, and the configuration module keeps every export a claim anchor cites, so every anchor still resolves.
reader-resolvable-prose: the new files' comments name keys and things, never a forge number or planning label.
Suggested agent-class — high: a schema split that must reproduce every error text and resolved value exactly.
Stop-and-escalate — Stop if a refinement cannot be placed in one side without changing its error text or order, and quote it. Stop if a kernel key's resolver needs a Vinaya key.
Docs to keep coherent — apps/cli/specs/config-reference.md names keys, not files, so it is unaffected; doc-owners derivation for the pinned files fired no binding.
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
apps/cli/src/lib/config.tscontainsconst VinayaConfigObjectSchema = z.object({apps/cli/src/lib/config.tscontainsexport const VinayaConfigSchema = VinayaConfigObjectSchema.superRefine((config, ctx) => {apps/cli/src/lib/config.tscontainsexport function resolveReviewPolicy(config: VinayaConfig | null): ReviewPolicy {
Surface
in: apps/cli/src, apps/cli/scripts, apps/cli/tests, harness, scripts/ci, scripts/test-suites
out: packages, apps/log-server, apps/vinaya-portal, aeg-root
Parts
Part 1 (O1) — The golden corpus and snapshot are recorded from the unchanged schema.
Part 2 (O2) — The kernel schema, loader and section interface are written.
Part 3 (O3) — Vinaya's section holds its keys, refinements and resolvers.
Part 4 (O4, O5) — The composition lives in the process, the configuration module re-exports it, and kernel-bound readers use the installed provider.
Part 5 (O6) — The snapshot, key set and existing tests match.
Part 6 (O7) — Test ownership, inputs and scheduling are updated with the move.
Test plan
cd apps/cli && bun test --timeout=30000 tests/config-golden.test.ts tests/config.test.ts tests/lib/config-local-override.test.ts → 0 fail; the golden snapshot is unchanged
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 refinement cannot be placed without changing its error text or order.
- A kernel key's resolver needs a Vinaya key.
Origin
Principal-directed, 2026-10-29: the configuration boundary belongs in this structural refactor through a bounded internal interface that preserves existing validation behaviour; the public Loop contract stays separate.
Activity
daniboomerang commentedon Nov 2, 2026
Brief hash: 8884df0159c15a5ca34542ab35f8d6eef8f5457187b10f6691a2801d8c9c811d
For: [model] (coding-agent CLI on a dev machine, dispatched locally, unattended)
Reason: Suggested agent-class — high: a schema split that must reproduce every error text and resolved value exactly.
Owner: the Principal
Goal: The configuration schema is a kernel schema plus a Vinaya-owned section, composed into the same validation, with identical results on every recorded configuration
Project: vinaya, cli
Tier: 1
Closes #1726
You are the AEG Developer. Run
bun apps/cli/src/index.ts doctrine --role developer --printand read its output first. Mandatory.Objectives
O1. Before any code changes, a golden snapshot records, for every configuration in the corpus (the repository's own
vinaya.config.json, every trackedvinaya.config*.jsonfixture, and every configuration value the existing configuration tests parse), whether the schema accepts it, the exact error text when it refuses, and the values every exported resolver returns when it accepts.O2. The kernel owns the configuration schema and loader for the keys that name no process or role (checks and their environment declarations, runtime directory, logs, agents, managed files, continuous-integration setup, workspaces and the file, local-override and trust-anchor loading), under
harness/kernel/config/schema/andharness/kernel/config/loader/, and exports a process configuration section interface: the keys a section owns, its object fragment, its cross-key refinements and its resolvers.O3. Vinaya's section, under
harness/processes/vinaya-strict/integration/config/, owns the review policy, gate cutovers, prose rules, collision threshold, release actor, principals, tokens collection and command-emitter paths, with their refinements and resolvers.O4. The composition of the kernel schema with Vinaya's section lives in the process, under
harness/processes/vinaya-strict/integration/config/, andapps/cli/src/lib/config.tsbecomes a re-export of it that keeps every export it has today under the same name and type, so none of its importers changes.O5. Kernel code reads configuration only through a configuration provider the kernel defines: a composition root (the CLI entry point and each process runner entry) installs Vinaya's composed loader before any command or run starts, a kernel read with no installed provider is refused with an error naming it, and the files a later task moves into the kernel (
run-paths.ts,log-sink.tsand every other kernel-bound importer of the configuration module) read through the provider from this task on.O6. The golden snapshot is identical after the split, the exported configuration shape's key set is unchanged, and the existing configuration 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 schema, loader and provider tests live with the kernel owner, Vinaya section and resolver tests with the process, and the golden composition and the CLI export tests with the process integration consumer; the golden configurations and snapshot are recorded as input edges, and the full golden parity check stays required. 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/config.ts— the one schema, its refinements and resolvers this task splits (O2, O3, O4)apps/cli/tests/config.test.ts— the existing accept and refuse cases the golden corpus includes (O1, O5)docs/adr/0012-harness-and-processes-sit-at-the-repository-root.md— today's layout, superseded by the target-tree record whose kernel rule this split serves (O2)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/config.tscontainsconst VinayaConfigObjectSchema = z.object({apps/cli/src/lib/config.tscontainsexport const VinayaConfigSchema = VinayaConfigObjectSchema.superRefine((config, ctx) => {apps/cli/src/lib/config.tscontainsexport function resolveReviewPolicy(config: VinayaConfig | null): ReviewPolicy {2. Context — read before doing anything
structure-boundaries-v1, task 2, Issue [structure-boundaries-v1] 2 — The configuration schema is a kernel schema plus a Vinaya-owned section, composed into the same validation, with identical results on every recorded configuration #1726. Branchtask/structure-boundaries-v1/2.Depends-on: #1723,Conflicts-with: #1723.1cadb745f62031077331eac3e7e3c8ccdbc85774— the checkout's HEAD equaled the remote default branch, and no pinned file below carried an uncommitted change, when these facts were read.apps/cli/src/lib/config.ts,apps/cli/tests/config.test.ts,apps/cli/tests/config-golden.test.ts,apps/cli/tests/fixtures/config-golden/snapshot.json,harness/kernel/config/schema/config-schema.ts,harness/kernel/config/loader/config-loader.ts,harness/kernel/config/schema/process-config-section.ts,harness/processes/vinaya-strict/integration/config/vinaya-config-section.ts,harness/processes/vinaya-strict/integration/config/compose.ts,harness/kernel/config/loader/config-provider.ts,apps/cli/src/lib/run-paths.ts,apps/cli/src/lib/log-sink.ts. Out: no key is added, removed or renamed; no default or error text changes; the importers of the configuration module, across the CLI, the process package andpackages/aeg-core, are run and never edited, except the kernel-bound readers that switch to the provider.doc-claims: no claim anchor names a line this task changes, and the configuration module keeps every export a claim anchor cites, so every anchor still resolves.reader-resolvable-prose: the new files' comments name keys and things, never a forge number or planning label.3. Technical dependencies
Dependency rationale —
Depends-on: #1723— the command-emitter paths the schema imports must already sit in the process.Conflicts-with: #1723— that task rewrites the configuration module's emitter imports.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): The golden corpus and snapshot are recorded from the unchanged schema.
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 (O2): The kernel schema, loader and section interface are written.
Part 3 (O3): Vinaya's section holds its keys, refinements and resolvers.
Part 4 (O4, O5): The composition lives in the process, the configuration module re-exports it, and kernel-bound readers use the installed provider.
Part 5 (O6): The snapshot, key set and existing tests match.
Part 6 (O7): Test ownership, inputs and scheduling are updated with the move.
7. Documentation-update list
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 refinement cannot be placed in one side without changing its error text or order, and quote it. Stop if a kernel key's resolver needs a Vinaya key.
11. Constraints
12. Deliverable
[structure-boundaries-v1] 2 — The configuration schema is a kernel schema plus a Vinaya-owned section, composed into the same validation, with identical results on every recorded configurationopen_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 #1726at 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.daniboomerang commentedon Nov 2, 2026
The dev-review-loop paused: ready_push_failed — the ready push failed 3 consecutive times; failing test files: ~/Work/Repositories/Me/atta-labs/vinaya/.worktrees/task/structure-boundaries-v1/2/harness/processes/vinaya-strict/tests/standalone/lib/task-worktree-install.test.ts.
No pull request exists yet for this task, so the pause is recorded on this Issue instead.
A Principal ruling is needed before this can continue. Post it as a comment on this Issue whose FIRST line is exactly this marker — the second number is the ordinal, and it must exceed the one this pause recorded:
Then continue with:
The Operator's own
task_resumeclears this pause too, reading that same ruling from this Issue;task_cancelends it instead.daniboomerang commentedon Nov 2, 2026
Ruling: proceed_as_briefed — retry the ready push; change nothing.
The run's record leaves one file unproven,
harness/processes/vinaya-strict/tests/standalone/lib/task-worktree-install.test.ts. On this branch's head that file passes on its own, 9 pass and 0 fail, with the push's own--timeout=30000; it also passes on the default branch. The failing push ran while the host was out of memory. Retry: the next ready push re-runs only that file plus any change since. If it fails again there, find what in the shared run affects it (the other files in its group) and fix that; change no assertion.