Repository navigation
Description
Tier: 1
Project: vinaya
Type: feat
Objectives
O1. packages/aeg-core/src/log/schema.ts adds a schema: 4 header meta: every schema: 3 field unchanged (including runtime, still string | null), plus a new process object { id: string, version: string | null, digest: string | null } and a new runner object { id: string, version: string | null } (id a free string, not an enum); HeaderMetaSchema and the custom family accept schema: 4 as well as 1–3; KNOWN_SCHEMA_VERSIONS in packages/aeg-core/src/log/store.ts includes 4, and classifyStoredLine returns a validated record for a schema: 4 line; schema.test.ts asserts a schema: 4 line missing process or runner, or with a string runner, is rejected, and a schema: 3 line still parses.
O2. buildHeader writes schema: 4, and this task adds the environment variables VINAYA_PROCESS_ID, VINAYA_PROCESS_VERSION, VINAYA_PROCESS_DIGEST, VINAYA_RUNNER_ID and VINAYA_RUNNER_VERSION: the process id defaults to vinaya and the runner id to standalone when absent or empty, and every version and digest absent or empty reads null; apps/cli/src/lib/log-sink.ts passes all five to buildHeader; VINAYA_RUNTIME still fills runtime exactly as on schema 3; packages/aeg-core/src/log/fixtures/generator.ts sets work.revision for a schema: 4 header as it does for schema: 3.
O3. @attalabs/aeg-core exports processAndRunnerOf(meta), which this task adds, returning the header's process and runner for a schema: 4 header, and process { id: 'vinaya', version: null, digest: null } with runner { id: 'standalone', version: null } for schema 1, 2 and 3.
O4. One table-driven test, packages/aeg-core/src/log/process-runner.test.ts, which this task adds, builds each row's header with buildHeader from its environment, parses it with HeaderMetaSchema, and asserts processAndRunnerOf returns the expected pair for exactly these five rows: vinaya + standalone; vinaya + atta-engine with a version and a digest; a process id other than vinaya + atta-engine; vinaya + a third-party runner id; and a stored schema: 3 line (not built), which reads as vinaya + standalone.
O5. The Engine runner sets the five variables before it creates its Log sink, through an exported function in processes/vinaya/bin/engine-log-identity.ts, which this task adds: process id, version and digest from the loaded process (identity.id, identity.version, digest of the harness loader's result), VINAYA_RUNNER_ID=atta-engine, and VINAYA_RUNNER_VERSION the version field of the installed @atta/engine package's own package.json; processes/vinaya/tests/engine-log-identity.test.ts asserts those five values for processes/vinaya.
O6. apps/log-server/src/worker.test.ts asserts an ingested schema: 4 event with runner atta-engine and one with runner standalone are each stored as valid events, not unknown_version or rejected, and an ingested schema: 3 event is still stored as valid.
Documentation
apps/cli/specs/log.md— the header field list names theprocessandrunnerobjects, the five environment variables, thatruntimekeeps its schema 3 meaning, and that a schema 1–3 line reads as processvinayaon runnerstandalone(O1, O2, O3)apps/log-server/specs/server.md— aschema: 4line is validated once the server is redeployed; until then it is stored asunknown_version; and the sentence "The sender writesschema: 3" now readsschema: 4(O6)processes/vinaya/README.md— the Engine runner stamps its Log events with the loaded process identity and runneratta-enginewith its version (O5)
Planner's rationale
Boundary — A new header schema version carrying the process (id, version, digest) and the runner (id, version) that produced each event, its producer wiring on both paths, a reader helper, and the server's acceptance test. Pinned files: packages/aeg-core/src/log/schema.ts, packages/aeg-core/src/log/store.ts, packages/aeg-core/src/log/fixtures/generator.ts, packages/aeg-core/src/log/envelope.ts, packages/aeg-core/src/log/schema.test.ts, packages/aeg-core/src/log/envelope.test.ts, packages/aeg-core/src/log/process-runner.test.ts, apps/cli/src/lib/log-sink.ts, apps/log-server/src/worker.test.ts, processes/vinaya/bin/engine-log-identity.ts, processes/vinaya/tests/engine-log-identity.test.ts, processes/vinaya/bin/run-engine.ts, apps/cli/specs/log.md, apps/log-server/specs/server.md. Out: deploying the Worker (a Principal step below), changing meta.flow or meta.source, any reader UI or Studio change, rewriting lines already on disk, any change to the standalone loop driver beyond the sink, the public portal's event-contract page (it still says schema 3; left for a later docs change).
doc-claims: the two specs' new sentences name only symbols this change adds or that exist on main (buildHeader, processAndRunnerOf, the five VINAYA_PROCESS_*/VINAYA_RUNNER_* variables, logEventSchemaDigest); no claim names a removed symbol.
reader-resolvable-prose: specs, READMEs and comments carry no Issue or pull-request numbers and no tranche slugs.
Sizing — One story: an event says which process and which runner wrote it. Schema, header, both producers and the server test fail together if split; the field is useless until both paths set it.
Project(s) + blast radius — Project: vinaya. @attalabs/aeg-core changes its exported schema and buildHeader; consumers: apps/cli (the sink, log cache and sync readers), apps/log-server (bundles the validator, so its schema_digest changes), processes/vinaya and harness (build headers in tests). Every test that asserts schema: 3 on a freshly built header moves to schema: 4; tests that parse stored schema 3 lines keep them. Both published packages need a changeset.
blast-radius-ack: aeg-core consumers enumerated above; the Worker needs a redeploy after merge.
Dependency rationale — Depends-on: #1564 — the Engine runner processes/vinaya/bin/run-engine.ts, where the sink is created, does not exist on main until that task merges; O5 edits it. Only that one file is shared with that task (below the collision threshold of three), but it cannot be edited before it exists.
Traps to avoid — Do NOT add process or runner to the schema: 3 meta, and do NOT change runtime on any version: that meta is strict, so an older server would reject such a line as invalid instead of keeping it as unknown_version; a new version is the only lossless path (the HeaderMetaV3Schema doc comment states the same rule for 2 → 3). Do NOT make the runner id an enum: a third-party runner names itself. Do NOT read process from meta.flow; flow stays as on schema 3. Do NOT read the Engine version from a hard-coded string or the tarball file name; read the installed package's package.json. Do NOT hand-edit a digest constant: logEventSchemaDigest derives itself.
Suggested agent-class — mid: an additive schema version plus mechanical test updates across known files.
Stop-and-escalate — Escalate when processes/vinaya/tests/engine-log-identity.test.ts cannot resolve a version for the installed Engine package; name the path it tried.
Docs to keep coherent — apps/cli/specs/log.md, apps/log-server/specs/server.md, processes/vinaya/README.md. Doc-owners derivation: apps/log-server/src/** binds apps/log-server/specs/server.md; packages/aeg-core/src/log and apps/cli/src/lib/log-sink.ts are documented by apps/cli/specs/log.md.
Principal step after merge — the Log server validates against the schema it bundled, so redeploy it or every schema: 4 event is stored as unknown_version:
[principal] cd apps/log-server && bunx wrangler deploy, then vinaya doctor shows the [logs] schema digest matching. No replay is needed: schema 4 lines that reached the old server are stored as unknown_version events, not rejected, and the redeployed server validates them. Deploy the Worker before any CLI release that writes schema 4.
Premises
packages/aeg-core/src/log/schema.tscontainsconst HeaderMetaSchema = z.discriminatedUnion('schema', [HeaderMetaV1Schema, HeaderMetaV2Schema, HeaderMetaV3Schema])packages/aeg-core/src/log/schema.tscontainsmeta: HeaderMetaV3Schema,packages/aeg-core/src/log/envelope.tscontainsexport function buildHeader(input: HeaderInput): Header {packages/aeg-core/src/log/envelope.tscontainsschema: 3,apps/cli/src/lib/log-sink.tscontainsruntime: env.VINAYA_RUNTIME,processes/vinaya/package.jsoncontains"@atta/engine": "file:../../apps/cli/tests/fixtures/atta-engine/atta-engine-0.0.1.tgz",apps/log-server/package.jsoncontains"deploy": "wrangler deploy",
Surface
in: packages/aeg-core/src, apps/cli/src, apps/cli/specs, apps/cli/tests, apps/log-server, processes, harness, .changeset
out: aeg-root
Parts
Part 1 (O1, O3, O4) — The schema 4 meta, the custom family, the store versions, processAndRunnerOf and the table test.
Part 2 (O2) — buildHeader, the sink, and moving fresh-header test assertions to schema 4.
Part 3 (O5) — The Engine log identity function, its test, and the runner call.
Part 4 (O6) — The Worker ingest test, specs and the changeset.
Test plan
cd packages/aeg-core && bunx vitest run src/log/schema.test.ts src/log/envelope.test.ts src/log/store.test.ts src/log/process-runner.test.ts src/log/schema-digest.test.ts → 0 fail
cd apps/cli && bun test tests/lib/log-sink.test.ts → 0 fail
cd apps/log-server && bunx vitest run src/worker.test.ts → 0 fail
cd processes/vinaya && bun test tests/engine-log-identity.test.ts → 0 fail
bunx turbo typecheck --affected → exits 0
Stop conditions
processes/vinaya/tests/engine-log-identity.test.tscannot resolve aversionfor the installed Engine package.
Origin
Principal rule, 2026-10-22: Engine test runs must never mix with standalone-loop data in the Vinaya Log; every event records the process and the runtime (with versions) that produced it, as two independent objects (field runner; runtime already holds the agent vendor).
Activity
[-][engine-harness-v1] 7 — Every Log event records which backend produced it and the Engine version[/-][+][engine-harness-v1] 7 — Every Log event records the process and the runner that produced it[/+]daniboomerang commentedon Oct 22, 2026
Brief hash: 819b4c50b71ced0abb03d86fca37e27557c11dbbbc29251a0c38880d5467eb1e
For: [model] (coding-agent CLI on a dev machine, dispatched locally, unattended)
Reason: Suggested agent-class — mid: an additive schema version plus mechanical test updates across known files.
Owner: the Principal
Goal: Every Log event records the process and the runner that produced it
Project: vinaya
Tier: 1
Closes #1574
You are the AEG Developer. Run
bun apps/cli/src/index.ts doctrine --role developer --printand read its output first. Mandatory.Objectives
O1.
packages/aeg-core/src/log/schema.tsadds aschema: 4header meta: everyschema: 3field unchanged (includingruntime, stillstring | null), plus a newprocessobject{ id: string, version: string | null, digest: string | null }and a newrunnerobject{ id: string, version: string | null }(ida free string, not an enum);HeaderMetaSchemaand thecustomfamily acceptschema: 4as well as 1–3;KNOWN_SCHEMA_VERSIONSinpackages/aeg-core/src/log/store.tsincludes4, andclassifyStoredLinereturns a validated record for aschema: 4line;schema.test.tsasserts aschema: 4line missingprocessorrunner, or with a stringrunner, is rejected, and aschema: 3line still parses.O2.
buildHeaderwritesschema: 4, and this task adds the environment variablesVINAYA_PROCESS_ID,VINAYA_PROCESS_VERSION,VINAYA_PROCESS_DIGEST,VINAYA_RUNNER_IDandVINAYA_RUNNER_VERSION: the processiddefaults tovinayaand the runneridtostandalonewhen absent or empty, and every version and digest absent or empty readsnull;apps/cli/src/lib/log-sink.tspasses all five tobuildHeader;VINAYA_RUNTIMEstill fillsruntimeexactly as on schema 3;packages/aeg-core/src/log/fixtures/generator.tssetswork.revisionfor aschema: 4header as it does forschema: 3.O3.
@attalabs/aeg-coreexportsprocessAndRunnerOf(meta), which this task adds, returning the header'sprocessandrunnerfor aschema: 4header, andprocess { id: 'vinaya', version: null, digest: null }withrunner { id: 'standalone', version: null }for schema 1, 2 and 3.O4. One table-driven test,
packages/aeg-core/src/log/process-runner.test.ts, which this task adds, builds each row's header withbuildHeaderfrom its environment, parses it withHeaderMetaSchema, and assertsprocessAndRunnerOfreturns the expected pair for exactly these five rows:vinaya+standalone;vinaya+atta-enginewith a version and a digest; a process id other thanvinaya+atta-engine;vinaya+ a third-party runner id; and a storedschema: 3line (not built), which reads asvinaya+standalone.O5. The Engine runner sets the five variables before it creates its Log sink, through an exported function in
processes/vinaya/bin/engine-log-identity.ts, which this task adds: process id, version and digest from the loaded process (identity.id,identity.version,digestof the harness loader's result),VINAYA_RUNNER_ID=atta-engine, andVINAYA_RUNNER_VERSIONtheversionfield of the installed@atta/enginepackage's ownpackage.json;processes/vinaya/tests/engine-log-identity.test.tsasserts those five values forprocesses/vinaya.O6.
apps/log-server/src/worker.test.tsasserts an ingestedschema: 4event with runneratta-engineand one with runnerstandaloneare each stored as valid events, notunknown_versionor rejected, and an ingestedschema: 3event is still stored as valid.Documentation
apps/cli/specs/log.md— the header field list names theprocessandrunnerobjects, the five environment variables, thatruntimekeeps its schema 3 meaning, and that a schema 1–3 line reads as processvinayaon runnerstandalone(O1, O2, O3)apps/log-server/specs/server.md— aschema: 4line is validated once the server is redeployed; until then it is stored asunknown_version; and the sentence "The sender writesschema: 3" now readsschema: 4(O6)processes/vinaya/README.md— the Engine runner stamps its Log events with the loaded process identity and runneratta-enginewith its version (O5)Premises
Checked against the code when this task Issue was cut, and again against the default branch at dispatch — facts, not claims.
packages/aeg-core/src/log/schema.tscontainsconst HeaderMetaSchema = z.discriminatedUnion('schema', [HeaderMetaV1Schema, HeaderMetaV2Schema, HeaderMetaV3Schema])packages/aeg-core/src/log/schema.tscontainsmeta: HeaderMetaV3Schema,packages/aeg-core/src/log/envelope.tscontainsexport function buildHeader(input: HeaderInput): Header {packages/aeg-core/src/log/envelope.tscontainsschema: 3,apps/cli/src/lib/log-sink.tscontainsruntime: env.VINAYA_RUNTIME,processes/vinaya/package.jsoncontains"@atta/engine": "file:../../apps/cli/tests/fixtures/atta-engine/atta-engine-0.0.1.tgz",apps/log-server/package.jsoncontains"deploy": "wrangler deploy",2. Context — read before doing anything
engine-harness-v1, task 7, Issue [engine-harness-v1] 7 — Every Log event records the process and the runner that produced it #1574. Branchtask/engine-harness-v1/7.Depends-on: #1564,Conflicts-with: —.c9cd70aec33e92de7430ddd1c7db2f35f41aa28a— the checkout's HEAD equaled the remote default branch, and no pinned file below carried an uncommitted change, when these facts were read.packages/aeg-core/src/log/schema.ts,packages/aeg-core/src/log/store.ts,packages/aeg-core/src/log/fixtures/generator.ts,packages/aeg-core/src/log/envelope.ts,packages/aeg-core/src/log/schema.test.ts,packages/aeg-core/src/log/envelope.test.ts,packages/aeg-core/src/log/process-runner.test.ts,apps/cli/src/lib/log-sink.ts,apps/log-server/src/worker.test.ts,processes/vinaya/bin/engine-log-identity.ts,processes/vinaya/tests/engine-log-identity.test.ts,processes/vinaya/bin/run-engine.ts,apps/cli/specs/log.md,apps/log-server/specs/server.md. Out: deploying the Worker (a Principal step below), changingmeta.flowormeta.source, any reader UI or Studio change, rewriting lines already on disk, any change to the standalone loop driver beyond the sink, the public portal's event-contract page (it still says schema 3; left for a later docs change).doc-claims: the two specs' new sentences name only symbols this change adds or that exist on main (buildHeader,processAndRunnerOf, the fiveVINAYA_PROCESS_*/VINAYA_RUNNER_*variables,logEventSchemaDigest); no claim names a removed symbol.reader-resolvable-prose: specs, READMEs and comments carry no Issue or pull-request numbers and no tranche slugs.processorrunnerto theschema: 3meta, and do NOT changeruntimeon any version: that meta is strict, so an older server would reject such a line as invalid instead of keeping it asunknown_version; a new version is the only lossless path (theHeaderMetaV3Schemadoc comment states the same rule for 2 → 3). Do NOT make the runneridan enum: a third-party runner names itself. Do NOT readprocessfrommeta.flow;flowstays as on schema 3. Do NOT read the Engine version from a hard-coded string or the tarball file name; read the installed package'spackage.json. Do NOT hand-edit a digest constant:logEventSchemaDigestderives itself.3. Technical dependencies
Dependency rationale —
Depends-on: #1564— the Engine runnerprocesses/vinaya/bin/run-engine.ts, where the sink is created, does not exist on main until that task merges; O5 edits it. Only that one file is shared with that task (below the collision threshold of three), but it cannot be edited before it exists.4. Technical surface map
Create:
Modify:
packages/aeg-core/src
apps/cli/src
apps/cli/specs
apps/cli/tests
apps/log-server
processes
harness
.changeset
consumer-tests: none — no consumer test path named yet for @attalabs/aeg-core (apps/cli), @attalabs/aeg-core (packages/sources), @attalabs/aeg-core (processes/vinaya); name one before dispatch, or confirm no consumer-facing behavior changed.
consumer-tests: apps/log-server/src/worker.test.ts (covers @attalabs/aeg-core)
Out of surface: 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, O3, O4): The schema 4 meta, the custom family, the store versions,
processAndRunnerOfand the table test.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):
buildHeader, the sink, and moving fresh-header test assertions to schema 4.Files (touches @attalabs/log-server):
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 3 (O5): The Engine log identity function, its test, and the runner call.
Files (touches @attalabs/aeg-core):
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 4 (O6): The Worker ingest test, specs and the changeset.
Files (touches the repo root):
The pre-push hook runs the affected suite on your one push and refuses it on failure — do not run it yourself per Part.
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
processes/vinaya/tests/engine-log-identity.test.tscannot resolve aversionfor the installed Engine package.Stop-and-escalate — Escalate when
processes/vinaya/tests/engine-log-identity.test.tscannot resolve aversionfor the installed Engine package; name the path it tried.11. Constraints
12. Deliverable
[engine-harness-v1] 7 — Every Log event records the process and the runner that produced itopen_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 #1574at 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.