Skip to content

[engine-harness-v1] 5 — One real task reaches a reviewed pull request on the Engine path, on Claude and on Codex #1564

Closed

Description

@daniboomerang

Tier: 1
Project: vinaya
Type: feat

Objectives

O1. A repository-only entry point in the Vinaya process folder runs one task on the Engine path (--task <n> --agent <claude|codex>), and the current loop stays the default: no CLI command, default or published file changes.
O2. The entry point writes a run trace to <runtimeDir>/engine-runs/<task>/trace.json listing every step executed in order with its exit code, each agent step's node:streaming records as the executor delivered them (tool calls and any vendor-reported refusal text, verbatim, for both vendors), and both reviewer verdicts parsed by parseReviewResult; a test over a stub run asserts the step order and that each agent step's streaming records appear unaltered.
O3. A dry-run mode prints the loaded process digest, the compiled step list and each role's generated launch command without launching an agent or writing to the forge, and a test covers it.
O4. The dry run also lists, for each of the 11 mechanical steps, its action name and the name of each function in ACTION_BINDINGS[action].adapts; a test asserts 11 lines and that harness.publish lists publishRound.
O5. The entry point builds the run's onEvent from createLogTranslator(createVinayaLogProducers({ loopId, task }), emit) where emit is the CLI log sink's createLogSink().log (see Premises), imported read-only, and awaits the sink's drain() before exiting on every path (success, failure, pause); a test with an injected sink asserts the stub run's emitted events reach it in the translator's order and that drain was called; the entry point prints the run's loopId on its first stdout line.
O6. vinaya.validate-reviewer-outputs writes both held verdicts with writeHeldVerdict for the round in progress, and harness.publish reads prNumber and expectedHead at call time (via findOpenPrForBranch(task.branch) and the worktree head) instead of from a value fixed before the run; the stub-run tests in processes/vinaya/tests/run-flow.test.ts still pass and one new test asserts publish receives the PR number found at publish time.
O7. Before launching the Developer, the entry point starts the dev-tools host with startDevToolsHost (the CLI's dev-tools host, see Premises, imported read-only) on devToolsSocketPath(engine-${task}) (where task is the --task number) for role developer, writes an executable shim script under <runtimeDir>/engine-runs/<task>/ that execs process.execPath <repo>/apps/cli/src/index.ts task-tools dev-bridge --socket <socketPath>, and passes the shim's absolute path as createRoleLaunchers({ mcpCommand }) for both vendors (the generators accept one executable with no arguments); it closes the host on every exit path. The dry run prints the shim path per role without starting the host. A test asserts the shim's content for a given socket path and that the host's close is called on success, failure and pause (injected host).

Documentation

  • apps/cli/specs/loop.md — the standalone loop stays the default and the oracle (O1)
  • apps/cli/tests/conformance/packed-runtime-not-shipped.test.ts — why the switch cannot live in CLI source (O1)
  • docs/adr/README.md — the index naming the record that supersedes 0009 (migrate first, evaluate the Engine after migration) (O4)

Premises

  • harness/dev-process/engine-runtime/run-process.ts contains export async function startProcessRun(options: ProcessRunOptions, identity?: RunIdentity): Promise<RunOutcome> {
  • harness/dev-process/engine-runtime/run-process.ts contains onEvent?: AgentSpawnExecutorConfig['onEvent']
  • harness/dev-process/loader/load-process.ts contains export function loadProcess(folderPath: string): LoadedProcess {
  • processes/vinaya/executing/actions.ts contains export function createRoleLaunchers(options: {
  • processes/vinaya/executing/actions.ts contains publishRound(task.root, task.publish)
  • harness/dev-process/logging/translate.ts contains export function createLogTranslator(producers: LogProducers, emit: EmitLogEvent): LogTranslator {
  • processes/vinaya/executing/log-events.ts contains export function createVinayaLogProducers(config: Pick<LoopConfig, 'loopId' | 'task'>): LogProducers {
  • apps/cli/src/lib/log-sink.ts contains export function createLogSink(overrides: Partial<LogSinkDeps> = {}): {
  • apps/cli/src/lib/dev-review-loop/reviewer-dispatch.ts contains export function writeHeldVerdict(
  • apps/cli/src/lib/task-tools/dev-tools-host.ts contains export async function startDevToolsHost(opts: StartDevToolsHostOptions): Promise<DevToolsHost> {
  • harness/adapters/agents/claude-code/settings.ts contains command: paths.mcpCommand ?? 'vinaya-dev-bridge', args: []

Planner's rationale

Boundary — A repository-only runner over the executor and bindings from the previous tasks, its trace, the real Log sink wiring, and the two binding fixes a live run needs to reach a reviewed pull request: held verdicts written at validation, and the publish input (PR number, judged head) read at publish time. The switch is choosing this entry point instead of vinaya task run; the CLI is not touched because CLI source may not import any Engine package. The Developer opens its pull request through the repository MCP tools, reached via a runner-started dev-tools host and a relay shim (O7). Pinned files: README.md, processes/vinaya/bin/run-engine.ts, processes/vinaya/tests/run-engine.test.ts, processes/vinaya/package.json, processes/vinaya/executing/actions.ts, processes/vinaya/tests/run-flow.test.ts. Out: any CLI change, any default change, any evaluation run, any change to harness/dev-process/logging or packages/aeg-core.

reader-resolvable-prose: README and comments carry no Issue or pull-request numbers and no tranche slugs.

Sizing — One verification story: a reviewed pull request per vendor with a complete trace and its Log events. About four rounds (runner + trace, sink wiring, held-verdict and publish-time binding, dev-tools host and shim); kept as one task by Principal direction, because without O6 and O7 the live proof cannot succeed.

Project(s) + blast radius — Project: vinaya. No shared package changes.

blast-radius-ack: only the private process package and the root README change.

Dependency rationale — Depends-on: #1563, #1570 — the runner calls its engine runtime and bindings, and the first real run must already write the review loop's Log events.

Traps to avoid — Do NOT add a backend flag to any CLI command: CLI source may not import Engine packages and the CLI is published. Do NOT run a live task from a test. Do NOT claim a grants.yaml rule per tool call: the executor exposes only node:start|streaming|complete|failed, so the trace records what the vendor reported, verbatim. Keep the trace out of the repository: the Principal posts it on this Issue.

Suggested agent-class — mid: a thin runner, a trace writer and two binding changes.

Stop-and-escalate — Stop if the dry run cannot print a launch command for a role from the generated settings, or if either vendor's generated Developer sandbox blocks the shim's socket connection or denies the repository MCP tools (do not edit harness/ to work around it).

Docs to keep coherent — README.md (one line naming the Engine-path runner as repository-only). Doc-owners derivation: no binding fires for processes or root files.

Surface

in: processes, *, harness
out: apps, packages, aeg-root, .changeset

Parts

Part 1 (O1, O3) — The runner with dry-run and its test.
Part 2 (O2) — The run trace.
Part 3 (O4) — Bound functions shown in the dry run.
Part 4 (O5) — The real Log sink as the run's event handler, drained on every exit path.
Part 5 (O6) — Held verdicts written at validation; publish input read at publish time.
Part 6 (O7) — The dev-tools host started by the runner and the relay shim passed to both vendors.

Test plan

cd processes/vinaya && bun test tests/run-engine.test.ts tests/run-flow.test.ts tests/log-events.test.ts → 0 fail
cd processes/vinaya && bun bin/run-engine.ts --task 1 --agent claude --dry-run → prints digest, 14 steps, 11 bound-function lines, three launch commands; exits 0; no forge write
cd processes/vinaya && bun bin/run-engine.ts --task 1 --agent codex --dry-run → same shape, Codex launch commands
bunx turbo typecheck --filter=@attalabs/process-vinaya → exits 0

[principal]

bun processes/vinaya/bin/run-engine.ts --task <small backlog Issue A> --agent claude
bun processes/vinaya/bin/run-engine.ts --task <small backlog Issue B> --agent codex

Before each run, note last_seq as S (EVENTS is logs.url from vinaya.config.json, the repository's events route on the Vinaya log server):

EVENTS=$(jq -r .logs.url vinaya.config.json)
curl -s -H "Authorization: Bearer $VINAYA_LOG_READ_TOKEN" "${EVENTS%/events}/stats"

After each run (count must be above 0):

curl -s -H "Authorization: Bearer $VINAYA_LOG_READ_TOKEN" "$EVENTS?after=S&limit=1000" | grep -c '<loopId from the run's first stdout line>'

Confirm both verdicts are posted on each pull request and the count is above 0, then post each <runtimeDir>/engine-runs/<n>/trace.json on this Issue.

Stop conditions

  • The dry run cannot produce a role's launch command.
  • Either vendor's generated Developer sandbox blocks the shim from connecting to the dev-tools socket, or its generated settings deny publish_changes or open_pull_request; name the vendor and the refused call or path. Do not edit harness/ to work around it.

Origin

Principal-approved target architecture, 2026-10-21: live proof that one real task reaches a reviewed pull request on the Engine path, both vendors.

Activity

  1. daniboomerang commented on Oct 22, 2026

    @daniboomerang
    MemberAuthor

    Brief hash: c3d8640f8430fb091c6396db3e426c74848f54b5eb920ddce813b0a75f347ca7
    For: [model] (coding-agent CLI on a dev machine, dispatched locally, unattended)
    Reason: Suggested agent-class — mid: a thin runner, a trace writer and two binding changes.
    Owner: the Principal
    Goal: One real task reaches a reviewed pull request on the Engine path, on Claude and on Codex
    Project: vinaya
    Tier: 1

    Closes #1564

    You are the AEG Developer. Run bun apps/cli/src/index.ts doctrine --role developer --print and read its output first. Mandatory.

    Objectives

    O1. A repository-only entry point in the Vinaya process folder runs one task on the Engine path (--task <n> --agent <claude|codex>), and the current loop stays the default: no CLI command, default or published file changes.
    O2. The entry point writes a run trace to <runtimeDir>/engine-runs/<task>/trace.json listing every step executed in order with its exit code, each agent step's node:streaming records as the executor delivered them (tool calls and any vendor-reported refusal text, verbatim, for both vendors), and both reviewer verdicts parsed by parseReviewResult; a test over a stub run asserts the step order and that each agent step's streaming records appear unaltered.
    O3. A dry-run mode prints the loaded process digest, the compiled step list and each role's generated launch command without launching an agent or writing to the forge, and a test covers it.
    O4. The dry run also lists, for each of the 11 mechanical steps, its action name and the name of each function in ACTION_BINDINGS[action].adapts; a test asserts 11 lines and that harness.publish lists publishRound.
    O5. The entry point builds the run's onEvent from createLogTranslator(createVinayaLogProducers({ loopId, task }), emit) where emit is the CLI log sink's createLogSink().log (see Premises), imported read-only, and awaits the sink's drain() before exiting on every path (success, failure, pause); a test with an injected sink asserts the stub run's emitted events reach it in the translator's order and that drain was called; the entry point prints the run's loopId on its first stdout line.
    O6. vinaya.validate-reviewer-outputs writes both held verdicts with writeHeldVerdict for the round in progress, and harness.publish reads prNumber and expectedHead at call time (via findOpenPrForBranch(task.branch) and the worktree head) instead of from a value fixed before the run; the stub-run tests in processes/vinaya/tests/run-flow.test.ts still pass and one new test asserts publish receives the PR number found at publish time.
    O7. Before launching the Developer, the entry point starts the dev-tools host with startDevToolsHost (the CLI's dev-tools host, see Premises, imported read-only) on devToolsSocketPath(engine-${task}) (where task is the --task number) for role developer, writes an executable shim script under <runtimeDir>/engine-runs/<task>/ that execs process.execPath <repo>/apps/cli/src/index.ts task-tools dev-bridge --socket <socketPath>, and passes the shim's absolute path as createRoleLaunchers({ mcpCommand }) for both vendors (the generators accept one executable with no arguments); it closes the host on every exit path. The dry run prints the shim path per role without starting the host. A test asserts the shim's content for a given socket path and that the host's close is called on success, failure and pause (injected host).

    Documentation

    • apps/cli/specs/loop.md — the standalone loop stays the default and the oracle (O1)
    • apps/cli/tests/conformance/packed-runtime-not-shipped.test.ts — why the switch cannot live in CLI source (O1)
    • docs/adr/README.md — the index naming the record that supersedes 0009 (migrate first, evaluate the Engine after migration) (O4)

    Premises

    Checked against the code when this task Issue was cut, and again against the default branch at dispatch — facts, not claims.

    • harness/dev-process/engine-runtime/run-process.ts contains export async function startProcessRun(options: ProcessRunOptions, identity?: RunIdentity): Promise<RunOutcome> {
    • harness/dev-process/engine-runtime/run-process.ts contains onEvent?: AgentSpawnExecutorConfig['onEvent']
    • harness/dev-process/loader/load-process.ts contains export function loadProcess(folderPath: string): LoadedProcess {
    • processes/vinaya/executing/actions.ts contains export function createRoleLaunchers(options: {
    • processes/vinaya/executing/actions.ts contains publishRound(task.root, task.publish)
    • harness/dev-process/logging/translate.ts contains export function createLogTranslator(producers: LogProducers, emit: EmitLogEvent): LogTranslator {
    • processes/vinaya/executing/log-events.ts contains export function createVinayaLogProducers(config: Pick<LoopConfig, 'loopId' | 'task'>): LogProducers {
    • apps/cli/src/lib/log-sink.ts contains export function createLogSink(overrides: Partial<LogSinkDeps> = {}): {
    • apps/cli/src/lib/dev-review-loop/reviewer-dispatch.ts contains export function writeHeldVerdict(
    • apps/cli/src/lib/task-tools/dev-tools-host.ts contains export async function startDevToolsHost(opts: StartDevToolsHostOptions): Promise<DevToolsHost> {
    • harness/adapters/agents/claude-code/settings.ts contains command: paths.mcpCommand ?? 'vinaya-dev-bridge', args: []

    2. Context — read before doing anything

    • Tranche: engine-harness-v1, task 5, Issue [engine-harness-v1] 5 — One real task reaches a reviewed pull request on the Engine path, on Claude and on Codex #1564. Branch task/engine-harness-v1/5. Depends-on: #1563, #1570, Conflicts-with: —.
    • Read Issue [engine-harness-v1] 5 — One real task reaches a reviewed pull request on the Engine path, on Claude and on Codex #1564 in full for the complete rationale — do not re-derive it.
    • Revision: rendered at 369973d91cf6d164d30283c2f00ce8b8e7b835cc — the checkout's HEAD equaled the remote default branch, and no pinned file below carried an uncommitted change, when these facts were read.
    • Boundary — A repository-only runner over the executor and bindings from the previous tasks, its trace, the real Log sink wiring, and the two binding fixes a live run needs to reach a reviewed pull request: held verdicts written at validation, and the publish input (PR number, judged head) read at publish time. The switch is choosing this entry point instead of bun apps/cli/src/index.ts task run; the CLI is not touched because CLI source may not import any Engine package. The Developer opens its pull request through the repository MCP tools, reached via a runner-started dev-tools host and a relay shim (O7). Pinned files: README.md, processes/vinaya/bin/run-engine.ts, processes/vinaya/tests/run-engine.test.ts, processes/vinaya/package.json, processes/vinaya/executing/actions.ts, processes/vinaya/tests/run-flow.test.ts. Out: any CLI change, any default change, any evaluation run, any change to harness/dev-process/logging or packages/aeg-core.

    reader-resolvable-prose: README and comments carry no Issue or pull-request numbers and no tranche slugs.

    • Traps to avoid — Do NOT add a backend flag to any CLI command: CLI source may not import Engine packages and the CLI is published. Do NOT run a live task from a test. Do NOT claim a grants.yaml rule per tool call: the executor exposes only node:start|streaming|complete|failed, so the trace records what the vendor reported, verbatim. Keep the trace out of the repository: the Principal posts it on this Issue.

    3. Technical dependencies

    Dependency rationale — Depends-on: #1563, #1570 — the runner calls its engine runtime and bindings, and the first real run must already write the review loop's Log events.

    4. Technical surface map

    Create:

    • (none — every surface file already exists)

    Modify:

    • README.md
    • processes/vinaya/executing/actions.ts
    • processes/vinaya/package.json
    • processes/vinaya/tests/run-flow.test.ts

    Out of surface: apps, packages, harness, aeg-root, .changeset — 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:

    • README.md sha256: b381bfacf4d57fe05813e831e06620da1b2d9243c39894c21a092d9cbd762b11
    • processes/vinaya/executing/actions.ts sha256: 5489b6ee206fcb4deb9107c5c39f88642beb4ca101b46dc1f8673d1bb74b75c5
    • processes/vinaya/package.json sha256: f82fbd82113a76d7eca6efbdec0fba70de61bd1fda4497c9353bc9b35ebb0423
    • processes/vinaya/tests/run-flow.test.ts sha256: d94864fa17c01bfa745482d06778a384b0854a041fbeb15aea4b0408ce57bfb2

    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, O3): The runner with dry-run and its test.

      Files (touches the repo root):

      • README.md
      • processes/vinaya/executing/actions.ts
      • processes/vinaya/package.json
      • processes/vinaya/tests/run-flow.test.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 (O2): The run trace.

    3. Part 3 (O4): Bound functions shown in the dry run.

    4. Part 4 (O5): The real Log sink as the run's event handler, drained on every exit path.

    5. Part 5 (O6): Held verdicts written at validation; publish input read at publish time.

    6. Part 6 (O7): The dev-tools host started by the runner and the relay shim passed to both vendors.

    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 processes/vinaya && bun test tests/run-engine.test.ts tests/run-flow.test.ts tests/log-events.test.ts → 0 fail
    cd processes/vinaya && bun bin/run-engine.ts --task 1 --agent claude --dry-run → prints digest, 14 steps, 11 bound-function lines, three launch commands; exits 0; no forge write
    cd processes/vinaya && bun bin/run-engine.ts --task 1 --agent codex --dry-run → same shape, Codex launch commands
    bunx turbo typecheck --filter=@attalabs/process-vinaya → exits 0
    bun processes/vinaya/bin/run-engine.ts --task <small backlog Issue A> --agent claude
    bun processes/vinaya/bin/run-engine.ts --task <small backlog Issue B> --agent codex
    EVENTS=$(jq -r .logs.url vinaya.config.json)
    curl -s -H "Authorization: Bearer $VINAYA_LOG_READ_TOKEN" "${EVENTS%/events}/stats"
    curl -s -H "Authorization: Bearer $VINAYA_LOG_READ_TOKEN" "$EVENTS?after=S&limit=1000" | grep -c '<loopId from the run's first stdout line>'
    

    10. Stop conditions

    • The dry run cannot produce a role's launch command.
    • Either vendor's generated Developer sandbox blocks the shim from connecting to the dev-tools socket, or its generated settings deny publish_changes or open_pull_request; name the vendor and the refused call or path. Do not edit harness/ to work around it.

    Stop-and-escalate — Stop if the dry run cannot print a launch command for a role from the generated settings, or if either vendor's generated Developer sandbox blocks the shim's socket connection or denies the repository MCP tools (do not edit harness/ to work around it).

    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, or in a PR comment after open, and continue. Halt only for the explicit Section 10 stop conditions — and when you halt, record the blocker in a PR comment or an Issue comment rather than waiting interactively for input.

    12. Deliverable

    • PR title (exact): [engine-harness-v1] 5 — One real task reaches a reviewed pull request on the Engine path, on Claude and on Codex
    • 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 #1564 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.
  2. daniboomerang commented on Oct 22, 2026

    @daniboomerang
    MemberAuthor

    Brief hash: d9cc4a38301dd2a906e6b3fc6be39b9978ae24f9ba3cb1ea0a7f21cd18f20bbe
    Supersedes: #1564 (comment) — The live Engine runs proved generic fixes belong in harness/ (grants contract, agent launch, checks runner, Codex login and output); the Surface gains harness
    For: [model] (coding-agent CLI on a dev machine, dispatched locally, unattended)
    Reason: Suggested agent-class — mid: a thin runner, a trace writer and two binding changes.
    Owner: the Principal
    Goal: One real task reaches a reviewed pull request on the Engine path, on Claude and on Codex
    Project: vinaya
    Tier: 1

    Closes #1564

    You are the AEG Developer. Run bun apps/cli/src/index.ts doctrine --role developer --print and read its output first. Mandatory.

    Objectives

    O1. A repository-only entry point in the Vinaya process folder runs one task on the Engine path (--task <n> --agent <claude|codex>), and the current loop stays the default: no CLI command, default or published file changes.
    O2. The entry point writes a run trace to <runtimeDir>/engine-runs/<task>/trace.json listing every step executed in order with its exit code, each agent step's node:streaming records as the executor delivered them (tool calls and any vendor-reported refusal text, verbatim, for both vendors), and both reviewer verdicts parsed by parseReviewResult; a test over a stub run asserts the step order and that each agent step's streaming records appear unaltered.
    O3. A dry-run mode prints the loaded process digest, the compiled step list and each role's generated launch command without launching an agent or writing to the forge, and a test covers it.
    O4. The dry run also lists, for each of the 11 mechanical steps, its action name and the name of each function in ACTION_BINDINGS[action].adapts; a test asserts 11 lines and that harness.publish lists publishRound.
    O5. The entry point builds the run's onEvent from createLogTranslator(createVinayaLogProducers({ loopId, task }), emit) where emit is the CLI log sink's createLogSink().log (see Premises), imported read-only, and awaits the sink's drain() before exiting on every path (success, failure, pause); a test with an injected sink asserts the stub run's emitted events reach it in the translator's order and that drain was called; the entry point prints the run's loopId on its first stdout line.
    O6. vinaya.validate-reviewer-outputs writes both held verdicts with writeHeldVerdict for the round in progress, and harness.publish reads prNumber and expectedHead at call time (via findOpenPrForBranch(task.branch) and the worktree head) instead of from a value fixed before the run; the stub-run tests in processes/vinaya/tests/run-flow.test.ts still pass and one new test asserts publish receives the PR number found at publish time.
    O7. Before launching the Developer, the entry point starts the dev-tools host with startDevToolsHost (the CLI's dev-tools host, see Premises, imported read-only) on devToolsSocketPath(engine-${task}) (where task is the --task number) for role developer, writes an executable shim script under <runtimeDir>/engine-runs/<task>/ that execs process.execPath <repo>/apps/cli/src/index.ts task-tools dev-bridge --socket <socketPath>, and passes the shim's absolute path as createRoleLaunchers({ mcpCommand }) for both vendors (the generators accept one executable with no arguments); it closes the host on every exit path. The dry run prints the shim path per role without starting the host. A test asserts the shim's content for a given socket path and that the host's close is called on success, failure and pause (injected host).

    Documentation

    • apps/cli/specs/loop.md — the standalone loop stays the default and the oracle (O1)
    • apps/cli/tests/conformance/packed-runtime-not-shipped.test.ts — why the switch cannot live in CLI source (O1)
    • docs/adr/README.md — the index naming the record that supersedes 0009 (migrate first, evaluate the Engine after migration) (O4)

    Premises

    Checked against the code when this task Issue was cut, and again against the default branch at dispatch — facts, not claims.

    • harness/dev-process/engine-runtime/run-process.ts contains export async function startProcessRun(options: ProcessRunOptions, identity?: RunIdentity): Promise<RunOutcome> {
    • harness/dev-process/engine-runtime/run-process.ts contains onEvent?: AgentSpawnExecutorConfig['onEvent']
    • harness/dev-process/loader/load-process.ts contains export function loadProcess(folderPath: string): LoadedProcess {
    • processes/vinaya/executing/actions.ts contains export function createRoleLaunchers(options: {
    • processes/vinaya/executing/actions.ts contains publishRound(task.root, task.publish)
    • harness/dev-process/logging/translate.ts contains export function createLogTranslator(producers: LogProducers, emit: EmitLogEvent): LogTranslator {
    • processes/vinaya/executing/log-events.ts contains export function createVinayaLogProducers(config: Pick<LoopConfig, 'loopId' | 'task'>): LogProducers {
    • apps/cli/src/lib/log-sink.ts contains export function createLogSink(overrides: Partial<LogSinkDeps> = {}): {
    • apps/cli/src/lib/dev-review-loop/reviewer-dispatch.ts contains export function writeHeldVerdict(
    • apps/cli/src/lib/task-tools/dev-tools-host.ts contains export async function startDevToolsHost(opts: StartDevToolsHostOptions): Promise<DevToolsHost> {
    • harness/adapters/agents/claude-code/settings.ts contains command: paths.mcpCommand ?? 'vinaya-dev-bridge', args: []

    2. Context — read before doing anything

    • Tranche: engine-harness-v1, task 5, Issue [engine-harness-v1] 5 — One real task reaches a reviewed pull request on the Engine path, on Claude and on Codex #1564. Branch task/engine-harness-v1/5. Depends-on: #1563, #1570, Conflicts-with: —.
    • Read Issue [engine-harness-v1] 5 — One real task reaches a reviewed pull request on the Engine path, on Claude and on Codex #1564 in full for the complete rationale — do not re-derive it.
    • Revision: rendered at 369973d91cf6d164d30283c2f00ce8b8e7b835cc — the checkout's HEAD equaled the remote default branch, and no pinned file below carried an uncommitted change, when these facts were read.
    • Boundary — A repository-only runner over the executor and bindings from the previous tasks, its trace, the real Log sink wiring, and the two binding fixes a live run needs to reach a reviewed pull request: held verdicts written at validation, and the publish input (PR number, judged head) read at publish time. The switch is choosing this entry point instead of bun apps/cli/src/index.ts task run; the CLI is not touched because CLI source may not import any Engine package. The Developer opens its pull request through the repository MCP tools, reached via a runner-started dev-tools host and a relay shim (O7). Pinned files: README.md, processes/vinaya/bin/run-engine.ts, processes/vinaya/tests/run-engine.test.ts, processes/vinaya/package.json, processes/vinaya/executing/actions.ts, processes/vinaya/tests/run-flow.test.ts. Out: any CLI change, any default change, any evaluation run, any change to harness/dev-process/logging or packages/aeg-core.

    reader-resolvable-prose: README and comments carry no Issue or pull-request numbers and no tranche slugs.

    • Traps to avoid — Do NOT add a backend flag to any CLI command: CLI source may not import Engine packages and the CLI is published. Do NOT run a live task from a test. Do NOT claim a grants.yaml rule per tool call: the executor exposes only node:start|streaming|complete|failed, so the trace records what the vendor reported, verbatim. Keep the trace out of the repository: the Principal posts it on this Issue.

    3. Technical dependencies

    Dependency rationale — Depends-on: #1563, #1570 — the runner calls its engine runtime and bindings, and the first real run must already write the review loop's Log events.

    4. Technical surface map

    Create:

    • (none — every surface file already exists)

    Modify:

    • processes
    • harness

    Out of surface: apps, packages, aeg-root, .changeset — 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:

    • README.md sha256: b381bfacf4d57fe05813e831e06620da1b2d9243c39894c21a092d9cbd762b11
    • processes/vinaya/executing/actions.ts sha256: 5489b6ee206fcb4deb9107c5c39f88642beb4ca101b46dc1f8673d1bb74b75c5
    • processes/vinaya/package.json sha256: f82fbd82113a76d7eca6efbdec0fba70de61bd1fda4497c9353bc9b35ebb0423
    • processes/vinaya/tests/run-flow.test.ts sha256: d94864fa17c01bfa745482d06778a384b0854a041fbeb15aea4b0408ce57bfb2

    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, O3): The runner with dry-run and its test.

      Files (touches the repo root):

      • README.md
      • processes/vinaya/executing/actions.ts
      • processes/vinaya/package.json
      • processes/vinaya/tests/run-flow.test.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 (O2): The run trace.

    3. Part 3 (O4): Bound functions shown in the dry run.

    4. Part 4 (O5): The real Log sink as the run's event handler, drained on every exit path.

    5. Part 5 (O6): Held verdicts written at validation; publish input read at publish time.

    6. Part 6 (O7): The dev-tools host started by the runner and the relay shim passed to both vendors.

    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 processes/vinaya && bun test tests/run-engine.test.ts tests/run-flow.test.ts tests/log-events.test.ts → 0 fail
    cd processes/vinaya && bun bin/run-engine.ts --task 1 --agent claude --dry-run → prints digest, 14 steps, 11 bound-function lines, three launch commands; exits 0; no forge write
    cd processes/vinaya && bun bin/run-engine.ts --task 1 --agent codex --dry-run → same shape, Codex launch commands
    bunx turbo typecheck --filter=@attalabs/process-vinaya → exits 0
    bun processes/vinaya/bin/run-engine.ts --task <small backlog Issue A> --agent claude
    bun processes/vinaya/bin/run-engine.ts --task <small backlog Issue B> --agent codex
    EVENTS=$(jq -r .logs.url vinaya.config.json)
    curl -s -H "Authorization: Bearer $VINAYA_LOG_READ_TOKEN" "${EVENTS%/events}/stats"
    curl -s -H "Authorization: Bearer $VINAYA_LOG_READ_TOKEN" "$EVENTS?after=S&limit=1000" | grep -c '<loopId from the run's first stdout line>'
    

    10. Stop conditions

    • The dry run cannot produce a role's launch command.
    • Either vendor's generated Developer sandbox blocks the shim from connecting to the dev-tools socket, or its generated settings deny publish_changes or open_pull_request; name the vendor and the refused call or path. Do not edit harness/ to work around it.

    Stop-and-escalate — Stop if the dry run cannot print a launch command for a role from the generated settings, or if either vendor's generated Developer sandbox blocks the shim's socket connection or denies the repository MCP tools (do not edit harness/ to work around it).

    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, or in a PR comment after open, and continue. Halt only for the explicit Section 10 stop conditions — and when you halt, record the blocker in a PR comment or an Issue comment rather than waiting interactively for input.

    12. Deliverable

    • PR title (exact): [engine-harness-v1] 5 — One real task reaches a reviewed pull request on the Engine path, on Claude and on Codex
    • 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 #1564 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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions