Skip to content

LORE + CAIRNET wire-up — register a consumer repo with Pebble's knowledge collection and agent emission stream

LORE + CAIRNET wire-up — register a consumer repo with Pebble’s knowledge collection and agent emission stream

Section titled “LORE + CAIRNET wire-up — register a consumer repo with Pebble’s knowledge collection and agent emission stream”

What this is. A single prompt that walks Claude Code through wiring one repo to LORE (Pebble’s knowledge-collection registry) and CAIRNET (Pebble’s agent-emission stream). Two operator-driven Pebble HTTP POSTs (one to register the LORE collection, one to register the CAIRNET agent), a deterministic wire-time integration stone emitted in Taskset 5, and finally a contract flip via consumer-repo PR. Paste verbatim into a fresh Claude Code session opened at the consumer repo’s root (not petrova-codes, not Pebble).

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 rocky-wire (a thin wrapper that opens an editor pre-loaded with this prompt — wrapper not yet built; for now, paste).


You are a single-repo LORE + CAIRNET 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 LORE collection is registered in Pebble under <<LORE_COLLECTION_SLUG>>, the CAIRNET agent is registered as <<CAIRNET_AGENT_ID>>, the agent has emitted its first stone, and petrova doctor --slug=<<SLUG>> reports lore_cairnet: ok. Reversible: revert the contract block to status: pending (Pebble registrations are append-only by convention).

  • Do NOT modify Pebble internals (storage, query, build pipeline, or schema). You make two HTTP POSTs against documented public endpoints and read back the responses; you do not extend Pebble itself.
  • Do NOT invent new evidence shapes. The contract’s lore_cairnet evidence shape is enforced by petrova-codes:contracts/contract.schema.json (lore_collection_slug + cairnet_agent_id required; cairnet_stones_endpoint, emission_rate_30d, emission_budget_30d, decay_policy optional).
  • Do NOT push to main of any repo without explicit per-step operator authorisation. All content lands via PR.
  • Taskset 5 emits exactly one wire-time integration stone via petrova wire_integration. The wiring itself is a genuine governance event; this is not synthesised emission — it is the first real governance stone. Do NOT emit additional stones from this prompt beyond that single TS5 stone.
  • Pebble is an authenticated HTTP service. The token name is <<PEBBLE_TOKEN_NAME>> (default PETROVA_PEBBLE_TOKEN). Never echo, log, or commit the token value. Use ${VAR:+set} substitution patterns when verifying presence.
  • Pebble registrations are append-only by convention. Never DELETE a registered collection or agent. If the wiring needs to be undone, the operator opens a separate decision doc; the registration is left in place.
  • 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 (Pebble registrations, flipping the contract block) pauses for explicit operator confirmation, even in auto-mode.

Before anything else, run:

Terminal window
SLUG="$(yq '.slug' .petrova/contract.yaml)"
petrova doctor --slug="$SLUG"

If lore_cairnet.current_status == "ok" AND the contract block reads status: wired, 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.lore_cairnet 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.


Two surfaces, both reached via <<PEBBLE_ENDPOINT>> (default resolved by the probe to PETROVA_PEBBLE_ENDPOINT env var, or the documented public default).

LORE — knowledge-collection registry (write-side, this prompt)

Section titled “LORE — knowledge-collection registry (write-side, this prompt)”
  • POST <<PEBBLE_ENDPOINT>>/api/knowledge/repos — register a collection. Body: {"slug": "<<LORE_COLLECTION_SLUG>>", "owner_repo": "<<SLUG>>", ...}. Auth: Authorization: Bearer $<<PEBBLE_TOKEN_NAME>>.
  • GET <<PEBBLE_ENDPOINT>>/api/knowledge/repos/<<LORE_COLLECTION_SLUG>> — verify registration. Read-side, used by the probe.

CAIRNET — agent emission stream (write-side, this prompt)

Section titled “CAIRNET — agent emission stream (write-side, this prompt)”
  • POST <<PEBBLE_ENDPOINT>>/api/cairn/agents — register an agent. Body: {"agent_id": "<<CAIRNET_AGENT_ID>>", "owner_repo": "<<SLUG>>", ...}. Auth: as above.
  • GET <<PEBBLE_ENDPOINT>>/api/cairn/stones?agent=<<CAIRNET_PRODUCER_PRINCIPAL>> — list stones for the producer principal. Read-side, used by the probe + the petrova lore-cairnet-status verb. The query param is agent= (Pebble has no since param — the 30-day window is applied client-side by the probe over each stone’s createdAt). Its value is the producer principal (agent:petrova-cairnet-bridge), not the collection-owner agent id — Pebble records the wire-time stone under the agent:-prefixed bridge principal, so the probe must filter by it. In a literal curl URL the value MUST be URL-encoded (: → %3A): ?agent=agent%3Apetrova-cairnet-bridge.

petrova lore-cairnet-status (operator-facing diagnostic)

Section titled “petrova lore-cairnet-status (operator-facing diagnostic)”
  • petrova lore-cairnet-status <<SLUG>> [--workspace <path>] [--json] — read-only; same 2 GETs as the probe; prints LORE registration status, CAIRNET emission rate vs emission_budget_30d, top-5 latest stones. Always exits 0; never gates merges.
  • The probe (loreCairnetProbe in petrova-codes:cli/src/probes/lore_cairnet.ts) checks two conditions in order: (1) the LORE collection responds 2xx, (2) the CAIRNET stone count over 30d is in band — 0 → failing (silent agent), > budget → failing (over budget), > 80% of budget → degraded, else → ok.

Each taskset halts on explicit GO TASKSET N. Work only the gated taskset. Do NOT speculatively stage the next.

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.

Deliverable: docs/findings/<<YYYYMMDD-HHMM>>-lore-cairnet-wire-audit-<<SLUG>>.md.

Collect and record:

  1. Existing agent + emission patterns in this repo. Identify any code paths that already emit logs, events, or audit records. Look in agent/, councils/, src/, internal/, and any *.log.* files. Note the natural points where CAIRNET stones could be authored (decisions, observations, questions, escalations).

  2. Existing LORE/CAIRNET footprint. If <<PEBBLE_TOKEN_NAME>> is set, query Pebble:

    Terminal window
    if [ -n "${<<PEBBLE_TOKEN_NAME>>:+set}" ]; then
    curl -s -H "Authorization: Bearer $<<PEBBLE_TOKEN_NAME>>" \
    "${PETROVA_PEBBLE_ENDPOINT:-<<PEBBLE_ENDPOINT>>}/api/knowledge/repos/<<LORE_COLLECTION_SLUG>>" \
    | jq -r '.slug // "not_registered"'
    else
    echo "skipped: <<PEBBLE_TOKEN_NAME>> not set; will check in Taskset 2"
    fi
  3. Recommended <<LORE_COLLECTION_SLUG>> and <<CAIRNET_AGENT_ID>>. Default convention:

    • <<LORE_COLLECTION_SLUG>> = <<SLUG>> (the consumer’s registry slug).
    • <<CAIRNET_AGENT_ID>> = <<SLUG>>-<role>-001 where <role> is fleet, coordinator, or another short noun describing the agent’s purpose (e.g. grace-fleet-001, kahn-coordinator-001). Surface both for operator confirmation; the operator may override.

    Two distinct identities — do not interchange them:

    • <<CAIRNET_AGENT_ID>> = the collection-owner / registered-agent id (<<SLUG>>-<role>-001, operator-chosen). It identifies who owns the lore collection and is the agent registered via POST /api/cairn/agents (TS4). Operator-confirmed at the TS1 gate.
    • <<CAIRNET_PRODUCER_PRINCIPAL>> = the wire-time-stone producer principal, fixed to agent:petrova-cairnet-bridge. It is not operator-chosen and is not the collection-owner id. It is the constant bridge principal that emits the wire-time governance stone (TS5) and is the value Pebble records as the stone’s agent_id — therefore it is what the rocky probe filters Pebble stones by (evidence cairnet_agent_id, stones-GET ?agent=, sc-2 curl). Pebble’s principal provenance (_principal_triple) records machine-agent stones under an agent: prefix applied to the bridge OAuth client_id, so the value the operator writes into the rocky evidence cairnet_agent_id and uses in the probe/sc-2 query is literally agent:petrova-cairnet-bridge — the bare petrova-cairnet-bridge matches zero stones (that was the SP-1.5 seam defect). Always agent:petrova-cairnet-bridge; the operator does not change it.

    These are different identities for different jobs (registry/ownership vs stone-production/probe-filtering); never substitute one for the other.

  4. Recommended <<EMISSION_BUDGET_30D>>. Default suggestion: 50 stones in 30 days. Calibrate after observing wave-1’s actual emission rate. Document the chosen value in the audit findings.

  5. Recommended <<DECAY_POLICY>>. Default: 90d. This is operator-tunable; it documents how long stones are queryable before archival.

  6. Contract block readiness. Read .petrova/contract.yaml and note the current integrations.lore_cairnet block status (pending, wired, or absent). If absent, it will be added at Taskset 6.

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. Present the recommended <<LORE_COLLECTION_SLUG>>, <<CAIRNET_AGENT_ID>>, <<EMISSION_BUDGET_30D>>, and <<DECAY_POLICY>> in a compact table. Do not proceed without explicit operator OK on each.

Verify the operator has <<PEBBLE_TOKEN_NAME>> set in the active shell. Never echo the value.

Terminal window
if [ -n "${<<PEBBLE_TOKEN_NAME>>:+set}" ]; then
echo "<<PEBBLE_TOKEN_NAME>>: set"
else
echo "<<PEBBLE_TOKEN_NAME>>: missing"
fi

If missing, halt. The operator must export the token before continuing:

Terminal window
# Operator action — do not commit this:
export <<PEBBLE_TOKEN_NAME>>="<paste-from-1password>"

Tokens are issued by the Pebble admin (out of scope for this prompt). The operator is responsible for token rotation and storage in their secret manager.

<gate/> Token gate — proceed only if <<PEBBLE_TOKEN_NAME>> is confirmed set. Do not store, log, or transmit the token value.

Taskset 3 — Register LORE collection (Pebble-side POST)

Section titled “Taskset 3 — Register LORE collection (Pebble-side POST)”

One HTTP POST against Pebble. No PR. The token is piped from the env; the response body is captured into a local variable and surfaced to the operator (do not commit it).

Terminal window
LORE_RESPONSE="$(curl -sS -X POST \
-H "Authorization: Bearer $<<PEBBLE_TOKEN_NAME>>" \
-H "Content-Type: application/json" \
-d "{\"slug\":\"<<LORE_COLLECTION_SLUG>>\",\"owner_repo\":\"<<SLUG>>\"}" \
"${PETROVA_PEBBLE_ENDPOINT:-<<PEBBLE_ENDPOINT>>}/api/knowledge/repos")"
echo "$LORE_RESPONSE" | jq '.'

Expected: 201 Created with the collection echoed back. If the response is 409 Conflict, the collection already exists — capture the existing record and proceed.

If the response is 401/403, the token is invalid or lacks scope. Halt; the operator rotates the token.

<gate/> Halt with the LORE response payload (slug, registered_at). Operator confirms the registration is correct before proceeding.

Taskset 4 — Register CAIRNET agent (Pebble-side POST)

Section titled “Taskset 4 — Register CAIRNET agent (Pebble-side POST)”

Same pattern, second endpoint:

Terminal window
AGENT_RESPONSE="$(curl -sS -X POST \
-H "Authorization: Bearer $<<PEBBLE_TOKEN_NAME>>" \
-H "Content-Type: application/json" \
-d "{\"agent_id\":\"<<CAIRNET_AGENT_ID>>\",\"owner_repo\":\"<<SLUG>>\"}" \
"${PETROVA_PEBBLE_ENDPOINT:-<<PEBBLE_ENDPOINT>>}/api/cairn/agents")"
echo "$AGENT_RESPONSE" | jq '.'

Expected: 201 Created. 409 Conflict means the agent_id already exists — capture and proceed.

<gate/> Halt with the CAIRNET response payload (agent_id, registered_at). Operator confirms.

Taskset 5 — wire-time integration stone (intrinsic)

Section titled “Taskset 5 — wire-time integration stone (intrinsic)”

Run the wire verb. It emits the wire-time integration stone synchronously as part of wiring and records first_emitted_at automatically — there is no flag and no separate emit step. (This replaces the old “wait for first natural emission” gate, which is why repos without a bespoke emission path could never reach ok. Governance events are now emitted as CAIRNET stones by the petrova write-verbs; the wire-up is the first such event.)

Terminal window
petrova wire_integration --integration rocky --repo <<SLUG>>

The stone is emitted only when the wire passes its pre-flight refusal gates — a refused wire emits nothing. On a passing wire this POSTs one agentic-emission.v1 stone via the petrova CAIRNET emitter (source_artifact.kind = petrova.integration.wired, stone_type = connection, producer_role = bridge, agent_id = agent:petrova-cairnet-bridge) and records its emitted_at as first_emitted_at in the rocky evidence block. If Pebble is briefly unreachable the stone is queued and first_emitted_at is still set from its deterministic timestamp; petrova doctor pre-flushes the outbox before the probe reads.

Read back the first_emitted_at the wire verb already recorded (do not recompute it — this is a confirmation read, not the source of truth):

Terminal window
FIRST_EMISSION="$(petrova rocky-status <<SLUG>> --json \
| jq -r '.cairnet.latest_stones[0].emitted_at')"
echo "wire-time stone emitted at: $FIRST_EMISSION"

<gate/> Halt with the recorded first_emitted_at. The operator confirms the stone registered: petrova rocky-status <<SLUG>> (or petrova lore-cairnet-status) shows >= 1 stone. That value is the first_emitted_at TS5 already wrote to the rocky evidence block; Taskset 6 only carries it into .petrova/contract.yaml. If the repo is genuinely inapplicable (applicability for rocky is not required in petrova-codes:registry.yaml — surfaced at Phase 0), the operator may flip the slug to not_applicable instead; the wire-time emit is not a fallback for repos that simply lack a bespoke emission path.

Scope: host (consumer) repo. One PR containing the contract edit and the decision doc.

  1. Populate the evidence block in .petrova/contract.yaml:
lore_cairnet:
status: wired
declared_at: <<ISO_DATE>>
evidence:
lore_collection_slug: <<LORE_COLLECTION_SLUG>>
cairnet_agent_id: <<CAIRNET_PRODUCER_PRINCIPAL>>
first_emitted_at: <<EMITTED_AT>>
emission_budget_30d: <<EMISSION_BUDGET_30D>>
cadence_floor_days: 30
decay_policy: <<DECAY_POLICY>>

Fill in:

  • <<ISO_DATE>> — today’s date in YYYY-MM-DD format.
  • <<LORE_COLLECTION_SLUG>> — from Taskset 3 (registered collection).
  • cairnet_agent_id — set to <<CAIRNET_PRODUCER_PRINCIPAL>> (agent:petrova-cairnet-bridge), not the operator-chosen <<CAIRNET_AGENT_ID>>. The rocky probe filters Pebble stones by this field, and the TS5 wire-time stone is recorded under the agent:-prefixed bridge principal; using the collection-owner id here makes the probe find zero stones → failing. The collection-owner id <<CAIRNET_AGENT_ID>> from Taskset 4 is recorded in the decision doc / registration payload, not in this evidence field.
  • <<EMITTED_AT>> — paste the exact ISO-8601 datetime captured in Taskset 5 ($FIRST_EMISSION echo). Any truncation/reformatting makes the rocky probe treat first_emitted_at as unparseable → failing once the 30-day window passes.
  • <<EMISSION_BUDGET_30D>>, <<DECAY_POLICY>> — from Taskset 1 (operator-confirmed).
  • cadence_floor_days: 30 — write explicitly to pin the default (the rocky probe uses this as the idle-grace window, T13/T14); an operator may set a larger integer for a low-churn repo.
  1. Author the decision doc at docs/decisions/<<ISO_DATE>>-wire-lore-cairnet-<<SLUG>>.md. Use the petrova-codes decision-doc template. Cover:

    • Context: applicability from petrova-codes:registry.yaml, sourced from docs/decisions/2026-05-01-lore-cairnet-wire-adoption.md.
    • Decision: authorise the wiring per core/prompts/09-lore-cairnet-wire.md taskset ladder. LORE collection slug <<LORE_COLLECTION_SLUG>>, CAIRNET agent id <<CAIRNET_AGENT_ID>>, initial emission_budget_30d <<EMISSION_BUDGET_30D>>, decay policy <<DECAY_POLICY>>.
    • Consequences: petrova doctor --slug=<<SLUG>> reports lore_cairnet: ok; wire-time integration stone emitted at $FIRST_EMISSION (TS5); contract block flips to wired in this PR.
    • Rollback: revert the contract block to status: pending. Pebble registrations remain (append-only by convention).
  2. Open the consumer-repo PR:

    Terminal window
    gh pr create \
    --title "feat(lore_cairnet): wire <<SLUG>> to LORE + CAIRNET" \
    --body "..."

    PR body cites docs/decisions/<<ISO_DATE>>-wire-lore-cairnet-<<SLUG>>.md and includes the LORE + CAIRNET registration response payloads (slug, agent_id, registered_at).

<gate/> Halt with the consumer-repo PR URL. Do not merge without explicit operator authorisation.


  • 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.
  • 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.
  • <<PEBBLE_TOKEN_NAME>> is a bearer token. Pipe it via the env (-H "Authorization: Bearer $<<PEBBLE_TOKEN_NAME>>"); never print, log, or commit the value. Use ${VAR:+set} substitution patterns when verifying presence.
  • If a curl 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.

  • All taskset numbers are global. Prefix PR titles feat(lore_cairnet): [LC-WIRE/<n>] <summary>.
  • Resources you create carry discoverable tags:
    • LORE collection slug: <<LORE_COLLECTION_SLUG>> (typically equal to the consumer’s registry slug).
    • CAIRNET agent id: <<CAIRNET_AGENT_ID>> (typically <slug>-<role>-NNN).
    • Consumer decision doc: docs/decisions/<<ISO_DATE>>-wire-lore-cairnet-<<SLUG>>.md. These tags are the rollback handle.
  • Pebble registrations are independent across slugs — concurrent wirings from multiple consumer repos do not interact. Each repo’s agent runs its own POSTs against Pebble.
  • If the LORE collection or CAIRNET agent is already registered when the agent reaches Taskset 3 or 4 (e.g. operator pre-registered manually), the 409 Conflict response is the success signal — capture the existing record and proceed.
  • Pipelining rule: consumer-repo PRs (decision doc + contract flip) may overlap across slugs. Pebble-side registrations are HTTP POSTs, not PRs — no review serialisation.

  • After Phase 0: auto-proceed only if lore_cairnet is not yet ok AND applicability is required. Halt and surface if no-op detected OR if applicability is not required.
  • After Taskset 1 (audit): <gate/> — operator OKs <<LORE_COLLECTION_SLUG>>, <<CAIRNET_AGENT_ID>>, <<EMISSION_BUDGET_30D>>, <<DECAY_POLICY>>.
  • At Taskset 2 (token verify): <gate/> — halt if <<PEBBLE_TOKEN_NAME>> is unset; do not proceed until operator confirms it is set.
  • At Taskset 3 (LORE registration): <gate/> — operator confirms the response payload before proceeding.
  • At Taskset 4 (CAIRNET registration): <gate/> — operator confirms the response payload before proceeding.
  • At Taskset 5 (wire-time integration stone): <gate/> — run petrova wire_integration --integration rocky --repo <<SLUG>> (a passing wire emits the stone and records first_emitted_at intrinsically; a refused wire emits nothing); operator confirms petrova rocky-status <<SLUG>> shows >= 1 stone and reads back the recorded first_emitted_at.
  • At Taskset 6 with the consumer-repo PR URL: <gate/> — operator merges.

IDVerifiable claim
sc-1curl -H "Authorization: Bearer $<<PEBBLE_TOKEN_NAME>>" "<<PEBBLE_ENDPOINT>>/api/knowledge/repos/<<LORE_COLLECTION_SLUG>>" returns 200
sc-2curl -H "Authorization: Bearer $<<PEBBLE_TOKEN_NAME>>" "<<PEBBLE_ENDPOINT>>/api/cairn/stones?agent=agent%3Apetrova-cairnet-bridge" returns total >= 1 (query param is agent=, value is the producer principal agent:petrova-cairnet-bridge URL-encoded as agent%3Apetrova-cairnet-bridge — not the collection-owner id; Pebble has no since param, the probe windows 30d client-side over createdAt)
sc-3petrova lore-cairnet-status <<SLUG>> prints cairnet: <N>/<<EMISSION_BUDGET_30D>> stones with N within budget
sc-4docs/decisions/<<ISO_DATE>>-wire-lore-cairnet-<<SLUG>>.md exists, is dated, and references the registration response payloads
sc-5No secret, vault path, private hostname, or private project data appears in any committed file
sc-6.petrova/contract.yaml.integrations.lore_cairnet.status == "wired" and petrova doctor --slug=<<SLUG>> reports lore_cairnet: ok

Pebble is auth-gated. The token (<<PEBBLE_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.
  • 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 (<<PEBBLE_ENDPOINT>>) or the env var PETROVA_PEBBLE_ENDPOINT.
  • <<OPERATOR>> resolves to a public handle only.

  1. Acknowledge this mission in one short paragraph. Name the host repo (inferred from pwd or git remote) and the single target: LORE + CAIRNET registration and contract flip per this prompt.
  2. Run Phase 0 (petrova doctor --slug=<self>). If already-wired no-op detected, exit.
  3. Run a quick readiness probe: check yq .slug .petrova/contract.yaml, yq '.integrations.lore_cairnet' .petrova/contract.yaml, and [ -n "${<<PEBBLE_TOKEN_NAME>>:+set}" ] && echo "token: set" || echo "token: missing". Emit results in a compact table.
  4. Present the taskset ladder as a numbered plan tailored to the host repo (note any skips — e.g. if the LORE collection or CAIRNET agent is already registered, Tasksets 3/4 capture the existing record and proceed; if <<PEBBLE_TOKEN_NAME>> is unset, surface that immediately and pause Taskset 2 until the operator exports it).
  5. End with Awaiting GO TASKSET 1 to begin the audit. Do nothing else.