EVA wire-up — register a consumer repo against the three EVA surfaces
EVA wire-up — register a consumer repo against the three EVA surfaces
Section titled “EVA wire-up — register a consumer repo against the three EVA surfaces”What this is. A single prompt that walks Claude Code through wiring one repo to EVA’s three production surfaces:
events(run-event ingest),control(queue mutation API), andprompts(prompt library). Each surface is independently gated; surfaces not applicable to the consumer repo are declarednot_applicablewith a reason and skipped entirely. The mission ends with a contract flip via consumer-repo PR and validation againsteva-hq:contracts/consumption.v1.json. Paste verbatim into a fresh Claude Code session opened at the consumer repo’s root (not petrova-codes, not eva-hq).The mission is gated: the agent halts at every
<gate/>for explicit operator authorisation. Do not skip gates. Do not echo secrets.Verb form:
petrova act eva-wire(thin wrapper not yet built; for now, paste).
Prompt to paste
Section titled “Prompt to paste”You are a single-repo EVA wiring agent, executing this brief against the codebase your working directory is rooted in. Your work is scoped to ONE consumer repository — do NOT reach across repos unless this brief explicitly directs you to.
After your work lands, the host repo’s applicable EVA surfaces are wired and evidenced, petrova doctor --slug=<<SLUG>> reports eva: ok, and .petrova/contract.yaml.integrations.eva holds a valid eva.consumption.v1 block. Reversible: revert the contract block to per-surface state: pending (EVA-side configuration is not persistent state — no append-only constraint applies).
Non-goals
Section titled “Non-goals”- Do NOT modify eva-hq internals (queue, prompt library, control API schema, or CI). You call documented public endpoints and use the published composite action; you do not extend EVA itself.
- Do NOT invent new evidence shapes. The per-surface evidence schema is enforced by
eva-hq:contracts/consumption.v1.json:eventsrequiresworkflow+action_ref+last_run_id+last_emitted_at;controlrequiresclient_ref+last_health_ok_at;promptsrequiresmanifest+eva_sha. - Do NOT push to
mainof any repo without explicit per-step operator authorisation. All content lands via PR. - Do NOT author the
evidence_consumerblock. That block is materialised bypetrova-verify-roundrunningeva consumption mirroragainstdist/evidence/<slug>/in eva-hq. Leave it absent; verify-round writes it on its next pass. - Do NOT synthesise CI runs. Taskset 3 requires the consumer’s CI to actually execute and land a real run ID.
Airlock constraints
Section titled “Airlock constraints”- EVA endpoints (
eventsandcontrolsurfaces) are authenticated. The token name is<<EVA_TOKEN_NAME>>(defaultPETROVA_EVA_TOKEN). Never echo, log, or commit the token value. Use${VAR:+set}substitution patterns when verifying presence. - The EVA endpoint base resolves from
PETROVA_EVA_ENDPOINTenv var, or the documented public default<<EVA_ENDPOINT>>(=eva.re). - Never commit
~/Documents/<vault>paths, operator-private hostnames, or private project information. <<OPERATOR>>resolves to a public handle only (e.g.Dev4rno). It never expands to an email address, internal ID, or private name.- If the sandbox denies a read or write, stop and ask the operator. Do not work around with creative re-routing.
- Every action that mutates shared state (CI workflow edits, control-API client commits, contract flip) pauses for explicit operator confirmation, even in auto-mode.
Phase 0 — Petrova-aware preflight
Section titled “Phase 0 — Petrova-aware preflight”Before anything else, run:
SLUG="$(yq '.slug' .petrova/contract.yaml)"petrova doctor --slug="$SLUG"If eva.current_status == "ok" AND every applicable surface in the contract block reads state: wired AND python <eva-hq-root>/bin/eva consumption validate --file .petrova/contract.yaml exits 0, the mission is a no-op. Surface this to the operator and exit — do not proceed.
If the slug is not in petrova-codes’s registry.yaml, halt and ask the operator whether to onboard the repo first (via core/prompts/05-petrova-onboard.md). Do not proceed until the slug is registered.
If integrations_applicability.eva for this repo (in petrova-codes:registry.yaml) is not required, halt and ask the operator whether the registry should be updated first. Proceeding without required applicability means the contract flip will not be accepted by the doctor sweep.
Contract with EVA
Section titled “Contract with EVA”Three surfaces, all documented in eva-hq:docs/decisions/2026-05-08-eva-consumption-contract.md. The endpoint base is <<EVA_ENDPOINT>> (resolved by the probe to PETROVA_EVA_ENDPOINT env var, or the public default eva.re).
events — run-event ingest
Section titled “events — run-event ingest”- Producer integration: the
eva-emitGitHub composite action ateva-hq/eva/.github/actions/eva-emit@v0, called from the consumer’s CI workflow. - Auth:
Authorization: Bearer $<<EVA_TOKEN_NAME>>(the action readsEVA_INGEST_TOKENfrom the calling workflow’s secrets;<<EVA_TOKEN_NAME>>is the consumer’s local name for the same secret). - Evidence when
state: wired:workflow(path to the CI YAML, e.g..github/workflows/ci.yml),action_ref(eva-hq/eva/.github/actions/eva-emit@v0),last_run_id(a real run ID returned by the ingest endpoint),last_emitted_at(ISO timestamp of that run).
control — queue mutation API
Section titled “control — queue mutation API”- Endpoints:
GET/POST <<EVA_ENDPOINT>>/api/health,GET <<EVA_ENDPOINT>>/api/tasks/list,POST <<EVA_ENDPOINT>>/api/tasks/transition. - Auth: bearer token as above.
- Evidence when
state: wired:client_ref(path or module ref to the consumer’s EVA control client),last_health_ok_at(ISO timestamp of a successful/api/healthcall).
prompts — prompt library
Section titled “prompts — prompt library”- The consumer maintains an
eva-prompts.lock.jsonmanifest that maps EVA prompt IDs to pinned git SHAs from eva-hq. - No auth required to read the prompt catalog from a public-readable git ref.
- Evidence when
state: wired:manifest(path toeva-prompts.lock.jsonin the consumer repo),eva_sha(the eva-hq commit SHA currently pinned).
petrova eva-status (operator-facing diagnostic)
Section titled “petrova eva-status (operator-facing diagnostic)”petrova eva-status <<SLUG>> [--workspace <path>] [--json]— read-only; calls/api/healthon the control surface and reads the lock manifest; always exits 0; never gates merges.python <eva-hq-root>/bin/eva consumption validate --file .petrova/contract.yaml— validates the full block againstcontracts/consumption.v1.json; must exit 0 at Taskset 6 before opening the PR.
Taskset ladder
Section titled “Taskset ladder”Each taskset halts on explicit GO TASKSET N. Work only the gated taskset. Do NOT speculatively stage the next.
Surfaces where the operator confirmed not_applicable in Taskset 1 cause their corresponding tasksets (3, 4, or 5) to be skipped entirely. The audit deliverable records which tasksets apply.
Taskset 0 — Petrova-aware preflight (auto, no separate gate)
Section titled “Taskset 0 — Petrova-aware preflight (auto, no separate gate)”This taskset is the Phase 0 block above. If petrova doctor reports a no-op, exit. Otherwise surface the result and wait for GO TASKSET 1.
Taskset 1 — Audit (read-only)
Section titled “Taskset 1 — Audit (read-only)”Deliverable: docs/findings/<<YYYYMMDD-HHMM>>-eva-wire-audit-<<SLUG>>.md.
Collect and record:
-
Per-surface applicability. For each of
events,control,prompts, determine whether the consumer repo actually uses or should use that surface. Look in.github/workflows/,src/,internal/, and any existing EVA client code. Emit a recommendation for each surface:wiredornot_applicable(with a one-line reason ≤ 120 chars). -
Existing EVA footprint. If
<<EVA_TOKEN_NAME>>is set, probe the control surface:Terminal window if [ -n "${<<EVA_TOKEN_NAME>>:+set}" ]; thencurl -s -H "Authorization: Bearer $<<EVA_TOKEN_NAME>>" \"${PETROVA_EVA_ENDPOINT:-<<EVA_ENDPOINT>>}/api/health" \| jq -r '.status // "unreachable"'elseecho "skipped: <<EVA_TOKEN_NAME>> not set; will check in Taskset 2"fiAlso check whether
.github/workflows/already callseva-emit, and whethereva-prompts.lock.jsonalready exists. -
Recommended
<<EVA_TOKEN_NAME>>. Default convention:PETROVA_EVA_TOKEN. Surface for operator confirmation; the operator may override to match an existing secrets convention in the consumer repo. -
Contract block readiness. Read
.petrova/contract.yamland note the currentintegrations.evablock status (pending, a partialeva.consumption.v1block, or absent). If absent, the full block will be authored at Taskset 6. -
Taskset skip map. Present a compact table: surface, recommended state, taskset that will execute it. Mark skipped tasksets clearly.
Commit the findings file on its own branch labelled docs. Open a PR. Do not block on its merge.
<gate/> Halt with the path to the audit findings doc and the taskset skip map in a compact table. Present per-surface recommendations. Do not proceed without explicit operator OK on each surface decision.
Taskset 2 — Token verify (conditional: skip if ALL surfaces are not_applicable for events and control)
Section titled “Taskset 2 — Token verify (conditional: skip if ALL surfaces are not_applicable for events and control)”Verify the operator has <<EVA_TOKEN_NAME>> set in the active shell. Never echo the value.
if [ -n "${<<EVA_TOKEN_NAME>>:+set}" ]; then echo "<<EVA_TOKEN_NAME>>: set"else echo "<<EVA_TOKEN_NAME>>: missing"fiIf missing and any events or control surface will be wired, halt. The operator must export the token before continuing:
# Operator action — do not commit this:export <<EVA_TOKEN_NAME>>="<paste-from-1password>"Tokens are issued by the EVA admin (out of scope for this prompt). The operator is responsible for token rotation and storage in their secret manager. If prompts is the only wired surface, this taskset is informational — no token is required for the prompts surface.
<gate/> Token gate — proceed only if the token requirement for the planned surfaces is satisfied. Do not store, log, or transmit the token value.
Taskset 3 — Wire events surface (skip if not_applicable)
Section titled “Taskset 3 — Wire events surface (skip if not_applicable)”Install eva-hq/eva/.github/actions/eva-emit@v0 as a step in the consumer’s CI workflow. The step must run after the build/test job and post both a started and terminal (passed | failed) event.
Minimal step example (adapt to the consumer’s workflow structure):
- name: Emit run event to EVA uses: eva-hq/eva/.github/actions/eva-emit@v0 if: always() with: run_id: ${{ github.run_id }} status: ${{ job.status }} env: EVA_INGEST_TOKEN: ${{ secrets.<<EVA_TOKEN_NAME>> }} EVA_ENDPOINT: ${{ vars.PETROVA_EVA_ENDPOINT || 'https://eva.re' }}After committing the workflow change to a branch, trigger a CI run and wait for it to complete. Capture:
LAST_RUN_ID="<run_id from the CI run>"LAST_EMITTED_AT="<ISO timestamp from the action's output or the ingest response>"<gate/> Halt with the CI run URL, last_run_id, and last_emitted_at. Operator confirms the run landed in EVA (check petrova eva-status <<SLUG>> or the EVA dashboard) before proceeding.
Taskset 4 — Wire control surface (skip if not_applicable)
Section titled “Taskset 4 — Wire control surface (skip if not_applicable)”Stand up a minimal EVA control-API client in the consumer repo. The client must be able to call at least GET /api/health. Location: follow the consumer’s conventions (e.g. src/clients/eva.ts, internal/eva/client.go, lib/eva_client.py). The client reads EVA_INGEST_TOKEN (or <<EVA_TOKEN_NAME>>) from the environment.
After committing the client, call /api/health once and capture the timestamp:
HEALTH_RESPONSE="$(curl -sS \ -H "Authorization: Bearer $<<EVA_TOKEN_NAME>>" \ "${PETROVA_EVA_ENDPOINT:-<<EVA_ENDPOINT>>}/api/health")"echo "$HEALTH_RESPONSE" | jq '.'LAST_HEALTH_OK_AT="$(echo "$HEALTH_RESPONSE" | jq -r '.checked_at // empty')"[ -z "$LAST_HEALTH_OK_AT" ] && LAST_HEALTH_OK_AT="$(date -u +%Y-%m-%dT%H:%M:%SZ)"If the response is 401/403, the token is invalid or lacks scope. Halt; the operator rotates the token.
<gate/> Halt with client_ref (the path or module ref committed) and last_health_ok_at. Operator confirms.
Taskset 5 — Wire prompts surface (skip if not_applicable)
Section titled “Taskset 5 — Wire prompts surface (skip if not_applicable)”Generate or refresh the eva-prompts.lock.json manifest in the consumer repo root (or another operator-specified path). The manifest maps EVA prompt IDs — whichever the consumer uses — to the current eva-hq HEAD SHA.
EVA_SHA="$(git -C <path-to-eva-hq> rev-parse HEAD)"Minimal manifest shape:
{ "schema": "eva-prompts-lock.v1", "eva_sha": "<<EVA_SHA>>", "locked_at": "<<ISO_DATE>>", "prompts": [ { "id": "<prompt-id>", "path": "prompts/<file>.md" } ]}Populate prompts[] with the IDs the consumer actually references. If the consumer does not yet reference any prompt IDs directly, record an empty array and note this in the decision doc.
<gate/> Halt with the path to eva-prompts.lock.json and the pinned eva_sha. Operator confirms the pinned SHA is the intended one.
Taskset 6 — Contract flip
Section titled “Taskset 6 — Contract flip”Scope: host (consumer) repo. One PR containing the contract edit and the decision doc.
- Populate the
eva.consumption.v1block in.petrova/contract.yaml:
integration_eva: schema: eva.consumption.v1 status: <<DERIVED>> # derived by eva consumption validate; do not author manually declared_at: <<ISO_DATE>> wired_at: <<ISO_DATE>> wired_decision_doc: docs/decisions/<<ISO_DATE>>-wire-eva-<<SLUG>>.md surfaces: events: state: wired # or: not_applicable evidence: workflow: <<WORKFLOW_PATH>> action_ref: eva-hq/eva/.github/actions/eva-emit@v0 last_run_id: <<LAST_RUN_ID>> last_emitted_at: <<LAST_EMITTED_AT>> control: state: wired # or: not_applicable evidence: client_ref: <<CLIENT_REF>> last_health_ok_at: <<LAST_HEALTH_OK_AT>> prompts: state: wired # or: not_applicable evidence: manifest: <<MANIFEST_PATH>> eva_sha: <<EVA_SHA>>For any surface confirmed not_applicable, replace the evidence sub-block with a reason field (≤ 120 chars). The status top-level field is derived; set it to wired if any surface is wired, else pending, else not_applicable.
Do NOT add an evidence_consumer block — verify-round materialises it.
- Validate the contract block:
python <eva-hq-root>/bin/eva consumption validate --file .petrova/contract.yamlMust exit 0. If it fails, fix the block and re-run before proceeding.
-
Author the decision doc at
docs/decisions/<<ISO_DATE>>-wire-eva-<<SLUG>>.md. Use the petrova-codes decision-doc template. Cover:- Context: applicability from
petrova-codes:registry.yaml, sourced fromeva-hq:docs/decisions/2026-05-08-eva-consumption-contract.md. - Decision: authorise the wiring per
core/prompts/10-eva-wire.mdtaskset ladder. State which surfaces are wired and which arenot_applicable, with the operator-confirmed reasons. - Consequences:
petrova doctor --slug=<<SLUG>>will reporteva: ok;evidence_consumerwill be materialised by next verify-round run; contract block flips to the appropriatestatusin this PR. - Rollback: revert the contract block surfaces to
state: pending. EVA-side configuration has no persistent append-only state; no Pebble-style registration to unwind.
- Context: applicability from
-
Open the consumer-repo PR:
gh pr create \ --title "feat(eva): wire <<SLUG>> to eva.consumption.v1" \ --body "..."PR body cites docs/decisions/<<ISO_DATE>>-wire-eva-<<SLUG>>.md and lists the per-surface states and evidence pointers.
<gate/> Halt with the consumer-repo PR URL. Do not merge without explicit operator authorisation.
Communication protocol
Section titled “Communication protocol”- plan-announce: First turn of a new taskset → emit a numbered plan block for that taskset only. Do not execute.
- await-gate: End the turn with
Awaiting GO TASKSET <N> to proceed.and nothing more. - execute: On
GO TASKSET <N>, execute steps in order. One tool call per atomic action. Narrate each in one short sentence before calling. - verify: Run the taskset’s verify block. Do NOT proceed.
- summarise: Two-line status: what changed, what the operator should see/do next.
Sandbox denials
Section titled “Sandbox denials”- Read denied → stop, surface command + reason verbatim, ask operator to either authorise once or run the command themselves and paste result.
- Write denied → never retry with a “cleverer” shape. Stop, surface, ask.
Secret denials
Section titled “Secret denials”<<EVA_TOKEN_NAME>>is a bearer token. Pipe it via the env (-H "Authorization: Bearer $<<EVA_TOKEN_NAME>>"); never print, log, or commit the value. Use${VAR:+set}substitution patterns when verifying presence.- If a curl or action response includes the token in any field (it should not), redact before surfacing.
<<OPERATOR>>is a public handle. Never expand it to an email address or internal ID in any committed file.
Cross-fleet compatibility
Section titled “Cross-fleet compatibility”-
All taskset numbers are global. Prefix PR titles
feat(eva): [EVA-WIRE/<n>] <summary>. -
Resources you create carry discoverable tags:
- Consumer contract path:
.petrova/contract.yaml#integration_eva. - Lock manifest:
<<MANIFEST_PATH>>(typicallyeva-prompts.lock.json). - Consumer decision doc:
docs/decisions/<<ISO_DATE>>-wire-eva-<<SLUG>>.md. - Token name:
<<EVA_TOKEN_NAME>>(secret in CI; env var in shell). These tags are the rollback handle.
- Consumer contract path:
-
EVA surfaces are independent across consumer slugs. Concurrent wirings from multiple repos do not interact.
-
If the CI workflow already calls
eva-emitwhen the agent reaches Taskset 3, capture the existing run ID andlast_emitted_atfrom the most recent run and proceed — no duplicate step needed. -
Pipelining rule: consumer-repo PRs (decision doc + contract flip) may overlap across slugs. Each repo’s wiring is isolated.
-
Placeholder inventory (all tokens that appear in committed files must be resolvable at runtime):
Placeholder Resolves to <<SLUG>>yq '.slug' .petrova/contract.yaml<<EVA_TOKEN_NAME>>Default PETROVA_EVA_TOKEN; operator may rename<<EVA_ENDPOINT>>Default eva.re; overridden byPETROVA_EVA_ENDPOINT<<ISO_DATE>>Today in YYYY-MM-DD<<YYYYMMDD-HHMM>>Current datetime for findings file name <<OPERATOR>>Public git handle only <<EVA_SHA>>git -C <eva-hq-root> rev-parse HEAD<<WORKFLOW_PATH>>Path to the consumer CI YAML that calls eva-emit<<LAST_RUN_ID>>Run ID from the first wired CI run <<LAST_EMITTED_AT>>ISO timestamp of that run’s ingest <<CLIENT_REF>>Path or module ref to the consumer’s control client <<MANIFEST_PATH>>Path to eva-prompts.lock.json<<LAST_HEALTH_OK_AT>>ISO timestamp of last successful /api/healthcall
Halt rules summary
Section titled “Halt rules summary”- After Phase 0: auto-proceed only if
evais not yetokAND applicability isrequired. Halt and surface if no-op detected OR if applicability is notrequired. - After Taskset 1 (audit):
<gate/>— operator OKs per-surface decisions (wiredvsnot_applicable) and confirms<<EVA_TOKEN_NAME>>. - At Taskset 2 (token verify):
<gate/>— halt if<<EVA_TOKEN_NAME>>is unset and anyeventsorcontrolsurface will be wired; do not proceed until operator confirms it is set. - At Taskset 3 (events surface):
<gate/>— operator confirms the CI run landed in EVA before proceeding. - At Taskset 4 (control surface):
<gate/>— operator confirmsclient_refand/api/healthresponse before proceeding. - At Taskset 5 (prompts surface):
<gate/>— operator confirms the pinnedeva_shabefore proceeding. - At Taskset 6 with the consumer-repo PR URL:
<gate/>— operator merges. Validate passes before PR is opened.
Success criteria
Section titled “Success criteria”| ID | Verifiable claim |
|---|---|
| sc-1 | python <eva-hq-root>/bin/eva consumption validate --file .petrova/contract.yaml exits 0 |
| sc-2 | For each surface with state: wired: the evidence pointers are real and dereferenceable (CI run ID exists; /api/health returned 200 at last_health_ok_at; eva-prompts.lock.json exists at manifest) |
| sc-3 | petrova doctor --slug=<<SLUG>> reports eva: ok |
| sc-4 | docs/decisions/<<ISO_DATE>>-wire-eva-<<SLUG>>.md exists, is dated, and references the per-surface evidence |
| sc-5 | No secret, vault path, private hostname, or private project data appears in any committed file |
| sc-6 | .petrova/contract.yaml.integration_eva.status equals wired (or pending / not_applicable if all surfaces are non-wired, per derive_status()) |
| sc-7 | evidence_consumer block is absent from the authored contract (verify-round writes it on next pass) |
Secret hygiene
Section titled “Secret hygiene”EVA events and control surfaces are auth-gated. The token (<<EVA_TOKEN_NAME>>) is a bearer secret.
- Never commit the token value. Never echo it. Never log it. Use
${VAR:+set}substitution patterns when verifying presence. - The token lives in the operator’s secret manager (1Password, vault, etc.). The operator exports it into the active shell at the start of each session, and stores it as a CI repository secret under the agreed name.
- Token rotation is the operator’s responsibility. If a token is rotated mid-wiring, halt at the next taskset gate and re-verify presence.
- Never commit
~/Documents/<vault>paths or operator-private hostnames. Use the documented public default (<<EVA_ENDPOINT>>) or the env varPETROVA_EVA_ENDPOINT. <<OPERATOR>>resolves to a public handle only.- The
promptssurface requires no token; do not apply token hygiene rules to the lock manifest file.
First-turn instructions
Section titled “First-turn instructions”- Acknowledge this mission in one short paragraph. Name the host repo (inferred from
pwdorgit remote) and the single target: wiring to EVA’s three surfaces (events,control,prompts) and a contract flip per this prompt. - Run Phase 0 (
petrova doctor --slug=<self>). If already-wired no-op detected, exit. - Run a quick readiness probe: check
yq .slug .petrova/contract.yaml,yq '.integrations.eva' .petrova/contract.yaml, and[ -n "${<<EVA_TOKEN_NAME>>:+set}" ] && echo "token: set" || echo "token: missing". Emit results in a compact table. - Present the taskset ladder as a numbered plan tailored to the host repo (note any expected skips — e.g. if the repo has no CI, Taskset 3 is likely
not_applicable; if no EVA queue integration, Taskset 4 is likelynot_applicable). Call out any surfaces the audit will need to decide. - End with
Awaiting GO TASKSET 1 to begin the audit.Do nothing else.