Merge pull request #72 from living-ip/codex/working-leo-db-path-20260709
Some checks are pending
CI / lint-and-test (push) Waiting to run

Add guarded canonical claim application
This commit is contained in:
twentyOne2x 2026-07-10 17:51:20 +02:00 committed by GitHub
commit 771679061f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
37 changed files with 22266 additions and 388 deletions

View file

@ -1,3 +1,8 @@
---
name: crabbox
description: Use for remote Linux verification, isolated CI proof, Crabbox lease cleanup, and PR evidence without production deploys or secret forwarding.
---
# Crabbox # Crabbox
Use Crabbox for remote Linux verification and PR proof only. Use Crabbox for remote Linux verification and PR proof only.

View file

@ -1,85 +1,88 @@
--- ---
name: teleo-gcp-parity-ops name: teleo-gcp-parity-ops
description: Use for Teleo/Leo GCP parity, Cloud SQL/cutover readiness, account selection, non-interactive auth blockers, and separating GCP from VPS Telegram proof. description: Use for Teleo GCP control-plane inventory, Cloud SQL read-only parity, authenticated Chrome routing, VM/runtime comparison, and exact GCP access gates without collapsing VPS proof into GCP proof.
--- ---
# Teleo GCP Parity Ops # Teleo GCP Parity Ops
## Job ## Working Target
Handle GCP parity as its own lane, with precise auth/account/project facts and no overclaiming from VPS or local proof. Prove the real GCP resource, database, runtime, and Cory-answer paths separately.
Control-plane inventory alone is not database or Leo parity.
## Trigger Phrases ## Current Verified Inventory
- "GCP parity" - Project: `teleo-501523` (`Teleo`).
- "move Leo to GCP" - Authenticated Console account: redacted `b***@livingip.xyz` via `authuser=5`.
- "Cloud SQL Teleo" - VM: `teleo-prod-1`, running in `europe-west6-a`.
- "teleo-501523" - Cloud SQL: exactly one instance, `teleo-pgvector-standby`.
- "GCP blocker" - Database engine: PostgreSQL `16.14`, Enterprise.
- "VPS then GCP" - Region/zone: `europe-west6` / `europe-west6-c`, single-zone.
- Network: private-only `teleo-staging-net`; public IP disabled.
- Connection name: `teleo-501523:europe-west6:teleo-pgvector-standby`.
- Cloud SQL Studio loads, but the observed session exposes no enabled existing
IAM DB principal and selects built-in password authentication.
## Current Known Facts Read these before refreshing:
- Project: `teleo-501523` - `docs/reports/leo-working-state-20260709/gcp-cloud-sql-t3-live-readonly-current.json`
- Expected project-access account: `billy@livingip.xyz` - `docs/reports/leo-working-state-20260709/gcp-cloudsql-studio-parity-gate-current.json`
- Retained blocker: selected account cannot refresh access token non-interactively. - `docs/reports/leo-working-state-20260709/gcp-parallel-delegation-current.json`
- Other locally available accounts refresh but lack access to `teleo-501523`.
- Current probe artifact: `docs/reports/leo-working-state-20260709/gcp-db-parity-current-probe-current.json`
- Prior auth-capable blocker artifact: `docs/reports/leo-working-state-20260709/gcp-db-parity-current-blocker-20260709.json`
The current probe may report `blocked_on_network_dns_before_auth_refresh` when the ## Browser Rule
runner cannot resolve `oauth2.googleapis.com`; that is an environment/network
gate before the prior Google account reauth or project-permission checks.
Always refresh if cheap before making a current claim. Use authenticated Chrome/Computer Use for Console-only facts when CLI auth is
stale. Never use AppleScript, `osascript`, JXA, `open`, System Events, or a
focus-stealing fallback. Do not inspect cookies, tokens, saved passwords, or
secret values.
## Status Split ## Required Parity Rows
Never claim GCP complete from: Track each independently:
- VPS service health, 1. control-plane project/VM/Cloud SQL inventory;
- Telegram group canaries, 2. database name, principal, transaction-read-only proof, schema, counts, and
- local DB rehearsals, selected row identities;
- GCP account selection alone. 3. VM process/service/repo/profile/model/bot configuration;
4. no-post `DC-01` through `DC-06` replies and score;
5. cleanup and no-mutation readback.
GCP parity needs its own: ## Cloud SQL Read-Only Query
- auth readback, Only after Studio is already authenticated with an existing principal:
- project access readback,
- Cloud SQL or target DB readback,
- runtime/config parity readback,
- Telegram or operator path proof if the target is GCP-hosted Leo.
## Safe Actions ```sql
begin transaction read only;
Allowed: select current_database(), current_user,
current_setting('transaction_read_only');
- `gcloud config list` / account list readbacks, select count(*) from public.claims;
- `scripts/probe_gcp_db_parity.py`, select count(*) from public.sources;
- non-secret auth status checks, select count(*) from public.claim_evidence;
- project access probes, select count(*) from public.claim_edges;
- read-only SQL or service discovery where auth works, select count(*) from kb_stage.kb_proposals;
- blocker artifact updates. rollback;
Stop for:
- password/OTP/KYC,
- raw secret requests,
- irreversible cloud changes,
- production cutover without explicit authorization.
## Blocker Format
If auth is still blocked, retain:
```json
{
"current_canary": "gcloud account can read teleo-501523 and target DB metadata",
"attempted_routes": [],
"exact_gate": "",
"clear_CTA": "",
"next_non_user_action": ""
}
``` ```
The CTA must name the exact account and what the operator should do, not a vague "reauth needed." Do not submit a raw password, create a user, change IAM, enable a flag, open
browser SSH that creates a key, or mutate infrastructure under this read-only
skill.
## Exact Current Gate
Database parity is unproven because Studio requires a database credential and
did not expose an enabled existing IAM principal. The clear CTA is:
```text
Authenticate Cloud SQL Studio with an already-existing authorized database
principal without sharing the credential, then tell Codex: GCP Studio authenticated.
```
Otherwise route IAM/database-user enablement to a separately authorized write
window. Once authenticated, run only the read-only transaction above, retain
sanitized results, and close the agent-created tab.
## Claim Ceiling
Current proof covers the GCP project, VM, Cloud SQL instance, health, version,
and private routing surface. It does not cover DB contents, VPS-vs-GCP schema or
row parity, application connectivity, GCP Leo behavior, or cutover.

View file

@ -1,79 +1,103 @@
--- ---
name: teleo-kb-db-change-workflow name: teleo-kb-db-change-workflow
description: Use for Teleo KB proposal normalization, approved-to-canonical DB change workflows, clone rehearsals, apply packets, rollback plans, and row-level proof without silently mutating production. description: Use for Teleo canonical Postgres changes, proposal normalization, separate review and apply authority, isolated approve_claim canaries, row-level proof, rollback, and production-apply gating.
--- ---
# Teleo KB DB Change Workflow # Teleo KB DB Change Workflow
## Job ## Working Target
Turn approved KB proposal intent into a strict, reviewable, rehearsed, row-level DB change contract. Move one reviewed knowledge change from `kb_stage.kb_proposals` to exact
canonical rows without confusing approval with application or silently changing
## Trigger Phrases production.
- "manipulate the knowledge base"
- "approved proposal apply"
- "canonical DB rows"
- "kb_stage to public"
- "claim/source contract"
- "rollback rehearsal"
- "production apply packet"
## State Model ## State Model
Use these states exactly: - `pending_review`: staged, not approved, canonical rows unchanged.
- `approved`: reviewed intent exists, but `applied_at` may still be null.
- `applied`: the guarded transaction finished and canonical postflight matches.
- `packet_ready_not_executed`: rehearsed artifacts exist; production is unchanged.
- `proposed`: draft or staged proposal text exists. Never infer `applied` from chat text, a packet, a clone, or `status=approved`.
- `pending_review`: proposal exists but is not approved.
- `approved`: reviewer approved intent; canonical DB may still be unchanged.
- `applied`: canonical row mutation happened and was read back.
- `not_applied`: no canonical mutation exists.
- `packet_ready_not_executed`: SQL packet and rehearsal proof exist, but production was not changed.
## Current Table Contract ## Canonical Contract
Core canonical tables used in July 9 work: The strict `approve_claim` v2 payload may create:
- `public.claims` - `public.claims`
- `public.sources` - `public.sources`
- `public.claim_edges`
- `public.claim_evidence` - `public.claim_evidence`
- `public.claim_edges`
- `public.reasoning_tools` - `public.reasoning_tools`
Staging table: The lifecycle is split across:
- `kb_stage.kb_proposals` - `scripts/kb_proposal_normalize.py`: fail-closed rich intent to strict payload.
- `scripts/approve_proposal.py`: `kb_review` login approves the exact type/payload.
- `scripts/apply_proposal.py`: operator-only `kb_apply` login writes and verifies.
- `scripts/kb_apply_prereqs.sql`: roles, immutable approval row, gate functions,
ownership, indexes, and exact ACL matrix.
- `scripts/run_approve_claim_isolated_container_canary.sh`: disposable runtime
proof plus read-only live count/service endpoints.
## Required Workflow ## Authority Invariants
1. Fetch proposal and status from live DB. 1. `kb_review` and `kb_apply` are separate `NOINHERIT` login roles with no role
2. Normalize proposal payload into planned rows. memberships.
3. Preserve reviewer/caveat text as source/evidence/proposal body context. 2. `kb_gate_owner` is `NOLOGIN` and owns the immutable approval table and three
4. Reject or block unsupported edge types, unknown targets, and schema gaps unless a reviewed mapping overlay exists. `SECURITY DEFINER` gate functions.
5. Generate rollback-only SQL first. 3. The legacy three-argument approval overload must be absent.
6. Run live rollback rehearsal in a transaction. 4. Review binds DB role, reviewer principal, type, full payload, timestamp, and
7. Run disposable clone commit rehearsal. note in one immutable row.
8. Generate production packet only after clone proof passes. 5. Apply locks and compares that snapshot before writes, verifies every
9. Do not execute production commit unless explicitly authorized. payload-controlled field and exact table deltas, then finishes the ledger.
10. If production commit is authorized later, run preflight, commit, postflight, and service stability readback. 6. Existing semantic evidence/edge rows must match approved weights and owners;
a mismatch rolls back.
7. `kb_apply` remains a trusted operator-only canonical writer. This design does
not claim resistance to a compromised apply credential.
## Current Important Proposals ## Operator Path
- `00957f6c-9883-4015-95a4-6b09367efb0e`: strict `add_edge` canary, applied. 1. Read the proposal and canonical target rows.
- `8dfedb3f-3aa4-4200-970f-4c0016f6869f`: live Telegram staged proposal, pending review at the retained snapshot. 2. Normalize rich intent; reject malformed, duplicate, lossy, or unsupported
- `14fa5ecc-ac7a-41c1-807d-a2e85b936617`: approved rich proposal, mapped subset packet-ready, not production-applied. candidates instead of guessing.
- `ac036c9d-20a0-4ffe-881f-57d6b7bacf22`: approved rich proposal, mapped subset packet-ready, not production-applied. 3. Run focused tests:
- `a64df080-8502-42e2-98f4-9bbdecb8da73`: approved Helmer 7 Powers proposal, plan generated for reasoning tool plus claims/sources/edges; continue with rehearsal/packet proof.
## Claim Ceiling
Clone proof is not production apply. A packet file is not a DB change. Production apply requires explicit authority and postflight row readback.
## Validation
Run focused tests after planner/packet changes:
```bash ```bash
uv run --extra dev ruff check scripts/kb_rich_proposal_creation_plan.py scripts/kb_rich_proposal_apply_packet.py tests/test_kb_rich_proposal_creation_plan.py tests/test_kb_rich_proposal_apply_packet.py .venv/bin/python -m pytest -q \
uv run --extra dev pytest tests/test_kb_rich_proposal_creation_plan.py tests/test_kb_rich_proposal_apply_packet.py tests/test_kb_proposal_normalize.py \
tests/test_approve_proposal.py \
tests/test_apply_proposal.py \
tests/test_kb_apply_prereqs.py
``` ```
4. Run both disposable canaries from the repo root:
```bash
scripts/run_approve_claim_isolated_container_canary.sh \
--output docs/reports/leo-working-state-20260709/approve-claim-clone-canary-current.json
scripts/run_approve_claim_isolated_container_canary.sh \
--normalization-json docs/reports/leo-working-state-20260709/helmer-approve-claim-normalization-current.json \
--output docs/reports/leo-working-state-20260709/helmer-approve-claim-clone-canary-current.json
```
5. Require `status=pass`, every check true, expected deltas equal measured
deltas, source hashes matching current files, zero live count deltas, stable
service endpoints, and independent clone/container/workdir cleanup.
6. Treat production deployment, permission migration, worker enablement, and a
production proposal apply as separate authorization windows.
## Current Proof And Claim Ceiling
The retained generic and Helmer v3 receipts each pass `37/37`. Generic proves
`2/2/2/1/1`; Helmer proves `5/13/17/7/1`. Live read-only endpoint counts stayed
`1837/4145/4670/4916/17/26`, and the gateway stayed on PID `2999690` with zero
restarts during those runs.
This proves current-source behavior in disposable PostgreSQL and unchanged live
count/service endpoints. It does not prove byte-for-byte live content, code
deployment, the production permission migration, or Helmer application.
Read `docs/reports/leo-working-state-20260709/approve-claim-clone-canary-current.md`
and both JSON receipts before making a current claim.

View file

@ -24,7 +24,8 @@ Orient the worker before action. Build a current, proof-linked understanding of
- Leo is the operator-facing agent expected to answer in Telegram, remember operator context, reason from canonical KB state, stage concrete KB changes, and support approved changes becoming canonical DB rows with proof. - Leo is the operator-facing agent expected to answer in Telegram, remember operator context, reason from canonical KB state, stage concrete KB changes, and support approved changes becoming canonical DB rows with proof.
- The immediate July 9 issue is not generic bot liveness. It is Cory/m3taversal's expectation that approved KB changes move beyond proposal state when appropriate. - The immediate July 9 issue is not generic bot liveness. It is Cory/m3taversal's expectation that approved KB changes move beyond proposal state when appropriate.
- The VPS is the currently proven Telegram-visible Leo surface. - The VPS is the currently proven Telegram-visible Leo surface.
- GCP parity is a separate lane and is not proven current until the GCP blocker clears. - GCP is a separate lane. Project, VM, and private Cloud SQL inventory are now
current; DB contents, runtime parity, Cory answers, and cutover are not.
## Read First ## Read First
@ -34,6 +35,9 @@ From the repo root, read:
2. `docs/reports/leo-working-state-20260709/operator-surface-map.md` 2. `docs/reports/leo-working-state-20260709/operator-surface-map.md`
3. `docs/reports/leo-working-state-20260709/working-leo-definition-20260709.md` 3. `docs/reports/leo-working-state-20260709/working-leo-definition-20260709.md`
4. `docs/reports/leo-working-state-20260709/cory-expected-working-leo-outcomes-20260709.md` 4. `docs/reports/leo-working-state-20260709/cory-expected-working-leo-outcomes-20260709.md`
5. `docs/reports/leo-working-state-20260709/approve-claim-clone-canary-current.md`
6. `docs/reports/leo-working-state-20260709/gcp-cloud-sql-t3-live-readonly-current.md`
7. `docs/reports/leo-working-state-20260709/gcp-cloudsql-studio-parity-gate-current.md`
## Required Status Split ## Required Status Split
@ -55,6 +59,9 @@ Do not collapse GCP demo readiness into Telegram completion. Do not collapse pro
- Do not production-apply DB packets unless explicitly authorized. - Do not production-apply DB packets unless explicitly authorized.
- Do not expose secret contents. - Do not expose secret contents.
- Do not treat an old summary as current if a fresh readback is cheap. - Do not treat an old summary as current if a fresh readback is cheap.
- Do not call GCP blocked on CLI login when authenticated Chrome can answer the
control-plane question. Conversely, do not submit a raw DB password or create
IAM/users under a read-only lane.
## Output Format ## Output Format

View file

@ -26,6 +26,9 @@ Navigate and verify the VPS safely, with exact readbacks and no surprise live-ru
- Service: `leoclean-gateway.service` - Service: `leoclean-gateway.service`
- DB container: `teleo-pg` - DB container: `teleo-pg`
- DB: `teleo` - DB: `teleo`
- Last retained count endpoints: claims `1837`, sources `4145`, evidence `4670`,
edges `4916`, reasoning tools `17`, proposals `26`. Treat these as stale until
refreshed.
- Profile reports: `/home/teleo/.hermes/profiles/leoclean/kb_stage/reports/` - Profile reports: `/home/teleo/.hermes/profiles/leoclean/kb_stage/reports/`
- Deploy/source area: `/opt/teleo-eval/workspaces/deploy-infra` - Deploy/source area: `/opt/teleo-eval/workspaces/deploy-infra`
@ -38,6 +41,7 @@ systemctl show leoclean-gateway.service -p ActiveState -p SubState -p MainPID -p
``` ```
Before claiming DB state, use `docker exec teleo-pg psql -U postgres -d teleo` and query exact tables/rows. Before claiming DB state, use `docker exec teleo-pg psql -U postgres -d teleo` and query exact tables/rows.
Wrap verification queries in `begin transaction read only; ... rollback;`.
Before claiming cleanup, query disposable DBs: Before claiming cleanup, query disposable DBs:
@ -76,6 +80,19 @@ For DB rehearsals:
7. Verify production counts stayed unchanged. 7. Verify production counts stayed unchanged.
8. Verify the disposable DB no longer exists. 8. Verify the disposable DB no longer exists.
For the guarded claim-bundle lifecycle, prefer the retained isolated wrapper:
```bash
scripts/run_approve_claim_isolated_container_canary.sh \
--output docs/reports/leo-working-state-20260709/approve-claim-clone-canary-current.json
```
It must use a disposable unexposed PostgreSQL container, bind its receipt to
current source hashes, compare exact payload projections and table deltas, read
live count/service endpoints without writing them, and independently prove the
container/workdir are absent afterward. Do not install the candidate code into
the live Leo deploy merely to run this canary.
## Blocker Format ## Blocker Format
Do not say "blocked" without: Do not say "blocked" without:

View file

@ -1,6 +1,6 @@
--- ---
name: working-leo-cory-outcomes name: working-leo-cory-outcomes
description: Use when defining, testing, or repairing Leo against Cory/m3taversal's expected outcomes: Telegram memory, critical reasoning, canonical KB truth, proposed-vs-approved-vs-applied state, and guarded DB manipulation. description: "Use when defining, testing, or repairing Leo against Cory/m3taversal's expected outcomes: Telegram memory, critical reasoning, canonical KB truth, proposed-vs-approved-vs-applied state, and guarded DB manipulation."
--- ---
# Working Leo / Cory Outcomes # Working Leo / Cory Outcomes
@ -32,6 +32,9 @@ A working Leo is a Telegram-facing agent that:
8. Retains Cory's caveats and review notes in source/evidence/proposal rows. 8. Retains Cory's caveats and review notes in source/evidence/proposal rows.
9. Produces before/after table-level proof and service stability readback. 9. Produces before/after table-level proof and service stability readback.
10. Survives an intentional `leoclean-gateway.service` restart: active before, active after, canonical KB counts unchanged, and a no-post handler smoke still answers. 10. Survives an intentional `leoclean-gateway.service` restart: active before, active after, canonical KB counts unchanged, and a no-post handler smoke still answers.
11. For a reviewed graph bundle, produces exact payload-controlled row
projections, exact table deltas, source-byte binding, and cleanup proof in a
disposable runtime before any production permission or apply window.
## Current Benchmark Cases ## Current Benchmark Cases
@ -41,6 +44,27 @@ A working Leo is a Telegram-facing agent that:
- Staged write canary: proposal `8dfedb3f-3aa4-4200-970f-4c0016f6869f`, status `pending_review`, with no new public canonical rows after the test timestamp. - Staged write canary: proposal `8dfedb3f-3aa4-4200-970f-4c0016f6869f`, status `pending_review`, with no new public canonical rows after the test timestamp.
- Open-ended triage canary: `WL-LIVE-TG-CORY-OPEN-20260709`, where Leo inferred the likely failure mode from "agents not working / same state as last night" and explained the proposed, approved, and applied split without exact IDs. - Open-ended triage canary: `WL-LIVE-TG-CORY-OPEN-20260709`, where Leo inferred the likely failure mode from "agents not working / same state as last night" and explained the proposed, approved, and applied split without exact IDs.
- Old rich proposal packets: `14fa5ecc...`, `ac036c9d...`, and `a64df080...` are not to be silently production-applied. - Old rich proposal packets: `14fa5ecc...`, `ac036c9d...`, and `a64df080...` are not to be silently production-applied.
- Guarded apply proof: both generic and real Helmer v3 receipts pass `37/37` in
disposable PostgreSQL; production Helmer remains unapplied.
## Cory Direct Questions
For vague or no-context questions, answer directly and then name the one action
that would change the proof:
- "Did the DB change?": split applied rows, approved-but-unapplied proposals,
pending rows, and canceled rows. Say `Approved is not the same as applied`.
- "Is Helmer in Leo?": say `no, not canonical` until the exact claims, sources,
evidence, edges, reasoning tool, and applied ledger read back from production.
- "Did the decision matrix approve it?": verify matrix tables first; reviewer
approval is not a matrix vote.
- "Are document pointers the blocker?": answer `not just pointer mismatch` and
separate files, source refs, canonical source rows, review, and apply.
- "Can I demo KB mutation?": split staging-demo truth from canonical-apply truth.
- "Did editing SOUL.md change identity?": no canonical identity change without
row IDs plus render/sync postflight.
End no-context answers with exactly one `Next Cory-style follow-up:` line.
## Required Answer Discipline ## Required Answer Discipline

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,88 @@
# Approved Claim Bundle Isolation Canary
Status: `pass`
## Working Target
A Cory-approved strict bundle must move through a separate reviewer credential,
an immutable approval snapshot, and a narrow apply credential before exact
claim/source/evidence/edge/reasoning-tool rows become canonical. A stale payload,
replay, hash collision, or direct privilege bypass must leave no partial rows.
## Final Runtime Proof
- Generic bundle: `37/37` checks passed. Its exact payload-controlled projection
and exact table deltas were `2` claims, `2` sources, `2` evidence links, `1`
edge, and `1` reasoning tool.
- Real Helmer bundle: `37/37` checks passed. Its exact payload-controlled
projection and exact table deltas were `5` claims, `13` sources, `17`
evidence links, `7` edges, and `1` reasoning tool.
- Helmer source proposal: `a64df080-8502-42e2-98f4-9bbdecb8da73`; strict child
proposal in the final isolated run: `87344d36-5005-5d88-a04c-067aeceda3c3`.
- `kb_review` approved the exact pending type and full JSON payload as mapped
reviewer `m3ta`; the immutable approval row matched the proposal ledger.
- `kb_apply` could not directly update proposal status/payload, immutable
approval rows, or existing `claim_evidence` rows.
- Mutating the approved ledger after SQL generation made the stale apply fail
before canonical writes; restoring the ledger left the immutable approval
unchanged.
- Replay was refused. A different source ID reusing an existing source hash was
refused, and its proposal/approval remained intact with zero partial rows.
- Every payload-controlled persisted field, including weights, roles, owners,
tags, hashes, and relationships, matched the reviewed payload. Exact table
deltas also exclude unexpected extra rows under unrelated IDs.
- The migration readback proves one expected signature per gate function, no
legacy overload, no protected-role membership, no object owned by a login
role, and the exact reviewer/apply execute split.
## Isolation And Cleanup
- Both runs used a new unexposed `postgres:16` container built from a read-only
schema/reference copy of live `teleo`; no cluster-wide role was created in the
production `teleo-pg` instance.
- The wrapper issued read-only queries to live `teleo-pg`; endpoint counts were
identical before and after each final run, with zero deltas for all six tables:
`claims=1837`, `sources=4145`, `claim_evidence=4670`, `claim_edges=4916`,
`reasoning_tools=17`, `kb_proposals=26`. This is endpoint-count evidence, not
a claim that every live row byte was compared.
- `leoclean-gateway.service` stayed `active/running`, `MainPID=2999690`,
`NRestarts=0`.
- Inner clone databases, outer Postgres containers, temporary workdirs, and the
remote source bundle were removed. Independent name/path readbacks found no
residue after cleanup.
- Both receipts retain repo-relative SHA-256/size bindings for the apply,
approval, prerequisite SQL, inner canary, and isolation wrapper source files;
a local recomputation matched every retained hash.
- No Telegram message, production canonical apply, production permission
migration, service restart, or runtime-profile write occurred.
## Reproduce
Generic bundle:
```bash
scripts/run_approve_claim_isolated_container_canary.sh \
--output docs/reports/leo-working-state-20260709/approve-claim-clone-canary-current.json
```
Real normalized Helmer bundle:
```bash
scripts/run_approve_claim_isolated_container_canary.sh \
--normalization-json docs/reports/leo-working-state-20260709/helmer-approve-claim-normalization-current.json \
--output docs/reports/leo-working-state-20260709/helmer-approve-claim-clone-canary-current.json
```
## Evidence And Claim Ceiling
- Generic receipt: `approve-claim-clone-canary-current.json`
- Helmer receipt: `helmer-approve-claim-clone-canary-current.json`
- Lossless normalization: `helmer-approve-claim-normalization-current.json`
- Inner harness: `scripts/run_approve_claim_clone_canary.py`
- Isolation wrapper: `scripts/run_approve_claim_isolated_container_canary.sh`
This proves the repo-owned review/apply lifecycle at isolated runtime tier. It
does not mean the new code, reviewer/apply roles, or permission migration are
deployed on VPS production, and it does not mean Helmer is canonical there.
`kb_apply` remains an operator-only trusted canonical writer; the proof covers
the supplied tool and privilege matrix, not a compromised apply credential.

View file

@ -55,6 +55,12 @@ Use this file before making status claims. Prefer fresh VPS/GCP readbacks when c
- Strict apply plan: `docs/reports/leo-working-state-20260709/kb-apply-canary-plan-current.md` - Strict apply plan: `docs/reports/leo-working-state-20260709/kb-apply-canary-plan-current.md`
- Strict apply execute: `docs/reports/leo-working-state-20260709/kb-apply-canary-execute-current.md` - Strict apply execute: `docs/reports/leo-working-state-20260709/kb-apply-canary-execute-current.md`
- Approved claim-bundle isolation canary: `docs/reports/leo-working-state-20260709/approve-claim-clone-canary-current.md`
- Generic approved claim-bundle isolation receipt: `docs/reports/leo-working-state-20260709/approve-claim-clone-canary-current.json`
- Helmer approved claim-bundle isolation receipt: `docs/reports/leo-working-state-20260709/helmer-approve-claim-clone-canary-current.json`
- Lossless Helmer strict normalization: `docs/reports/leo-working-state-20260709/helmer-approve-claim-normalization-current.json`
- Approved claim-bundle lifecycle harness: `scripts/run_approve_claim_clone_canary.py`
- Approved claim-bundle isolated-container wrapper: `scripts/run_approve_claim_isolated_container_canary.sh`
- Claim/source preview: `docs/reports/leo-working-state-20260709/claim-source-contract-preview-current.md` - Claim/source preview: `docs/reports/leo-working-state-20260709/claim-source-contract-preview-current.md`
- Mapped rich proposal plan: `docs/reports/leo-working-state-20260709/rich-proposal-creation-plan-mapped-current.md` - Mapped rich proposal plan: `docs/reports/leo-working-state-20260709/rich-proposal-creation-plan-mapped-current.md`
- Mapped production apply packet, not executed in production: `docs/reports/leo-working-state-20260709/production-apply-packet-current.md` - Mapped production apply packet, not executed in production: `docs/reports/leo-working-state-20260709/production-apply-packet-current.md`
@ -113,6 +119,10 @@ Use this file before making status claims. Prefer fresh VPS/GCP readbacks when c
- Current GCP parity probe: `docs/reports/leo-working-state-20260709/gcp-db-parity-current-probe-current.md` - Current GCP parity probe: `docs/reports/leo-working-state-20260709/gcp-db-parity-current-probe-current.md`
- Current GCP parity probe JSON: `docs/reports/leo-working-state-20260709/gcp-db-parity-current-probe-current.json` - Current GCP parity probe JSON: `docs/reports/leo-working-state-20260709/gcp-db-parity-current-probe-current.json`
- Current GCP Cloud SQL live inventory: `docs/reports/leo-working-state-20260709/gcp-cloud-sql-t3-live-readonly-current.md`
- Current GCP Cloud SQL live inventory JSON: `docs/reports/leo-working-state-20260709/gcp-cloud-sql-t3-live-readonly-current.json`
- Current GCP Cloud SQL Studio parity gate: `docs/reports/leo-working-state-20260709/gcp-cloudsql-studio-parity-gate-current.md`
- Current GCP Cloud SQL Studio parity gate JSON: `docs/reports/leo-working-state-20260709/gcp-cloudsql-studio-parity-gate-current.json`
- Prior auth-capable GCP blocker: `docs/reports/leo-working-state-20260709/gcp-db-parity-current-blocker-20260709.json` - Prior auth-capable GCP blocker: `docs/reports/leo-working-state-20260709/gcp-db-parity-current-blocker-20260709.json`
## Images ## Images
@ -148,6 +158,7 @@ Use this file before making status claims. Prefer fresh VPS/GCP readbacks when c
- The public Argus KB diagnostics HTTP probe passes from live `curl` payloads captured at `2026-07-10T02:47:53Z`: `3` approved proposals are visible (`14fa5ecc`, `ac036c9d`, `a64df080`), all are `approved_needs_apply_payload`, `worker_applyable_count=0`, and the endpoint is read-only. This supports Leo's expected answer that the approved KB work is not yet directly applyable/canonical, but it is not Docker/Postgres row-count or gateway-service proof. - The public Argus KB diagnostics HTTP probe passes from live `curl` payloads captured at `2026-07-10T02:47:53Z`: `3` approved proposals are visible (`14fa5ecc`, `ac036c9d`, `a64df080`), all are `approved_needs_apply_payload`, `worker_applyable_count=0`, and the endpoint is read-only. This supports Leo's expected answer that the approved KB work is not yet directly applyable/canonical, but it is not Docker/Postgres row-count or gateway-service proof.
- The highest-risk direct-claim cases are now green at handler level: `DC-02` recognizes approved Helmer staging row `a64df080-8502-42e2-98f4-9bbdecb8da73` as approved/unapplied, `DC-03` says the decision-matrix path is not shipped while matrix tables are absent, `DC-04` avoids a single-cause document pointer diagnosis, `DC-05` separates staging demo from authorized canonical apply, and `DC-06` separates `SOUL.md` runtime edits from canonical DB identity. - The highest-risk direct-claim cases are now green at handler level: `DC-02` recognizes approved Helmer staging row `a64df080-8502-42e2-98f4-9bbdecb8da73` as approved/unapplied, `DC-03` says the decision-matrix path is not shipped while matrix tables are absent, `DC-04` avoids a single-cause document pointer diagnosis, `DC-05` separates staging demo from authorized canonical apply, and `DC-06` separates `SOUL.md` runtime edits from canonical DB identity.
- A strict canonical `add_edge` apply canary is live-proven. - A strict canonical `add_edge` apply canary is live-proven.
- The repo-owned normal apply path now supports an atomic, insert-only `approve_claim` bundle behind separate `kb_review` and `kb_apply` credentials, a DB-role-to-reviewer principal mapping, and an immutable approval snapshot. Final source-bound isolated-container receipts pass `37/37` for both the generic bundle (`2` claims, `2` sources, `2` evidence rows, `1` edge, `1` reasoning tool) and the real losslessly normalized Helmer bundle (`5/13/17/7/1`). They prove exact payload-controlled projections and table deltas, reviewer/apply separation, denied direct proposal/evidence mutation, stale-payload refusal before canonical writes, replay refusal, source-hash rollback, exact agent/external-claim seeding, hardened role/function/ACL state, and independent inner/outer cleanup. Read-only live count endpoints stayed `1837/4145/4670/4916/17/26` and the gateway stayed PID `2999690`/`NRestarts=0`; this is not a byte-for-byte live-row comparison. The code and production permission migration are not deployed, and Helmer remains unapplied on production.
- Helmer 7 Powers is full-coverage packet and ledger clone-proven, not production-applied. - Helmer 7 Powers is full-coverage packet and ledger clone-proven, not production-applied.
- Mapped rich approved proposal packet is clone/rehearsal-proven for the supported subset, not production-applied. - Mapped rich approved proposal packet is clone/rehearsal-proven for the supported subset, not production-applied.
- The strategy-anchor companion write for mapped rich proposal `14fa5ecc` is clone-proven and reversible, not production-applied. - The strategy-anchor companion write for mapped rich proposal `14fa5ecc` is clone-proven and reversible, not production-applied.
@ -170,4 +181,4 @@ Use this file before making status claims. Prefer fresh VPS/GCP readbacks when c
- The live VPS runtime behavior should not be changed while the DB apply path is stabilized. - The live VPS runtime behavior should not be changed while the DB apply path is stabilized.
- Latest cleanup readback found no leftover rehearsal/clone/staging database names on the VPS Postgres container and did not mutate production DB or post to Telegram. - Latest cleanup readback found no leftover rehearsal/clone/staging database names on the VPS Postgres container and did not mutate production DB or post to Telegram.
- A live Telegram regression canary should be rerun after any production apply; no production apply has been authorized or executed. - A live Telegram regression canary should be rerun after any production apply; no production apply has been authorized or executed.
- GCP parity remains blocked by account auth/access facts until a fresh readback proves otherwise. - GCP control-plane Chrome readback now proves project `teleo-501523`, running VM `teleo-prod-1` in `europe-west6-a`, and one available private-only PostgreSQL `16.14` Cloud SQL instance, `teleo-pgvector-standby`, in `europe-west6-c`. Cloud SQL Studio is reachable but exposes no enabled existing IAM database principal and requires built-in password authentication; no credential was submitted and no SQL ran. Database-row/runtime/DC-01-through-DC-06 parity therefore remains unproven. The exact next gate is an already-existing authenticated Studio principal or a separately authorized IAM/database-user enablement window.

View file

@ -0,0 +1,127 @@
{
"schema": "teleo.gcpCloudSqlLiveReadonly.v1",
"captured_at_utc": "2026-07-10T06:52:27Z",
"source_thread_id": "019f47af-f2cc-7c10-b928-a9283afa2621",
"current_canary": "Open the authenticated Teleo Cloud SQL instances page with authuser=5 and retain the complete sanitized current inventory plus instance routing metadata.",
"required_tier": "T3_live_readonly",
"current_tier": "T3_live_readonly",
"status": "done_verified",
"strongest_claim_allowed": "Project teleo-501523 currently exposes exactly one Cloud SQL instance in the authenticated Console: teleo-pgvector-standby. It is runnable/available, PostgreSQL 16.14 Enterprise, single-zone in europe-west6-c, and reachable only through private connectivity on teleo-staging-net. This does not prove database contents, schema parity, row counts, application connectivity, or production cutover.",
"capture": {
"surface": "authenticated Google Cloud Console DOM readback",
"account": "b***@livingip.xyz",
"project_id": "teleo-501523",
"project_display_name": "Teleo",
"list_url": "https://console.cloud.google.com/sql/instances?authuser=5&project=teleo-501523",
"overview_url": "https://console.cloud.google.com/sql/instances/teleo-pgvector-standby/overview?authuser=5&project=teleo-501523",
"cli_authentication_used": false,
"secret_values_captured": false
},
"inventory": {
"instance_count": 1,
"zero_instances": false,
"instances": [
{
"instance_id": "teleo-pgvector-standby",
"status_ui": "Available; Runnable",
"engine": "PostgreSQL",
"major_version": "16",
"database_version": "PostgreSQL 16.14",
"cloud_sql_edition": "Enterprise",
"instance_role": "Primary instance",
"region": "europe-west6",
"zone": "europe-west6-c",
"availability": "Single zone",
"high_availability": false,
"machine": {
"vcpus": 1,
"memory_mb_ui": 628.74
},
"storage": {
"type": "SSD",
"provisioned_gb": 10,
"used_gb_ui": 2,
"automatic_storage_increase": true
},
"connectivity": {
"connection_name": "teleo-501523:europe-west6:teleo-pgvector-standby",
"private_ip_connectivity": true,
"private_service_access": true,
"associated_network": "projects/teleo-501523/global/networks/teleo-staging-net",
"vpc_network": "teleo-staging-net",
"allocated_ip_range": "Automatically assigned IP range",
"internal_ip": "10.61.0.3",
"public_ip_connectivity": false,
"public_ip": null,
"default_tcp_port": 5432,
"write_endpoint": null
},
"database_authentication": {
"cloudsql_iam_authentication": true,
"application_or_operator_principal_verified": false
},
"resilience_and_backup": {
"automated_backups": true,
"backup_tier": "Standard",
"point_in_time_recovery": true,
"deletion_prevention": true,
"latest_visible_backup_status": "Backup finished",
"latest_visible_backup_date_local": "2026-07-10"
},
"recent_visible_operations_sanitized": [
"Backup finished on 2026-07-10",
"Backup finished on 2026-07-09",
"Two imports succeeded on 2026-07-08",
"Database creation completed on 2026-07-08"
],
"observed_health_issues": [
"Auditing not enabled",
"No password policy",
"No user password policy"
],
"labels": [],
"maintenance": {
"window": "Sunday 06:00-07:00 CEST",
"scheduled_now": false,
"version_current": true
}
}
]
},
"db_parity_route": {
"target_instance": "teleo-pgvector-standby",
"required_network_surface": "A runtime or connector with private reachability to teleo-staging-net",
"private_endpoint": "10.61.0.3:5432",
"cloud_sql_connection_name": "teleo-501523:europe-west6:teleo-pgvector-standby",
"public_route_available": false,
"iam_database_authentication_enabled": true,
"database_name_proven_by_this_pass": false,
"next_readonly_owner_action": "From a verified teleo-staging-net-connected runtime, authenticate with an authorized database principal and run read-only database/schema/count parity queries against this instance. Do not attempt a public-IP connection because public connectivity is disabled."
},
"not_proven": [
"database names",
"database contents or row counts",
"schema parity with the VPS database",
"successful application-level connection from teleo-prod-1",
"production traffic cutover",
"high availability or cross-zone failover"
],
"actions": [
"Opened the exact Cloud SQL instances URL in the authorized authuser=5 Chrome session",
"Read the complete live instance treegrid and verified one and only one instance row",
"Opened the instance Overview through read-only navigation",
"Read status, engine/version, region/zone, machine, storage, connectivity, backup, IAM-authentication, and maintenance metadata",
"Did not activate Edit, Connect, Restart, Stop, Import, Export, Migrate, Clone, Delete, or failover controls"
],
"cleanup_readback": {
"browser_tab_finalized": true,
"browser_tab_disposition": "agent-created evidence tab closed; no handoff tab kept",
"gcp_writes": 0,
"database_writes": 0,
"service_restarts": 0,
"deploys": 0,
"iam_changes": 0,
"credential_or_key_creation": 0,
"telegram_sends": 0
}
}

View file

@ -0,0 +1,56 @@
# Teleo Cloud SQL T3 live-readonly inventory
Captured: `2026-07-10T06:52:27Z`
## Outcome
The authenticated `authuser=5` Cloud Console session proves that project `teleo-501523` currently has exactly one Cloud SQL instance.
- Required tier: `T3_live_readonly`
- Current tier: `T3_live_readonly`
- Status: `done_verified`
- Account: `b***@livingip.xyz`
- Project: `Teleo` / `teleo-501523`
## Current inventory
| Instance | Status | Engine | Region / zone | Availability | Connectivity |
|---|---|---|---|---|---|
| `teleo-pgvector-standby` | Available / runnable | PostgreSQL `16.14`, Enterprise | `europe-west6` / `europe-west6-c` | Single-zone; not highly available | Private IP and PSA enabled; public IP disabled |
Instance count: `1`.
## DB parity routing metadata
- Cloud SQL connection name: `teleo-501523:europe-west6:teleo-pgvector-standby`
- VPC: `projects/teleo-501523/global/networks/teleo-staging-net`
- Private endpoint: `10.61.0.3:5432`
- Private Service Access: enabled
- Public IP connectivity: disabled
- Cloud SQL IAM database authentication: enabled
- Write endpoint: none shown
The DB parity worker must run from a host or connector with private reachability to `teleo-staging-net`, authenticate with an authorized database principal, and issue read-only schema/count queries. A public-IP route is unavailable.
## Other current facts
- Machine: `1 vCPU`, `628.74 MB`
- Storage: `10 GB` SSD, `2 GB` shown used; automatic storage increase enabled
- Automated backups and point-in-time recovery: enabled
- Deletion prevention: enabled
- Latest visible backup: finished on `2026-07-10`
- Two sanitized import operations are shown as successful on `2026-07-08`
- Maintenance: Sunday `06:00-07:00 CEST`; no maintenance currently scheduled
- Observed health issues: auditing not enabled, no password policy, no user password policy
## Claim ceiling
This proves the current Cloud SQL control-plane inventory and private routing surface. It does not prove database names, contents, row counts, schema parity, an application connection from `teleo-prod-1`, production traffic cutover, or failover capability.
## Actions and mutation boundary
The exact live list and instance Overview URLs were read through Chrome. No Edit, Connect, Restart, Stop, Import, Export, Migrate, Clone, Delete, or failover control was activated. GCP writes, DB writes, IAM changes, key creation, service restarts, deploys, and Telegram sends were all zero.
Cleanup readback: the agent-created Cloud SQL evidence tab was closed and no browser handoff tab was retained.
Machine-readable evidence: `gcp-cloud-sql-t3-live-readonly-current.json`.

View file

@ -0,0 +1,85 @@
{
"schema": "teleo.gcpCloudSqlStudioParityGate.v1",
"captured_at_utc": "2026-07-10T06:57:22.085Z",
"source_thread_id": "019f47af-f2cc-7c10-b928-a9283afa2621",
"current_canary": "Open Cloud SQL Studio for teleo-pgvector-standby with authuser=5, authenticate only with an already-authorized principal, execute a transaction-level read-only schema/count parity query, and roll it back.",
"required_tier": "T3_live_readonly",
"current_tier": "T0_spec",
"supporting_gate_evidence_tier": "T3_live_readonly",
"status": "blocked_with_artifact",
"strongest_claim_allowed": "The authenticated Cloud SQL Studio surface is live and reachable for teleo-pgvector-standby, but it did not expose an existing IAM database principal. The visible login flow required built-in database authentication with a password credential. No database session or SQL readback was established, so database contents and VPS parity remain unproven.",
"control_plane_evidence_source": "/Users/user/Documents/Codex/2026-07-10/leo-gcp-control-plane/outputs/gcp-cloud-sql-t3-live-readonly-current.json",
"target": {
"project": "teleo-501523",
"instance": "teleo-pgvector-standby",
"connection_name": "teleo-501523:europe-west6:teleo-pgvector-standby",
"engine": "PostgreSQL 16.14",
"private_endpoint": "10.61.0.3:5432",
"network": "teleo-staging-net",
"instance_iam_db_authentication_flag": "on",
"console_account": "b***@livingip.xyz"
},
"live_studio_readback": {
"studio_url_sanitized": "https://console.cloud.google.com/sql/instances/teleo-pgvector-standby/studio?authuser=5&project=teleo-501523",
"studio_loaded": true,
"login_dialog_visible": true,
"database_selector_visible": true,
"user_selector_visible": true,
"iam_database_authentication_option_visible": true,
"iam_database_authentication_option_enabled": false,
"built_in_database_authentication_selected": true,
"password_credential_required": true,
"password_value_retained": false,
"authenticate_clicked": false
},
"sql_execution": {
"database_session_established": false,
"begin_transaction_read_only_executed": false,
"current_database_read": false,
"current_user_read": false,
"transaction_read_only_read": false,
"catalog_or_table_selects_executed": false,
"rollback_executed": false,
"query_timestamp_utc": null
},
"parity_fields": {
"database_name": null,
"schemas": null,
"public.claims": null,
"public.sources": null,
"public.claim_evidence": null,
"public.claim_edges": null,
"kb_stage.kb_proposals": null,
"proposal_ids_and_statuses": null,
"selected_row_identities": null,
"gcp_vs_vps": null,
"ahead_behind_equal": null
},
"actions": [
"Opened the authenticated instance Overview URL with authuser=5 in an agent-created Chrome tab.",
"Discovered the exact Cloud SQL Studio href from the live instance navigation.",
"Opened Cloud SQL Studio and read the visible database-login state.",
"Stopped before authentication because the IAM option was disabled and the selected built-in flow required a password credential.",
"Did not open browser SSH because that path may create an ephemeral key.",
"Finalized Chrome and closed the agent-created tab."
],
"exact_gate": "Cloud SQL Studio exposes no enabled existing IAM database principal for this console session. Its selected built-in authentication path requires a raw database password, which this lane is forbidden to inspect, submit, recover, or replace.",
"why_autonomous_repair_stops": "Authenticating would require handling a raw password, or creating/granting a database or IAM principal. Both are explicit stop conditions, and the latter would mutate GCP or the database.",
"clear_CTA": "Using an already-existing authorized database principal, have an operator complete the Cloud SQL Studio database login without sharing credentials with Codex, then tell Codex the Studio editor is authenticated. Otherwise route IAM/user enablement to a separately authorized write lane.",
"next_non_user_action": "Once Studio is already authenticated with an existing principal, run only BEGIN TRANSACTION READ ONLY; identity/read-only checks; catalog/table/count SELECTs; and ROLLBACK, then retain the sanitized results.",
"cleanup_readback": {
"browser_finalized": true,
"agent_created_tab_closed": true,
"database_session_open": false,
"transaction_open": false,
"gcp_writes": 0,
"database_writes": 0,
"iam_changes": 0,
"credential_or_key_creation": 0,
"browser_ssh_opened": false,
"service_restarts": 0,
"deploys": 0,
"telegram_sends": 0
},
"secret_values_included": false
}

View file

@ -0,0 +1,46 @@
# Cloud SQL Studio parity gate
Captured UTC: `2026-07-10T06:57:22.085Z`
## Outcome
Cloud SQL Studio opened successfully for `teleo-pgvector-standby` under the authenticated `b***@livingip.xyz` Console session. The database login dialog did not provide an enabled existing IAM principal: IAM database authentication was disabled in the dialog, built-in authentication was selected, and that path required a password credential.
The lane stopped before authentication. No database session was established and no SQL ran.
- Required tier: `T3_live_readonly`
- Current database-parity tier: `T0_spec`
- Live gate evidence: `T3_live_readonly`
- Status: `blocked_with_artifact`
## Target verified from the live control-plane evidence
- Project: `teleo-501523`
- Instance: `teleo-pgvector-standby`
- Connection: `teleo-501523:europe-west6:teleo-pgvector-standby`
- Engine: PostgreSQL 16.14
- Endpoint: private-only `10.61.0.3:5432`
- Network: `teleo-staging-net`
- Instance flag: `cloudsql.iam_authentication=on`
## Exact gate
The Studio login surface exposed no enabled existing IAM database principal for this Console session. The selected built-in authentication route requires a raw database password. Using that password, creating a database user, changing IAM, or granting access is outside this lane's hard ceiling.
An operator can complete Studio login independently with an already-existing authorized principal, without sharing credentials with Codex, and then tell Codex the SQL editor is authenticated. Otherwise, IAM/user enablement must be handled by a separately authorized write lane.
## Not proven
No current database name, principal identity, transaction-read-only result, schema presence, table counts, proposal IDs/statuses, selected row identities, or GCP-versus-VPS comparison was read.
The planned `BEGIN TRANSACTION READ ONLY` query was not executed and therefore is not counted as evidence.
## Cleanup
- `Authenticate` was not clicked.
- No browser SSH was opened.
- Chrome was finalized and the agent-created Studio tab was closed.
- No database session or transaction remains open.
- No GCP, database, IAM, credential, key, service, deploy, or Telegram mutation occurred.
Machine-readable evidence: `gcp-cloudsql-studio-parity-gate-current.json`.

View file

@ -0,0 +1,137 @@
{
"schema": "working-leo.gcp-parallel-delegation.v1",
"source_thread_id": "019f47af-f2cc-7c10-b928-a9283afa2621",
"last_updated_utc": "2026-07-10T15:47:17Z",
"source_goal": "Make Leo on VPS meet the working-Leo definition, then prove the equivalent GCP path without weakening the VPS standard.",
"dispatched_repo_head": "339565585dc417501488a38135998283988482d1",
"parallelism_expected": 10,
"parallelism_actual": 10,
"ship_owner": {
"thread_id": "019f4a53-b16c-7653-8026-bba381b853bf",
"working_target": "Authenticated live GCP project, Compute Engine, Cloud SQL, and Leo surface inventory",
"required_tier": "T3_live_readonly",
"status": "completed_t3_control_plane_inventory"
},
"visible_threads": [
{
"thread_id": "019f4a53-b16c-7653-8026-bba381b853bf",
"lane_class": "parallel_canary",
"role": "GCP control-plane ship owner",
"working_target": "Authenticated live GCP resource inventory",
"required_tier": "T3_live_readonly",
"owned_surface": "GCP project and resource discovery; read-only",
"forbidden_surface": "Shared repo writes, Telegram sends, DB writes, restarts, deploys, credentials, IAM, infrastructure mutation",
"status": "completed_t3_project_vm_cloudsql_inventory"
},
{
"thread_id": "019f4a53-9702-7a10-a894-354af22d9cee",
"lane_class": "parallel_canary",
"role": "GCP Cloud SQL parity owner",
"working_target": "Read-only canonical row and schema parity against VPS",
"required_tier": "T3_live_readonly",
"owned_surface": "Cloud SQL discovery and read-only SQL",
"forbidden_surface": "Shared repo writes, DB mutation, Telegram sends, restarts, deploys, credentials, IAM, infrastructure mutation",
"status": "completed_live_studio_gate_waiting_existing_db_principal"
},
{
"thread_id": "019f4a53-c10a-7781-b336-401d526d2a48",
"lane_class": "parallel_canary",
"role": "GCP Leo runtime parity owner",
"working_target": "Read-only VM, process, service, repo, profile, model, DB, and bot surface map",
"required_tier": "T3_live_readonly",
"owned_surface": "Compute Engine and Leo runtime inspection; read-only",
"forbidden_surface": "Shared repo writes, Telegram sends, profile writes, restarts, deploys, credentials, IAM, infrastructure mutation",
"status": "completed_runtime_contract_waiting_iap_access"
},
{
"thread_id": "019f4a53-cf0a-7e92-a7d0-16fde32224de",
"lane_class": "parallel_canary",
"role": "GCP no-post Cory canary owner",
"working_target": "Real GCP DC-01 through DC-06 runtime replies, score, unchanged DB counts, and cleanup",
"required_tier": "T2_runtime_plus_T3_live_readonly",
"owned_surface": "No-post handler execution and scoring against the discovered GCP runtime",
"forbidden_surface": "Shared repo writes, Telegram sends, DB apply, profile writes, restarts, deploys",
"status": "completed_before_runtime_canary_waiting_iap_access"
}
],
"support_agents": [
{
"agent_id": "019f4a53-d792-75e1-93c0-ad3c90868176",
"nickname": "Dewey",
"role": "gcloud DNS and auth anomaly diagnosis",
"owned_surface": "Local gcloud runtime and network diagnosis; read-only",
"status": "completed"
},
{
"agent_id": "019f4a53-e042-72e3-923f-bff6e067175e",
"nickname": "Fermat",
"role": "Cloud SQL parity query contract",
"owned_surface": "Retained artifacts, schema, and read-only query design",
"status": "completed"
},
{
"agent_id": "019f4a53-e68a-7e41-a7e6-be6b1f378b41",
"nickname": "Planck",
"role": "GCP probe script defect audit",
"owned_surface": "scripts/probe_gcp_db_parity.py and tests; read-only",
"status": "completed"
},
{
"agent_id": "019f4a53-ec08-7b02-945e-3aa3d3f275a5",
"nickname": "Banach",
"role": "Cory-standard GCP acceptance and cutover sequence",
"owned_surface": "Working-Leo acceptance gates and execution order; read-only",
"status": "completed"
}
],
"review_agents": [
{
"agent_id": "019f4a5b-552e-7873-84ab-a274eec7261f",
"nickname": "Bacon",
"role": "DB apply authority and race review",
"status": "completed_findings_integrated"
},
{
"agent_id": "019f4a5b-5ca4-7a80-88e1-20c9a8d147aa",
"nickname": "Meitner",
"role": "Rich normalization and lifecycle review",
"status": "completed_findings_integrated"
}
],
"root_owned_running_rows": [
{
"working_target": "Approved strict claim bundles become canonical rows with conflict-safe row-level proof in a disposable DB clone",
"owned_files": [
"scripts/apply_proposal.py",
"scripts/apply_worker.py",
"scripts/kb_apply_prereqs.sql",
"scripts/kb_proposal_normalize.py",
"tests/test_apply_proposal.py",
"tests/test_apply_worker.py",
"tests/test_kb_proposal_normalize.py"
],
"status": "isolated_runtime_green_generic_and_helmer_37_of_37"
}
],
"blocked_by_tooling_or_capacity": [],
"blocked_by_collision_risk": [],
"blocked_by_external_gate": [
{
"gate": "Cloud SQL Studio has no enabled existing IAM database principal and requires built-in password authentication",
"impact": "Cloud SQL schema, row-count, proposal-state, and VPS-versus-GCP database parity queries cannot run",
"clear_CTA": "Authenticate Studio with an already-existing authorized database principal without sharing the credential, then report GCP Studio authenticated; otherwise authorize a separate IAM/database-user enablement window."
},
{
"gate": "no verified no-key read-only VM session",
"impact": "GCP Leo runtime parity and DC-01 through DC-06 remain unproven",
"clear_CTA": "Use an already-authenticated no-key runtime route, or separately authorize the minimum IAP/OS Login setup needed for a read-only VM session."
}
],
"repair_action": "Authenticated Chrome cleared the old blanket login/DNS blocker and retained live project, VM, and private Cloud SQL inventory. Cloud SQL Studio was opened and safely stopped at the database-principal gate without submitting credentials. The next database pass starts from an already-authenticated Studio session; runtime parity remains a separate no-key session gate.",
"shared_checkout_rule": "All GCP workers are read-only. The source thread is the sole apply-engine integration owner.",
"gui_rule": "Authenticated Chrome or Computer Use is authorized for GCP login and read-only console navigation. AppleScript, osascript, open, focus stealing, credential creation, and representational communication are forbidden.",
"telegram_messages_authorized": false,
"database_mutation_authorized": false,
"infrastructure_mutation_authorized": false,
"no_x402_scope": true
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,11 +1,13 @@
{ {
"pack": "leo-teleo-skill-pack", "pack": "leo-teleo-skill-pack",
"created_date": "2026-07-09", "created_date": "2026-07-09",
"status": "draft_ready_for_review", "last_updated_utc": "2026-07-10T15:47:17Z",
"status": "repo_native_validated",
"contains_secrets": false, "contains_secrets": false,
"production_mutation_authorized": false, "production_mutation_authorized": false,
"install_target": "/Users/user/.codex/skills", "repo_skill_root": ".agents/skills",
"claim_ceiling": "Reusable onboarding and operations skill drafts only; they do not authorize production DB apply or runtime changes.", "optional_local_install_target": "/Users/user/.codex/skills",
"claim_ceiling": "Repo-native onboarding and operations skills. Generic and Helmer guarded-apply canaries pass 37/37 in disposable PostgreSQL; the production permission migration and Helmer apply remain undeployed. GCP control-plane inventory is live-proven, while DB row/runtime parity remains gated on an existing database principal.",
"skills": [ "skills": [
{ {
"name": "teleo-leo-onboarding", "name": "teleo-leo-onboarding",
@ -24,12 +26,12 @@
}, },
{ {
"name": "teleo-gcp-parity-ops", "name": "teleo-gcp-parity-ops",
"role": "GCP parity/cutover lane and exact auth blocker handling", "role": "GCP control-plane, private Cloud SQL inventory, Studio DB-auth gate, and parity workflow",
"path": ".agents/skills/teleo-gcp-parity-ops/SKILL.md" "path": ".agents/skills/teleo-gcp-parity-ops/SKILL.md"
}, },
{ {
"name": "teleo-kb-db-change-workflow", "name": "teleo-kb-db-change-workflow",
"role": "KB proposal normalization, clone rehearsal, apply packet, rollback proof", "role": "Strict proposal normalization, separated review/apply authority, isolated canary, and row-level proof",
"path": ".agents/skills/teleo-kb-db-change-workflow/SKILL.md" "path": ".agents/skills/teleo-kb-db-change-workflow/SKILL.md"
}, },
{ {
@ -51,6 +53,10 @@
"reference_files": [ "reference_files": [
"docs/reports/leo-working-state-20260709/current-truth-index.md", "docs/reports/leo-working-state-20260709/current-truth-index.md",
"docs/reports/leo-working-state-20260709/operator-surface-map.md", "docs/reports/leo-working-state-20260709/operator-surface-map.md",
"docs/reports/leo-working-state-20260709/fable-leo-teleo-onboarding.md" "docs/reports/leo-working-state-20260709/fable-leo-teleo-onboarding.md",
] "docs/reports/leo-working-state-20260709/approve-claim-clone-canary-current.md",
"docs/reports/leo-working-state-20260709/gcp-cloud-sql-t3-live-readonly-current.md",
"docs/reports/leo-working-state-20260709/gcp-cloudsql-studio-parity-gate-current.md"
],
"validation": "tests/test_repo_skill_pack.py"
} }

View file

@ -2,7 +2,7 @@
Purpose: reusable onboarding and operating knowledge for future Codex/Fable workers working on Leo, Teleo, the VPS, GCP parity, Telegram canaries, and KB database changes. Purpose: reusable onboarding and operating knowledge for future Codex/Fable workers working on Leo, Teleo, the VPS, GCP parity, Telegram canaries, and KB database changes.
This pack is a draft installable skill set. It is intentionally local-first and evidence-linked. It does not contain secrets, private key contents, bot tokens, or production mutation authorization. This is the repo-native skill set under `.agents/skills/`. It is evidence-linked and validated by `tests/test_repo_skill_pack.py`. It does not contain secrets, private key contents, bot tokens, or production mutation authorization.
## Skills In This Pack ## Skills In This Pack
@ -19,9 +19,9 @@ This pack is a draft installable skill set. It is intentionally local-first and
- VPS Leo Telegram memory, KB audit, and staged write canaries are live-proven. - VPS Leo Telegram memory, KB audit, and staged write canaries are live-proven.
- A strict canonical `add_edge` apply canary is live-proven. - A strict canonical `add_edge` apply canary is live-proven.
- Rich approved proposal packets are clone/rehearsal-proven, not production-applied. - Generic and real Helmer guarded-apply canaries pass `37/37` with exact payload projections/table deltas, current-source hashes, and cleanup proof; neither the new permission migration nor Helmer is production-applied.
- The live VPS runtime behavior should not be changed while the DB apply path is stabilized. - The live VPS runtime behavior should not be changed while the DB apply path is stabilized.
- GCP parity is not solved; it remains blocked by account auth/access facts recorded in the current blocker artifact. - GCP project, VM, and private Cloud SQL inventory are live-proven. Cloud SQL row/schema parity is not: Studio requires an already-existing authenticated DB principal.
## Key References ## Key References
@ -30,6 +30,6 @@ This pack is a draft installable skill set. It is intentionally local-first and
- Fable onboarding prompt: `docs/reports/leo-working-state-20260709/fable-leo-teleo-onboarding.md` - Fable onboarding prompt: `docs/reports/leo-working-state-20260709/fable-leo-teleo-onboarding.md`
- Skill manifest: `skill-pack-manifest.json` - Skill manifest: `skill-pack-manifest.json`
## Install Later ## Discovery
The reusable skills are committed in `.agents/skills/`. Keep this report directory as the evidence snapshot because it points to the retained July 9 artifacts. Codex-compatible workers should discover the committed `.agents/skills/` tree from this repository. The manifest is the index; each skill points to current retained evidence rather than embedding secrets or pretending a stale snapshot is live truth.

View file

@ -1,6 +1,6 @@
# Working Leo Current State - 2026-07-09 # Working Leo Current State - 2026-07-09
Generated UTC: `2026-07-10T02:12:56Z` Updated UTC: `2026-07-10T15:47:17Z`
## Current Working Definition ## Current Working Definition
@ -8,7 +8,7 @@ A working Leo, for Cory/m3taversal's current expectation, is a Telegram-facing a
## What Is Now Proven On VPS ## What Is Now Proven On VPS
- VPS gateway service is stable: `leoclean-gateway.service` is active/running, `MainPID=3252143`, `NRestarts=0`, `WorkingDirectory=/home/teleo`, `User=teleo`. - Fresh VPS gateway readback is stable: `leoclean-gateway.service` is active/running, `MainPID=2999690`, `NRestarts=0`; production counts are `claims=1837`, `sources=4145`, `claim_evidence=4670`, `claim_edges=4916`, `kb_proposals=26`.
- Restart survival is now live-proven as a named Cory-standard proof row, not inferred from passive uptime: `systemctl restart leoclean-gateway.service` returned `0`, service came back active/running with `MainPID=2845730` and `ExecMainStartTimestamp=Fri 2026-07-10 03:24:43 UTC`, canonical DB counts stayed unchanged (`1837/4145/4670/4916/26`), no Telegram post or production DB apply ran, the temp profile was removed, and the post-restart no-post `DC-01` through `DC-06` handler suite returned six runtime replies. Strict post-restart answer-quality score for that pre-repair retained run was `5/6`, then the VPS/GCP KB bridge skill was tightened for row-level proof, overclaim guardrails, and proposal state semantics. After deploy SHA `090becae1621436467610e529d6328d70964d11f`, the same no-post direct-claim suite strict-scored `6/6`; DB counts stayed unchanged, no Telegram post or production DB apply ran, no live profile changed, and cleanup confirmed no handler process or temp profile left behind. - Restart survival is now live-proven as a named Cory-standard proof row, not inferred from passive uptime: `systemctl restart leoclean-gateway.service` returned `0`, service came back active/running with `MainPID=2845730` and `ExecMainStartTimestamp=Fri 2026-07-10 03:24:43 UTC`, canonical DB counts stayed unchanged (`1837/4145/4670/4916/26`), no Telegram post or production DB apply ran, the temp profile was removed, and the post-restart no-post `DC-01` through `DC-06` handler suite returned six runtime replies. Strict post-restart answer-quality score for that pre-repair retained run was `5/6`, then the VPS/GCP KB bridge skill was tightened for row-level proof, overclaim guardrails, and proposal state semantics. After deploy SHA `090becae1621436467610e529d6328d70964d11f`, the same no-post direct-claim suite strict-scored `6/6`; DB counts stayed unchanged, no Telegram post or production DB apply ran, no live profile changed, and cleanup confirmed no handler process or temp profile left behind.
- Telegram Bot/API readiness is proven for group `Leo` (`-5146042086`), and the live adapter accepts the Cory-working canary without posting. - Telegram Bot/API readiness is proven for group `Leo` (`-5146042086`), and the live adapter accepts the Cory-working canary without posting.
- Live Telegram-visible group canary passed through authenticated Chrome/Telegram Web: - Live Telegram-visible group canary passed through authenticated Chrome/Telegram Web:
@ -94,8 +94,11 @@ A working Leo, for Cory/m3taversal's current expectation, is a Telegram-facing a
- Old Cory-approved mapped rich proposals are still not fully directly applyable: - Old Cory-approved mapped rich proposals are still not fully directly applyable:
- `14fa5ecc-ac7a-41c1-807d-a2e85b936617` - `14fa5ecc-ac7a-41c1-807d-a2e85b936617`
- `ac036c9d-20a0-4ffe-881f-57d6b7bacf22` - `ac036c9d-20a0-4ffe-881f-57d6b7bacf22`
- `14fa5ecc` normalization is blocked by missing canonical claim/source IDs. The proposal contains approved textual successor candidates, but the exact successor rows A/B/C/D do not already exist as canonical rows. - `14fa5ecc` still cannot become one direct strict bundle because its many-successor supersession, strategy/context, and concept/governance fragments require the reviewed companion mappings/schema decisions already captured in the integrated packet set. Missing canonical IDs alone are no longer the general apply-engine limitation.
- Current narrow apply worker supports `add_edge`, `attach_evidence`, and `revise_strategy`; it does not create new `public.claims` or `public.sources` rows. Applying `14fa5ecc` as Cory likely expects requires either creating/identifying canonical rows first or explicitly widening the guarded apply contract. - The repo implementation now adds an atomic `approve_claim` contract to the guarded apply worker. A fully resolved rich graph normalizes fail-closed into one strict bundle; malformed, missing-key, duplicate, or planner-dropped candidates produce no strict child. Type coercions and candidate-to-row accounting remain visible in `normalization_manifest` for review.
- Review and apply authority are separated in the pending migration: `kb_review` can only execute exact-payload approval, `kb_apply` cannot directly update proposal approval/payload or existing evidence, `kb_gate_owner` owns the SECURITY DEFINER gates with a locked `pg_catalog, pg_temp` search path, and one immutable approval snapshot binds the reviewed type/payload/reviewer/timestamp/note to apply.
- Final isolated-container runtime receipts pass `37/37` twice. The generic bundle's exact payload projection and table deltas are `2/2/2/1/1`; the live-derived Helmer bundle's are `5` claims, `13` sources, `17` evidence rows, `7` edges, and `1` reasoning tool after exact seeding of Leo and five referenced production claims. Stale payload execution, direct privilege probes, replay, source-hash conflict, legacy function overloads, role memberships, and stale ACL paths are refused without partial canonical rows. Read-only live endpoint counts stayed `1837/4145/4670/4916/17/26`, gateway stayed PID `2999690`/`NRestarts=0`, all retained source hashes match the current files, and all inner clones, outer containers, temp workdirs, and the remote source bundle were removed. This does not claim a byte-for-byte comparison of every live row.
- The current production VPS deploy SHA `339565585dc417501488a38135998283988482d1` does not include this pending implementation. No production role/function/table migration ran, the worker remains separately gated, and Helmer remains approved/staged rather than canonical.
- Live approved rich proposal contract preview is now available: - Live approved rich proposal contract preview is now available:
- `14fa5ecc`: 4 claim rows, 5 source rows, 17 edge fragments, 9 evidence fragments; not direct-apply-ready - `14fa5ecc`: 4 claim rows, 5 source rows, 17 edge fragments, 9 evidence fragments; not direct-apply-ready
- `ac036c9d`: 3 claim rows, 3 source rows, 6 edge fragments, 5 evidence fragments; not direct-apply-ready - `ac036c9d`: 3 claim rows, 3 source rows, 6 edge fragments, 5 evidence fragments; not direct-apply-ready
@ -216,11 +219,13 @@ A working Leo, for Cory/m3taversal's current expectation, is a Telegram-facing a
- `teleo-kb-apply-worker.service` exists but is disabled/inactive and ships inert/report-only unless `KB_APPLY_WORKER_ENABLED=1`; no active `KB_APPLY_RENDER_CMD` hook or scheduled 24-hour SOUL renderer was proven - `teleo-kb-apply-worker.service` exists but is disabled/inactive and ships inert/report-only unless `KB_APPLY_WORKER_ENABLED=1`; no active `KB_APPLY_RENDER_CMD` hook or scheduled 24-hour SOUL renderer was proven
- current VPS schema does not contain the designed decision-matrix tables `kb_stage.matrix_voters`, `kb_stage.proposal_votes`, or `kb_stage.proposal_decisions`; current approval evidence lives in `kb_stage.kb_proposals` - current VPS schema does not contain the designed decision-matrix tables `kb_stage.matrix_voters`, `kb_stage.proposal_votes`, or `kb_stage.proposal_decisions`; current approval evidence lives in `kb_stage.kb_proposals`
- live proposal inspection showed pending/approved rows have `source_ref` pointers, but inspected source refs did not directly match `public.sources`; document proposals are real staging rows but still need guarded mapping into canonical sources/evidence/edges - live proposal inspection showed pending/approved rows have `source_ref` pointers, but inspected source refs did not directly match `public.sources`; document proposals are real staging rows but still need guarded mapping into canonical sources/evidence/edges
- GCP parity was rechecked with a repeatable current probe and remains not proven: - GCP parity was rechecked across four visible owner threads plus support/review agents and remains only partially proven:
- current local config still selects `billy@livingip.xyz` for project `teleo-501523` - live Chrome control-plane readback confirms project `teleo-501523` and running VM `teleo-prod-1` in `europe-west6-a`
- the current Codex runner cannot resolve `oauth2.googleapis.com`, so all token refresh/project readbacks stop at `blocked_on_network_dns_before_auth_refresh` - the old blanket DNS diagnosis is stale; shell connectivity can reach Google OAuth, while sandboxed gcloud contexts vary
- the prior auth-capable July 9 artifact remains useful context: `billy@livingip.xyz` then failed non-interactive reauth, two other locally cached accounts refreshed but lacked `teleo-501523`, and `contact@attn.markets` failed token refresh - authenticated Chrome now proves exactly one Cloud SQL instance: `teleo-pgvector-standby`, available/runnable PostgreSQL `16.14` Enterprise in `europe-west6-c`, private-only on `teleo-staging-net`, with no public IP
- no GCP parity claim is valid until `scripts/probe_gcp_db_parity.py` reports `ready_for_gcp_readonly_parity=true` - Cloud SQL Studio is reachable, but its login dialog exposed no enabled existing IAM database principal and selected built-in password authentication; no credential was inspected or submitted and no SQL ran
- Cloud SQL row/schema parity, VM runtime parity, and GCP `DC-01` through `DC-06` replies remain unproven
- exact CTA: authenticate Cloud SQL Studio with an already-existing authorized database principal without sharing the credential, then report `GCP Studio authenticated`; otherwise authorize a separate IAM/database-user enablement window
## Current Evidence ## Current Evidence
@ -286,12 +291,16 @@ A working Leo, for Cory/m3taversal's current expectation, is a Telegram-facing a
- Integrated apply rehearsal proof image: `outputs/leo-db-state-25-integrated-apply-rehearsal.svg` - Integrated apply rehearsal proof image: `outputs/leo-db-state-25-integrated-apply-rehearsal.svg`
- Direct-claim handler pass image: `work/teleo-infra-main/docs/reports/leo-working-state-20260709/leo-db-state-27-direct-claim-handler-pass.svg` - Direct-claim handler pass image: `work/teleo-infra-main/docs/reports/leo-working-state-20260709/leo-db-state-27-direct-claim-handler-pass.svg`
- Current GCP parity probe: `work/teleo-infra-main/docs/reports/leo-working-state-20260709/gcp-db-parity-current-probe-current.md` - Current GCP parity probe: `work/teleo-infra-main/docs/reports/leo-working-state-20260709/gcp-db-parity-current-probe-current.md`
- Current GCP Cloud SQL inventory: `work/teleo-infra-main/docs/reports/leo-working-state-20260709/gcp-cloud-sql-t3-live-readonly-current.md`
- Current GCP Cloud SQL Studio parity gate: `work/teleo-infra-main/docs/reports/leo-working-state-20260709/gcp-cloudsql-studio-parity-gate-current.md`
- Prior GCP parity blocker: `outputs/gcp-db-parity-current-blocker-20260709.json` - Prior GCP parity blocker: `outputs/gcp-db-parity-current-blocker-20260709.json`
## Next Actions ## Next Actions
1. Keep the live Telegram canary as the top-level regression proof for Cory-working memory + KB audit. 1. Keep the live Telegram canary as the top-level regression proof for Cory-working memory + KB audit.
2. Do not auto-apply old freeform approvals. 2. Do not auto-apply old freeform approvals.
3. Do not run production apply without explicit authorization. The mapped base, cross-surface anchor, Rio strategy, governance/concept, and Helmer packets compose in one clone and are ready as a packet set, but are not production-applied. 3. Review and land the clone-proven `approve_claim` normal path. Do not deploy its production role grants or enable the apply worker without an explicit production permission-migration window.
4. Use the direct-claim/follow-up benchmark cases as the expectation for Cory-style no-context questions before broadening live tests beyond the safe OE suite. 4. Do not run production apply without explicit authorization. The mapped base, cross-surface anchor, Rio strategy, governance/concept, and Helmer packets compose in one clone and are ready as a packet set, but are not production-applied.
5. Keep VPS Leo runtime behavior unchanged while the DB apply path is stabilized. 5. Use the direct-claim/follow-up benchmark cases as the expectation for Cory-style no-context questions before broadening live tests beyond the safe OE suite.
6. Keep VPS Leo runtime behavior unchanged while the DB apply path is stabilized.
7. Resume GCP DB parity from an already-authenticated Cloud SQL Studio session; do not handle the raw DB password or create IAM/users inside the read-only lane.

View file

@ -196,6 +196,40 @@ Conclusion:
- July 5 "working" included at least one applied strategy proposal, so a staging-only system reads as broken to Cory. - July 5 "working" included at least one applied strategy proposal, so a staging-only system reads as broken to Cory.
- The local Telegram export confirms the exact unresolved tension: Leo's strategy rows changed canonically, but approved claim/evidence/internet-finance changes stayed in `kb_stage`. - The local Telegram export confirms the exact unresolved tension: Leo's strategy rows changed canonically, but approved claim/evidence/internet-finance changes stayed in `kb_stage`.
## Canonical Mutation Standard - 2026-07-10
For a Cory-approved knowledge change, working now means all of the following:
1. Leo reads the canonical graph and names whether the requested object is a
claim, source, evidence link, edge, strategy row, reasoning tool, or a target
that still needs a reviewed storage decision.
2. Rich intent normalizes fail-closed. Every candidate must be an object with a
unique stable key; every candidate must map to exact planned rows; every type
coercion must remain visible to the reviewer. A malformed or lossy proposal
emits no strict child.
3. A dedicated `kb_review` credential approves the exact proposal type and full
JSON payload. Its DB role maps to one reviewer identity, and approval creates
one immutable snapshot containing payload, reviewer, timestamp, and note.
4. A separate `kb_apply` credential can insert only the canonical rows needed by
supported contracts. It cannot directly approve, rewrite proposal payloads,
overwrite existing evidence, or alter the immutable approval snapshot.
5. Apply locks and compares the proposal plus immutable snapshot before writes,
inserts conflict-safely, verifies every payload-controlled persisted field
plus exact table deltas, and only then marks the proposal applied. Stale
payloads, replays, hash collisions, or row mismatches roll back the whole
transaction.
6. The receipt names proposal ID, reviewer, applier, exact target rows, before
and after counts/content, service state, and cleanup. "Approved" without this
canonical receipt remains approved/staged, not implemented.
Final repo-owned runtime proof passes `37/37` for both a generic graph bundle and
the real Helmer 7 Powers bundle. The receipts are bound to current source hashes,
prove exact payload projections and table deltas in disposable PostgreSQL, and
show unchanged live count/service endpoints. This is not production deployment
proof: VPS production remains on deploy SHA
`339565585dc417501488a38135998283988482d1`, counts
`1837/4145/4670/4916/17/26`, and Helmer is still not canonical there.
## VPS DB Path Proof On 2026-07-09 ## VPS DB Path Proof On 2026-07-09
Canary proposal: Canary proposal:

View file

@ -1,6 +1,6 @@
# Working Leo Execution Plan - 2026-07-09 # Working Leo Execution Plan - 2026-07-09
Claim ceiling: VPS Telegram memory/KB audit, full live-safe open-ended Telegram suite, full mixed-evidence Cory-style outcome/direct-claim sandbox benchmark, live VPS GatewayRunner no-context direct-claim suite, Telegram-visible DC authorization packet, Telegram-visible DC pre-send preflight, Telegram-to-`kb_stage` proposal staging, and strict existing-ID apply are working. The live VPS handler suite for exact `DC-01` through `DC-06` prompts strict-scores `6/6` after the authorized-session-plan deploy and scorer refresh, with no Telegram post, no live profile write, no production DB apply, unchanged DB counts, temp profile cleanup, and unchanged gateway service state during the harness. The Telegram-visible DC suite is exact-scoped and ready to request authorization, but not sent; the pre-send preflight now passes from supplied top-level SSH readback payload with deploy head and `.last-deploy-sha` at `63278b6354d3f5f15ed68897be95d484530db59f`, active/running service state (`MainPID=2999690`, `NRestarts=0`), and DB counts present. The capture receipt assembler is retained but remains `awaiting_capture` until real visible replies plus DB/service/visual proof are supplied and scored. Public Argus HTTP diagnostics currently show `3` approved proposals, all `approved_needs_apply_payload`, with `worker_applyable_count=0`, which supports the expected no-overclaim answer but does not prove Docker row counts or systemd state. The full prepared DB packet set is integrated-clone-proven in dependency order: mapped rich base, cross-surface anchor, Rio strategy context, governance/concept schema, and Helmer 7 Powers. A read-only live production preflight baseline now passes `5/5`, an authorized apply operator runbook is generated, an authorized apply session plan maps every apply-window output path with authorization gates, a production apply authorization packet is ready-to-request, the authorization verifier refuses the current apply window because no exact authorization text/operator identity is present, and the receipt assembler refuses final receipt emission because post-apply session artifacts/counts are absent. None of those packets are production-applied or authorized. GCP parity remains pending. Claim ceiling: VPS Telegram memory/KB audit, full live-safe open-ended Telegram suite, full mixed-evidence Cory-style outcome/direct-claim sandbox benchmark, live VPS GatewayRunner no-context direct-claim suite, Telegram-visible DC authorization packet, Telegram-visible DC pre-send preflight, Telegram-to-`kb_stage` proposal staging, and strict existing-ID apply are working. The live VPS handler suite for exact `DC-01` through `DC-06` prompts strict-scores `6/6` after the authorized-session-plan deploy and scorer refresh, with no Telegram post, no live profile write, no production DB apply, unchanged DB counts, temp profile cleanup, and unchanged gateway service state during the harness. The Telegram-visible DC suite is exact-scoped and ready to request authorization, but not sent; the pre-send preflight now passes from supplied top-level SSH readback payload with deploy head and `.last-deploy-sha` at `63278b6354d3f5f15ed68897be95d484530db59f`, active/running service state (`MainPID=2999690`, `NRestarts=0`), and DB counts present. The capture receipt assembler is retained but remains `awaiting_capture` until real visible replies plus DB/service/visual proof are supplied and scored. Public Argus HTTP diagnostics currently show `3` approved proposals, all `approved_needs_apply_payload`, with `worker_applyable_count=0`, which supports the expected no-overclaim answer but does not prove Docker row counts or systemd state. The full prepared DB packet set is integrated-clone-proven in dependency order: mapped rich base, cross-surface anchor, Rio strategy context, governance/concept schema, and Helmer 7 Powers. A read-only live production preflight baseline now passes `5/5`, an authorized apply operator runbook is generated, an authorized apply session plan maps every apply-window output path with authorization gates, a production apply authorization packet is ready-to-request, the authorization verifier refuses the current apply window because no exact authorization text/operator identity is present, and the receipt assembler refuses final receipt emission because post-apply session artifacts/counts are absent. None of those packets are production-applied or authorized. GCP project/VM/private Cloud SQL inventory is current; GCP DB-row, runtime, Cory-answer, and cutover parity remain pending.
## WL-EXEC-01 - VPS Telegram-visible memory + KB audit ## WL-EXEC-01 - VPS Telegram-visible memory + KB audit
- Status: `done` - Status: `done`
@ -62,6 +62,20 @@ Claim ceiling: VPS Telegram memory/KB audit, full live-safe open-ended Telegram
- `/Users/user/Documents/Codex/2026-07-09/019f34eb-d297-72d0-b7e2-b222d5515ab9-load/outputs/rich-proposal-creation-plan-20260709/rich-proposal-creation-plan-mapped-current.md` - `/Users/user/Documents/Codex/2026-07-09/019f34eb-d297-72d0-b7e2-b222d5515ab9-load/outputs/rich-proposal-creation-plan-20260709/rich-proposal-creation-plan-mapped-current.md`
- `/Users/user/Documents/Codex/2026-07-09/019f34eb-d297-72d0-b7e2-b222d5515ab9-load/outputs/rich-proposal-creation-plan-20260709/staging-db-commit-rehearsal-current.log` - `/Users/user/Documents/Codex/2026-07-09/019f34eb-d297-72d0-b7e2-b222d5515ab9-load/outputs/rich-proposal-creation-plan-20260709/staging-db-commit-rehearsal-current.log`
## WL-EXEC-05A - Approved claim-bundle normal apply path
- Status: `done_isolated_runtime_proven_not_deployed`
- Result: Added `approve_claim` plus a fail-closed rich normalizer, separate `kb_review`/`kb_apply` credentials, mapped reviewer identity, immutable approval snapshots, exact-payload row locks, denied direct ledger/evidence mutation, poison-safe worker selection, and exact payload-projection plus table-delta readback. Final source-bound isolated-container receipts pass `37/37` for both the generic bundle (`2/2/2/1/1`) and real Helmer bundle (`5` claims, `13` sources, `17` evidence rows, `7` edges, `1` reasoning tool). Stale approval payload, replay, source-hash conflict, legacy function overloads, role memberships, and stale ACL paths fail closed without partial canonical rows. Read-only live endpoint counts remain `1837/4145/4670/4916/17/26`; gateway stays PID `2999690`, `NRestarts=0`; inner clone, outer container, temp workdir, and remote source-bundle cleanup are independently proven.
- Required tier: `T2_runtime` for repo-owned apply lifecycle plus `T3_live_readonly` endpoint stability; current tiers: both proven.
- Next action: land the reviewed code in the infrastructure repo. Keep production code deployment, the reviewer/apply permission migration, worker enablement, and canonical Helmer application as separate changes with separate authorization/readback windows.
- Not done condition: The current VPS deploy does not contain this implementation, production role grants are unchanged, the apply worker remains a separate enablement decision, and no existing approved rich proposal has been applied to production.
- Evidence:
- `/Users/user/Documents/Codex/2026-07-09/019f34eb-d297-72d0-b7e2-b222d5515ab9-load/work/teleo-infra-main/docs/reports/leo-working-state-20260709/approve-claim-clone-canary-current.md`
- `/Users/user/Documents/Codex/2026-07-09/019f34eb-d297-72d0-b7e2-b222d5515ab9-load/work/teleo-infra-main/docs/reports/leo-working-state-20260709/approve-claim-clone-canary-current.json`
- `/Users/user/Documents/Codex/2026-07-09/019f34eb-d297-72d0-b7e2-b222d5515ab9-load/work/teleo-infra-main/docs/reports/leo-working-state-20260709/helmer-approve-claim-clone-canary-current.json`
- `/Users/user/Documents/Codex/2026-07-09/019f34eb-d297-72d0-b7e2-b222d5515ab9-load/work/teleo-infra-main/docs/reports/leo-working-state-20260709/helmer-approve-claim-normalization-current.json`
- `/Users/user/Documents/Codex/2026-07-09/019f34eb-d297-72d0-b7e2-b222d5515ab9-load/work/teleo-infra-main/scripts/run_approve_claim_clone_canary.py`
- `/Users/user/Documents/Codex/2026-07-09/019f34eb-d297-72d0-b7e2-b222d5515ab9-load/work/teleo-infra-main/scripts/run_approve_claim_isolated_container_canary.sh`
## WL-EXEC-05B - Production apply packet for mapped subset ## WL-EXEC-05B - Production apply packet for mapped subset
- Status: `done_packet_ready_not_executed` - Status: `done_packet_ready_not_executed`
- Result: Generated packet with preflight, rollback rehearsal, authorized commit SQL, postflight, and exact delete rollback. Live preflight and rollback passed; disposable clone commit produced 7 claims, 15 sources, 17 evidence rows, 11 edges; delete rollback returned clone counts to 0/0/0/0; production stayed zero. - Result: Generated packet with preflight, rollback rehearsal, authorized commit SQL, postflight, and exact delete rollback. Live preflight and rollback passed; disposable clone commit produced 7 claims, 15 sources, 17 evidence rows, 11 edges; delete rollback returned clone counts to 0/0/0/0; production stayed zero.
@ -139,16 +153,18 @@ Claim ceiling: VPS Telegram memory/KB audit, full live-safe open-ended Telegram
- `/Users/user/Documents/Codex/2026-07-09/019f34eb-d297-72d0-b7e2-b222d5515ab9-load/outputs/rich-proposal-creation-plan-20260709/staging-db-mapped-commit-rehearsal-current.log` - `/Users/user/Documents/Codex/2026-07-09/019f34eb-d297-72d0-b7e2-b222d5515ab9-load/outputs/rich-proposal-creation-plan-20260709/staging-db-mapped-commit-rehearsal-current.log`
## WL-EXEC-09 - GCP parity ## WL-EXEC-09 - GCP parity
- Status: `blocked_on_network_dns_before_auth_refresh` - Status: `control_plane_inventory_done_database_auth_gated`
- Result: Fresh current probe uses a temporary writable gcloud config copy and keeps token stdout redacted. Local config still selects `billy@livingip.xyz` for `teleo-501523`, but every token/project readback fails at DNS resolution for `oauth2.googleapis.com`; this runner cannot reach the prior auth/project-access gate. - Result: Parallel GCP owners used CLI plus authenticated Chrome/Computer Use. Live readback confirms project `teleo-501523`, running VM `teleo-prod-1` in `europe-west6-a`, and exactly one Cloud SQL instance: available/runnable PostgreSQL `16.14` instance `teleo-pgvector-standby` in `europe-west6-c`, private-only on `teleo-staging-net`. Cloud SQL Studio loads, but the current session exposes no enabled IAM database principal and requires built-in password authentication. No credential was inspected/submitted and no SQL ran. Cloud SQL row/schema parity, VM runtime parity, and GCP `DC-01` through `DC-06` remain unproven.
- Not done condition: No GCP parity claim until `scripts/probe_gcp_db_parity.py` reports `ready_for_gcp_readonly_parity=true` and then read-only VM/Cloud SQL/runtime parity is captured. - Next action: authenticate Studio with an already-existing authorized DB principal without sharing the credential, then run the retained read-only transaction/count query; otherwise use a separately authorized IAM/database-user enablement window.
- Not done condition: No GCP parity claim until current Cloud SQL/VM/runtime readback and DC suite proof exist.
- Evidence: - Evidence:
- `/Users/user/Documents/Codex/2026-07-09/019f34eb-d297-72d0-b7e2-b222d5515ab9-load/work/teleo-infra-main/docs/reports/leo-working-state-20260709/gcp-db-parity-current-probe-current.md` - `/Users/user/Documents/Codex/2026-07-09/019f34eb-d297-72d0-b7e2-b222d5515ab9-load/work/teleo-infra-main/docs/reports/leo-working-state-20260709/gcp-db-parity-current-probe-current.md`
- `/Users/user/Documents/Codex/2026-07-09/019f34eb-d297-72d0-b7e2-b222d5515ab9-load/outputs/gcp-db-parity-current-blocker-20260709.json` - `/Users/user/Documents/Codex/2026-07-09/019f34eb-d297-72d0-b7e2-b222d5515ab9-load/work/teleo-infra-main/docs/reports/leo-working-state-20260709/gcp-cloud-sql-t3-live-readonly-current.json`
- `/Users/user/Documents/Codex/2026-07-09/019f34eb-d297-72d0-b7e2-b222d5515ab9-load/work/teleo-infra-main/docs/reports/leo-working-state-20260709/gcp-cloudsql-studio-parity-gate-current.json`
## WL-EXEC-10 - Cleanup and retained evidence ## WL-EXEC-10 - Cleanup and retained evidence
- Status: `done_current_pass` - Status: `done_current_pass`
- Result: Temporary VPS SQL files removed; macOS sidecar files removed from remote report dir; reports copied under leoclean profile. Fresh read-only cleanup check found no leftover VPS Postgres database names matching `rehearsal|clone|staging`; gateway stayed active/running with `MainPID=1908033`, `NRestarts=0`. - Result: All final review/apply inner clone databases, outer Postgres containers, remote staging trees, and temp workdirs were removed. Fresh live counts remain `1837/4145/4670/4916/26`; gateway is active/running with `MainPID=2999690`, `NRestarts=0`.
- Not done condition: Reopen if any temp clone/VPS/bot is later created. - Not done condition: Reopen if any temp clone/VPS/bot is later created.
- Evidence: - Evidence:
- `/Users/user/Documents/Codex/2026-07-09/019f34eb-d297-72d0-b7e2-b222d5515ab9-load/outputs/working-leo-current-state-20260709.md` - `/Users/user/Documents/Codex/2026-07-09/019f34eb-d297-72d0-b7e2-b222d5515ab9-load/outputs/working-leo-current-state-20260709.md`

View file

@ -6,10 +6,12 @@ Stage 2 of the KB apply pipeline: approve -> APPLY -> render -> surface.
Governance boundary Governance boundary
------------------- -------------------
This tool connects to Postgres as the narrow ``kb_apply`` login role, never as This tool connects to Postgres as the narrow ``kb_apply`` login role, never as
superuser. ``kb_apply`` can write only ``public.strategies``, superuser. ``kb_apply`` can insert reviewed claim/source/reasoning-tool rows and
``public.strategy_nodes``, ``public.claim_evidence``, ``public.claim_edges`` and write the strategy/evidence/edge tables required by supported contracts. It has
update the ``kb_stage.kb_proposals`` ledger. It holds no DELETE, no DDL, and no SELECT-only access to ``kb_stage.kb_proposals``; payload-bound SECURITY DEFINER
access to any other table (agents, budgets, personas, ...). This enforces functions are the only way it can lock and finish an approved ledger row. It
holds no DELETE or DDL and no write access to unrelated tables (agents, budgets,
personas, ...). This enforces
"agents propose, but do not self-apply" at the database boundary rather than by "agents propose, but do not self-apply" at the database boundary rather than by
convention. The tool is an operator, invoked by a human/reviewer after approval; convention. The tool is an operator, invoked by a human/reviewer after approval;
it is deliberately not part of the agent runtime. it is deliberately not part of the agent runtime.
@ -30,7 +32,7 @@ ensures the one-active-strategy unique index. Run it before the first apply.
``--dry-run`` prints the exact SQL and does not connect for writes. ``--dry-run`` prints the exact SQL and does not connect for writes.
Contract (v1) Contract (v2)
------------- -------------
Freeform Leo eval packets are NOT applied directly. Each proposal must carry a Freeform Leo eval packets are NOT applied directly. Each proposal must carry a
strict ``apply_payload`` object; a separate normalizer produces it. Shapes: strict ``apply_payload`` object; a separate normalizer produces it. Shapes:
@ -53,9 +55,30 @@ strict ``apply_payload`` object; a separate normalizer produces it. Shapes:
"role": "<evidence_role>", "role": "<evidence_role>",
"weight": <0..1|null>}, ...]}} "weight": <0..1|null>}, ...]}}
Note: attach_evidence requires an existing ``source_id`` per item. kb_apply has approve_claim:
no INSERT on ``public.sources``, so source creation/dedup is intentionally out of {"apply_payload": {
scope for this tool and handled upstream (a follow-up + explicit grant). "agent_id": "<uuid|null>",
"claims": [{"id": "<uuid>", "type": "<claim type>",
"text": str, "status": "open", "confidence": <0..1|null>,
"tags": [str, ...], "created_by": "<uuid|null>"}, ...],
"sources": [{"id": "<uuid>", "source_type": "<source type>",
"url": str|null, "storage_path": str|null,
"excerpt": str|null, "hash": str,
"created_by": "<uuid|null>"}, ...],
"evidence": [{"claim_id": "<uuid>", "source_id": "<uuid>",
"role": "<evidence role>", "weight": <0..1|null>,
"created_by": "<uuid|null>"}, ...],
"edges": [{"from_claim": "<uuid>", "to_claim": "<uuid>",
"edge_type": "<edge type>", "weight": <0..1|null>,
"created_by": "<uuid|null>"}, ...],
"reasoning_tools": [{"id": "<uuid>", "agent_id": "<uuid|null>",
"name": str, "description": str,
"category": str|null}, ...]}}
``approve_claim`` is the only source-creation contract. It is an atomic,
insert-only canonical bundle: existing IDs must match the reviewed payload
exactly, source hashes cannot alias another ID, and every row is verified before
the approved proposal ledger can flip to ``applied``.
""" """
from __future__ import annotations from __future__ import annotations
@ -64,8 +87,9 @@ import argparse
import json import json
import subprocess import subprocess
import sys import sys
import uuid
from pathlib import Path from pathlib import Path
from typing import Any, Dict, List, Optional from typing import Any
DEFAULT_SECRETS_FILE = "/home/teleo/.hermes/profiles/leoclean/secrets/kb-apply.env" DEFAULT_SECRETS_FILE = "/home/teleo/.hermes/profiles/leoclean/secrets/kb-apply.env"
DEFAULT_CONTAINER = "teleo-pg" DEFAULT_CONTAINER = "teleo-pg"
@ -78,84 +102,170 @@ DEFAULT_ROLE = "kb_apply"
# public.agents so it can resolve this into applied_by_agent_id, never INSERT. # public.agents so it can resolve this into applied_by_agent_id, never INSERT.
SERVICE_AGENT_HANDLE = "kb-apply" SERVICE_AGENT_HANDLE = "kb-apply"
APPLYABLE_TYPES = ("revise_strategy", "add_edge", "attach_evidence") APPLYABLE_TYPES = ("revise_strategy", "add_edge", "attach_evidence", "approve_claim")
CLAIM_TYPES = {"empirical", "structural", "normative", "meta", "concept"}
SOURCE_TYPES = {"paper", "article", "transcript", "observation", "dataset", "post", "dm", "other"}
EVIDENCE_ROLES = {"grounds", "illustrates", "contradicts"}
EDGE_TYPES = {
"supports", "challenges", "requires", "relates", "contradicts",
"supersedes", "derives_from", "cites", "causes", "constrains", "accelerates",
}
MAX_BUNDLE_ROWS = {
"claims": 100,
"sources": 500,
"evidence": 1000,
"edges": 1000,
"reasoning_tools": 50,
}
# --------------------------------------------------------------------------- # # --------------------------------------------------------------------------- #
# SQL helpers # # SQL helpers #
# --------------------------------------------------------------------------- # # --------------------------------------------------------------------------- #
def sql_literal(value: Any) -> str: def sql_literal(value: Any) -> str:
"""Render a Python value as a safe SQL literal.""" """Render a Python value as an explicit PostgreSQL escape-string literal."""
if value is None: if value is None:
return "null" return "null"
if isinstance(value, bool): if isinstance(value, bool):
return "true" if value else "false" return "true" if value else "false"
if isinstance(value, (int, float)): if isinstance(value, (int, float)):
return repr(value) return repr(value)
return "'" + str(value).replace("'", "''") + "'" escaped = str(value).replace("\\", "\\\\").replace("'", "''")
return "E'" + escaped + "'"
def _jsonb(value: Any) -> str: def _jsonb(value: Any) -> str:
return sql_literal(json.dumps(value, sort_keys=True)) + "::jsonb" return sql_literal(json.dumps(value, sort_keys=True)) + "::jsonb"
def _ledger_and_verify(proposal_id: str, applied_by: str, extra_checks: str) -> str: def _text_array(values: list[str]) -> str:
"""Flip the proposal to applied and verify invariants, inside the txn. if not values:
return "'{}'::text[]"
return "array[" + ", ".join(sql_literal(value) for value in values) + "]::text[]"
The flip runs in a DO block that asserts exactly one row moved from
'approved' to 'applied' (``GET DIAGNOSTICS ... = row_count``). This closes
the concurrent double-apply race: ``load_proposal`` (read) and this flip
(write) are separate statements, so a ``SELECT ... FOR UPDATE`` row lock
would not span them -- but only one concurrent apply can match
``status='approved'``, so rowcount=1 is the authoritative guard. A loser
sees 0 rows and RAISEs -> the whole transaction rolls back.
``applied_by_agent_id`` is stamped as a real FK resolved from def _uuid(value: Any, path: str, *, nullable: bool = False) -> str | None:
``public.agents`` by handle (requires SELECT public.agents; the service-agent if value is None and nullable:
row is bootstrapped once by superuser -- see scripts/kb_apply_prereqs.sql). return None
The resolve is a hard lookup: an unresolved handle RAISEs -> rollback, never try:
a silent NULL FK (the column is ON DELETE SET NULL, so NULL is legal and return str(uuid.UUID(str(value)))
would otherwise pass unnoticed). except (TypeError, ValueError, AttributeError) as exc:
""" raise ValueError(f"{path} must be a UUID{' or null' if nullable else ''}") from exc
return f"""
do $flip$
declare def _weight(value: Any, path: str) -> Any:
flipped int; if value is None:
resolved_agent_id uuid; return None
if isinstance(value, bool) or not isinstance(value, (int, float)):
raise ValueError(f"{path} must be a number from 0 to 1 or null")
if not 0 <= value <= 1:
raise ValueError(f"{path} must be between 0 and 1")
return value
def _rows(payload: dict[str, Any], key: str, *, required: bool = False) -> list[dict[str, Any]]:
value = payload.get(key)
if value is None:
value = []
if not isinstance(value, list) or any(not isinstance(row, dict) for row in value):
raise ValueError(f"approve_claim apply_payload.{key} must be a list of objects")
if required and not value:
raise ValueError(f"approve_claim apply_payload requires non-empty '{key}'")
if len(value) > MAX_BUNDLE_ROWS[key]:
raise ValueError(f"approve_claim apply_payload.{key} exceeds {MAX_BUNDLE_ROWS[key]} rows")
return value
def _dedupe(values: list[Any], path: str) -> None:
seen = set()
for value in values:
marker = json.dumps(value, sort_keys=True)
if marker in seen:
raise ValueError(f"{path} contains a duplicate: {value!r}")
seen.add(marker)
def _reject_unknown(mapping: dict[str, Any], allowed: set[str], path: str) -> None:
unknown = sorted(set(mapping) - allowed)
if unknown:
raise ValueError(f"{path} contains unknown fields: {unknown}")
def _validate_approval_meta(
approval: dict[str, Any], proposal_type: str, apply_payload: dict[str, Any]
) -> None:
if not isinstance(approval, dict):
raise ValueError("approved proposal metadata is required")
if approval.get("proposal_type") != proposal_type:
raise ValueError("approved proposal_type does not match the apply builder")
payload = approval.get("payload")
if not isinstance(payload, dict) or payload.get("apply_payload") != apply_payload:
raise ValueError("approved proposal payload does not match the strict apply payload")
if not str(approval.get("reviewed_by_handle") or "").strip():
raise ValueError("approved proposal requires reviewed_by_handle")
if not str(approval.get("reviewed_at") or "").strip():
raise ValueError("approved proposal requires reviewed_at")
if not str(approval.get("review_note") or "").strip():
raise ValueError("approved proposal requires review_note")
_uuid(
approval.get("reviewed_by_agent_id"),
"approved proposal reviewed_by_agent_id",
nullable=True,
)
def _approval_guard_sql(proposal_id: str, approval: dict[str, Any]) -> str:
return f"""select kb_stage.assert_approved_proposal(
{sql_literal(proposal_id)}::uuid,
{sql_literal(approval['proposal_type'])},
{_jsonb(approval['payload'])},
{sql_literal(approval['reviewed_by_handle'])},
{sql_literal(approval.get('reviewed_by_agent_id'))}::uuid,
{sql_literal(approval['reviewed_at'])}::timestamptz,
{sql_literal(approval['review_note'])}
);"""
def _ledger_and_verify(
proposal_id: str,
applied_by: str,
extra_checks: str,
approval: dict[str, Any],
) -> str:
"""Verify canonical rows, then finish the locked, payload-bound apply."""
checks_sql = f"""do $verify$
begin begin
select id into resolved_agent_id
from public.agents
where handle = {sql_literal(applied_by)};
if resolved_agent_id is null then
raise exception 'apply_proposal: applied_by handle % does not resolve to a public.agents row; refusing to stamp a NULL applied_by_agent_id', {sql_literal(applied_by)};
end if;
update kb_stage.kb_proposals
set status = 'applied',
applied_by_handle = {sql_literal(applied_by)},
applied_by_agent_id = resolved_agent_id,
applied_at = now(),
updated_at = now()
where id = {sql_literal(proposal_id)}::uuid
and status = 'approved';
get diagnostics flipped = row_count;
if flipped <> 1 then
raise exception 'apply_proposal: ledger flip affected % row(s), expected 1; proposal % was not approved (already applied or a concurrent apply won)', flipped, {sql_literal(proposal_id)};
end if;
{extra_checks} {extra_checks}
end end
$flip$; $verify$;"""
""".rstrip() finish_sql = f"""select kb_stage.finish_approved_proposal(
{sql_literal(proposal_id)}::uuid,
{sql_literal(approval['proposal_type'])},
{_jsonb(approval['payload'])},
{sql_literal(approval['reviewed_by_handle'])},
{sql_literal(approval.get('reviewed_by_agent_id'))}::uuid,
{sql_literal(approval['reviewed_at'])}::timestamptz,
{sql_literal(approval['review_note'])},
{sql_literal(applied_by)}
);"""
return checks_sql + "\n" + finish_sql
# --------------------------------------------------------------------------- # # --------------------------------------------------------------------------- #
# Per-type SQL builders (pure; unit-tested without a DB) # # Per-type SQL builders (pure; unit-tested without a DB) #
# --------------------------------------------------------------------------- # # --------------------------------------------------------------------------- #
def build_revise_strategy_sql( def build_revise_strategy_sql(
apply_payload: Dict[str, Any], proposal_id: str, reviewer: Optional[str] apply_payload: dict[str, Any],
proposal_id: str,
applied_by: str | None,
approval: dict[str, Any],
) -> str: ) -> str:
_validate_approval_meta(approval, "revise_strategy", apply_payload)
agent_id = apply_payload.get("agent_id") agent_id = apply_payload.get("agent_id")
strategy = apply_payload.get("strategy") or {} strategy = apply_payload.get("strategy") or {}
nodes: List[Dict[str, Any]] = apply_payload.get("strategy_nodes") or [] nodes: list[dict[str, Any]] = apply_payload.get("strategy_nodes") or []
if not agent_id: if not agent_id:
raise ValueError("revise_strategy apply_payload requires 'agent_id'") raise ValueError("revise_strategy apply_payload requires 'agent_id'")
for key in ("diagnosis", "guiding_policy", "proximate_objectives"): for key in ("diagnosis", "guiding_policy", "proximate_objectives"):
@ -212,12 +322,19 @@ values
raise exception 'apply_proposal: expected exactly one active strategy for agent %', {sql_literal(agent_id)}; raise exception 'apply_proposal: expected exactly one active strategy for agent %', {sql_literal(agent_id)};
end if;""" end if;"""
return _wrap_txn(canonical, _ledger_and_verify(proposal_id, reviewer, checks)) ledger = _ledger_and_verify(
proposal_id, applied_by or SERVICE_AGENT_HANDLE, checks, approval
)
return _wrap_txn(canonical, ledger, _approval_guard_sql(proposal_id, approval))
def build_add_edge_sql( def build_add_edge_sql(
apply_payload: Dict[str, Any], proposal_id: str, reviewer: Optional[str] apply_payload: dict[str, Any],
proposal_id: str,
applied_by: str | None,
approval: dict[str, Any],
) -> str: ) -> str:
_validate_approval_meta(approval, "add_edge", apply_payload)
f = apply_payload.get("from_claim") f = apply_payload.get("from_claim")
t = apply_payload.get("to_claim") t = apply_payload.get("to_claim")
et = apply_payload.get("edge_type") et = apply_payload.get("edge_type")
@ -227,7 +344,12 @@ def build_add_edge_sql(
if f == t: if f == t:
raise ValueError("add_edge from_claim and to_claim must differ") raise ValueError("add_edge from_claim and to_claim must differ")
edge_lock_key = f"claim_edge:{f}:{t}:{et}"
canonical = f""" canonical = f"""
-- serialize semantic edge creation because the historical table has no
-- (from_claim,to_claim,edge_type) uniqueness constraint.
select pg_advisory_xact_lock(hashtextextended({sql_literal(edge_lock_key)}, 0));
-- insert the edge unless an identical (from,to,type) edge already exists -- insert the edge unless an identical (from,to,type) edge already exists
insert into public.claim_edges (from_claim, to_claim, edge_type, weight) insert into public.claim_edges (from_claim, to_claim, edge_type, weight)
select {sql_literal(f)}::uuid, {sql_literal(t)}::uuid, select {sql_literal(f)}::uuid, {sql_literal(t)}::uuid,
@ -239,20 +361,35 @@ select {sql_literal(f)}::uuid, {sql_literal(t)}::uuid,
and edge_type = {sql_literal(et)}::edge_type); and edge_type = {sql_literal(et)}::edge_type);
""".rstrip() """.rstrip()
checks = f""" if not exists (select 1 from public.claim_edges checks = f""" if exists (select 1 from public.claim_edges
where from_claim = {sql_literal(f)}::uuid where from_claim = {sql_literal(f)}::uuid
and to_claim = {sql_literal(t)}::uuid and to_claim = {sql_literal(t)}::uuid
and edge_type = {sql_literal(et)}::edge_type) then and edge_type = {sql_literal(et)}::edge_type
raise exception 'apply_proposal: claim_edge not present after apply'; and weight is distinct from {sql_literal(w)}) then
raise exception 'apply_proposal: claim_edge row does not match strict apply payload';
end if;
if not exists (select 1 from public.claim_edges
where from_claim = {sql_literal(f)}::uuid
and to_claim = {sql_literal(t)}::uuid
and edge_type = {sql_literal(et)}::edge_type
and weight is not distinct from {sql_literal(w)}) then
raise exception 'apply_proposal: claim_edge row does not match strict apply payload';
end if;""" end if;"""
return _wrap_txn(canonical, _ledger_and_verify(proposal_id, reviewer, checks)) ledger = _ledger_and_verify(
proposal_id, applied_by or SERVICE_AGENT_HANDLE, checks, approval
)
return _wrap_txn(canonical, ledger, _approval_guard_sql(proposal_id, approval))
def build_attach_evidence_sql( def build_attach_evidence_sql(
apply_payload: Dict[str, Any], proposal_id: str, reviewer: Optional[str] apply_payload: dict[str, Any],
proposal_id: str,
applied_by: str | None,
approval: dict[str, Any],
) -> str: ) -> str:
evidence: List[Dict[str, Any]] = apply_payload.get("evidence") or [] _validate_approval_meta(approval, "attach_evidence", apply_payload)
evidence: list[dict[str, Any]] = apply_payload.get("evidence") or []
if not evidence: if not evidence:
raise ValueError("attach_evidence apply_payload requires non-empty 'evidence'") raise ValueError("attach_evidence apply_payload requires non-empty 'evidence'")
@ -274,37 +411,406 @@ def build_attach_evidence_sql(
f"""insert into public.claim_evidence (claim_id, source_id, role, weight) f"""insert into public.claim_evidence (claim_id, source_id, role, weight)
select {sql_literal(claim_id)}::uuid, {sql_literal(source_id)}::uuid, select {sql_literal(claim_id)}::uuid, {sql_literal(source_id)}::uuid,
{sql_literal(role)}::evidence_role, {sql_literal(weight)} {sql_literal(role)}::evidence_role, {sql_literal(weight)}
where not exists ( on conflict (claim_id, source_id, role) do nothing;"""
select 1 from public.claim_evidence
where claim_id = {sql_literal(claim_id)}::uuid
and source_id = {sql_literal(source_id)}::uuid
and role = {sql_literal(role)}::evidence_role);"""
) )
checks.append( checks.append(
f""" if not exists (select 1 from public.claim_evidence f""" if exists (select 1 from public.claim_evidence
where claim_id = {sql_literal(claim_id)}::uuid where claim_id = {sql_literal(claim_id)}::uuid
and source_id = {sql_literal(source_id)}::uuid and source_id = {sql_literal(source_id)}::uuid
and role = {sql_literal(role)}::evidence_role) then and role = {sql_literal(role)}::evidence_role
raise exception 'apply_proposal: evidence row % not present after apply', {i}; and weight is distinct from {sql_literal(weight)}) then
raise exception 'apply_proposal: evidence row % does not match strict apply payload', {i};
end if;
if not exists (select 1 from public.claim_evidence
where claim_id = {sql_literal(claim_id)}::uuid
and source_id = {sql_literal(source_id)}::uuid
and role = {sql_literal(role)}::evidence_role
and weight is not distinct from {sql_literal(weight)}) then
raise exception 'apply_proposal: evidence row % does not match strict apply payload', {i};
end if;""" end if;"""
) )
canonical = "\n".join(statements) canonical = "\n".join(statements)
return _wrap_txn(canonical, _ledger_and_verify(proposal_id, reviewer, "\n".join(checks))) ledger = _ledger_and_verify(
proposal_id, applied_by or SERVICE_AGENT_HANDLE, "\n".join(checks), approval
)
return _wrap_txn(canonical, ledger, _approval_guard_sql(proposal_id, approval))
def _wrap_txn(canonical_sql: str, ledger_sql: str) -> str: def build_approve_claim_sql(
return f"begin;\n{canonical_sql}\n{ledger_sql}\ncommit;\n" apply_payload: dict[str, Any],
proposal_id: str,
applied_by: str | None,
approval: dict[str, Any],
) -> str:
"""Build one conflict-safe transaction for a reviewed canonical graph bundle."""
_validate_approval_meta(approval, "approve_claim", apply_payload)
_reject_unknown(
apply_payload,
{
"contract_version",
"source_proposal_id",
"agent_id",
"claims",
"sources",
"evidence",
"edges",
"reasoning_tools",
"normalization_manifest",
},
"approve_claim apply_payload",
)
if apply_payload.get("contract_version") != 2:
raise ValueError("approve_claim apply_payload.contract_version must equal 2")
for key in ("claims", "sources", "evidence", "edges", "reasoning_tools"):
if key not in apply_payload:
raise ValueError(f"approve_claim apply_payload requires explicit '{key}' collection")
if apply_payload.get("source_proposal_id") is not None:
_uuid(apply_payload["source_proposal_id"], "approve_claim.source_proposal_id")
if apply_payload.get("normalization_manifest") is not None and not isinstance(
apply_payload["normalization_manifest"], dict
):
raise ValueError("approve_claim.normalization_manifest must be an object")
agent_id = _uuid(apply_payload.get("agent_id"), "approve_claim.agent_id", nullable=True)
raw_claims = _rows(apply_payload, "claims", required=True)
raw_sources = _rows(apply_payload, "sources")
raw_evidence = _rows(apply_payload, "evidence")
raw_edges = _rows(apply_payload, "edges")
raw_tools = _rows(apply_payload, "reasoning_tools")
claims: list[dict[str, Any]] = []
for index, row in enumerate(raw_claims):
path = f"approve_claim.claims[{index}]"
_reject_unknown(
row,
{"id", "type", "text", "status", "confidence", "tags", "created_by", "superseded_by"},
path,
)
claim_type = row.get("type")
if claim_type not in CLAIM_TYPES:
raise ValueError(f"{path}.type must be one of {sorted(CLAIM_TYPES)}")
text = row.get("text")
if not isinstance(text, str) or not text.strip():
raise ValueError(f"{path}.text must be a non-empty string")
status = row.get("status", "open")
if status != "open":
raise ValueError(f"{path}.status must be 'open' for an insert-only approval")
tags = row.get("tags") or []
if not isinstance(tags, list) or any(not isinstance(tag, str) for tag in tags):
raise ValueError(f"{path}.tags must be a list of strings")
claims.append(
{
"id": _uuid(row.get("id"), f"{path}.id"),
"type": claim_type,
"text": text,
"status": status,
"confidence": _weight(row.get("confidence"), f"{path}.confidence"),
"tags": tags,
"created_by": _uuid(
row.get("created_by", agent_id), f"{path}.created_by", nullable=True
),
"superseded_by": _uuid(
row.get("superseded_by"), f"{path}.superseded_by", nullable=True
),
}
)
sources: list[dict[str, Any]] = []
for index, row in enumerate(raw_sources):
path = f"approve_claim.sources[{index}]"
_reject_unknown(
row,
{"id", "source_type", "url", "storage_path", "excerpt", "hash", "created_by"},
path,
)
source_type = row.get("source_type")
if source_type not in SOURCE_TYPES:
raise ValueError(f"{path}.source_type must be one of {sorted(SOURCE_TYPES)}")
source_hash = row.get("hash")
if not isinstance(source_hash, str) or not source_hash.strip():
raise ValueError(f"{path}.hash must be a non-empty string")
for key in ("url", "storage_path", "excerpt"):
if row.get(key) is not None and not isinstance(row.get(key), str):
raise ValueError(f"{path}.{key} must be a string or null")
sources.append(
{
"id": _uuid(row.get("id"), f"{path}.id"),
"source_type": source_type,
"url": row.get("url"),
"storage_path": row.get("storage_path"),
"excerpt": row.get("excerpt"),
"hash": source_hash,
"created_by": _uuid(
row.get("created_by", agent_id), f"{path}.created_by", nullable=True
),
}
)
evidence: list[dict[str, Any]] = []
for index, row in enumerate(raw_evidence):
path = f"approve_claim.evidence[{index}]"
_reject_unknown(
row,
{"claim_id", "source_id", "role", "weight", "created_by"},
path,
)
role = row.get("role", "grounds")
if role not in EVIDENCE_ROLES:
raise ValueError(f"{path}.role must be one of {sorted(EVIDENCE_ROLES)}")
evidence.append(
{
"claim_id": _uuid(row.get("claim_id"), f"{path}.claim_id"),
"source_id": _uuid(row.get("source_id"), f"{path}.source_id"),
"role": role,
"weight": _weight(row.get("weight"), f"{path}.weight"),
"created_by": _uuid(
row.get("created_by", agent_id), f"{path}.created_by", nullable=True
),
}
)
edges: list[dict[str, Any]] = []
for index, row in enumerate(raw_edges):
path = f"approve_claim.edges[{index}]"
_reject_unknown(
row,
{"from_claim", "to_claim", "edge_type", "weight", "created_by"},
path,
)
edge_type = row.get("edge_type")
if edge_type not in EDGE_TYPES:
raise ValueError(f"{path}.edge_type must be one of {sorted(EDGE_TYPES)}")
from_claim = _uuid(row.get("from_claim"), f"{path}.from_claim")
to_claim = _uuid(row.get("to_claim"), f"{path}.to_claim")
if from_claim == to_claim:
raise ValueError(f"{path} cannot be a self-edge")
edges.append(
{
"from_claim": from_claim,
"to_claim": to_claim,
"edge_type": edge_type,
"weight": _weight(row.get("weight"), f"{path}.weight"),
"created_by": _uuid(
row.get("created_by", agent_id), f"{path}.created_by", nullable=True
),
}
)
reasoning_tools: list[dict[str, Any]] = []
for index, row in enumerate(raw_tools):
path = f"approve_claim.reasoning_tools[{index}]"
_reject_unknown(
row,
{"id", "agent_id", "name", "description", "category"},
path,
)
name = row.get("name")
description = row.get("description")
category = row.get("category")
if not isinstance(name, str) or not name.strip():
raise ValueError(f"{path}.name must be a non-empty string")
if not isinstance(description, str) or not description.strip():
raise ValueError(f"{path}.description must be a non-empty string")
if category is not None and not isinstance(category, str):
raise ValueError(f"{path}.category must be a string or null")
reasoning_tools.append(
{
"id": _uuid(row.get("id"), f"{path}.id"),
"agent_id": _uuid(row.get("agent_id", agent_id), f"{path}.agent_id", nullable=True),
"name": name,
"description": description,
"category": category,
}
)
_dedupe([row["id"] for row in claims], "approve_claim.claims ids")
_dedupe([row["id"] for row in sources], "approve_claim.sources ids")
_dedupe([row["hash"] for row in sources], "approve_claim.sources hashes")
_dedupe(
[[row["claim_id"], row["source_id"], row["role"]] for row in evidence],
"approve_claim.evidence keys",
)
_dedupe(
[[row["from_claim"], row["to_claim"], row["edge_type"]] for row in edges],
"approve_claim.edges keys",
)
_dedupe([row["id"] for row in reasoning_tools], "approve_claim.reasoning_tools ids")
statements: list[str] = []
checks: list[str] = []
if sources:
collision_checks = []
for row in sources:
collision_checks.append(
f""" if exists (select 1 from public.sources
where hash = {sql_literal(row['hash'])}
and id <> {sql_literal(row['id'])}::uuid) then
raise exception 'approve_claim: source hash collision for source %', {sql_literal(row['id'])};
end if;"""
)
statements.append(
"do $source_conflicts$\nbegin\n"
+ "\n".join(collision_checks)
+ "\nend\n$source_conflicts$;"
)
for row in claims:
tags = _text_array(row["tags"])
statements.append(
f"""insert into public.claims (id, type, text, status, confidence, tags, created_by, superseded_by)
select {sql_literal(row['id'])}::uuid, {sql_literal(row['type'])}, {sql_literal(row['text'])},
{sql_literal(row['status'])}, {sql_literal(row['confidence'])}, {tags},
{sql_literal(row['created_by'])}::uuid, {sql_literal(row['superseded_by'])}::uuid
on conflict (id) do nothing;"""
)
checks.append(
f""" if not exists (select 1 from public.claims
where id = {sql_literal(row['id'])}::uuid
and type = {sql_literal(row['type'])}
and text = {sql_literal(row['text'])}
and status = {sql_literal(row['status'])}
and confidence is not distinct from {sql_literal(row['confidence'])}
and tags is not distinct from {tags}
and created_by is not distinct from {sql_literal(row['created_by'])}::uuid
and superseded_by is not distinct from {sql_literal(row['superseded_by'])}::uuid) then
raise exception 'approve_claim: claim row does not match strict apply payload: %', {sql_literal(row['id'])};
end if;"""
)
for row in sources:
statements.append(
f"""insert into public.sources (id, source_type, url, storage_path, excerpt, hash, created_by)
select {sql_literal(row['id'])}::uuid, {sql_literal(row['source_type'])}, {sql_literal(row['url'])},
{sql_literal(row['storage_path'])}, {sql_literal(row['excerpt'])}, {sql_literal(row['hash'])},
{sql_literal(row['created_by'])}::uuid
on conflict do nothing;"""
)
checks.append(
f""" if exists (select 1 from public.sources
where hash = {sql_literal(row['hash'])}
and id <> {sql_literal(row['id'])}::uuid) then
raise exception 'approve_claim: source hash collision for source %', {sql_literal(row['id'])};
end if;
if not exists (select 1 from public.sources
where id = {sql_literal(row['id'])}::uuid
and source_type = {sql_literal(row['source_type'])}
and url is not distinct from {sql_literal(row['url'])}
and storage_path is not distinct from {sql_literal(row['storage_path'])}
and excerpt is not distinct from {sql_literal(row['excerpt'])}
and hash = {sql_literal(row['hash'])}
and created_by is not distinct from {sql_literal(row['created_by'])}::uuid) then
raise exception 'approve_claim: source row does not match strict apply payload: %', {sql_literal(row['id'])};
end if;"""
)
for row in reasoning_tools:
statements.append(
f"""insert into public.reasoning_tools (id, agent_id, name, description, category)
select {sql_literal(row['id'])}::uuid, {sql_literal(row['agent_id'])}::uuid,
{sql_literal(row['name'])}, {sql_literal(row['description'])}, {sql_literal(row['category'])}
on conflict (id) do nothing;"""
)
checks.append(
f""" if not exists (select 1 from public.reasoning_tools
where id = {sql_literal(row['id'])}::uuid
and agent_id is not distinct from {sql_literal(row['agent_id'])}::uuid
and name = {sql_literal(row['name'])}
and description = {sql_literal(row['description'])}
and category is not distinct from {sql_literal(row['category'])}) then
raise exception 'approve_claim: reasoning tool row does not match strict apply payload: %', {sql_literal(row['id'])};
end if;"""
)
for row in evidence:
statements.append(
f"""insert into public.claim_evidence (claim_id, source_id, role, weight, created_by)
select {sql_literal(row['claim_id'])}::uuid, {sql_literal(row['source_id'])}::uuid,
{sql_literal(row['role'])}::evidence_role, {sql_literal(row['weight'])},
{sql_literal(row['created_by'])}::uuid
on conflict (claim_id, source_id, role) do nothing;"""
)
checks.append(
f""" if exists (select 1 from public.claim_evidence
where claim_id = {sql_literal(row['claim_id'])}::uuid
and source_id = {sql_literal(row['source_id'])}::uuid
and role = {sql_literal(row['role'])}::evidence_role
and (weight is distinct from {sql_literal(row['weight'])}
or created_by is distinct from {sql_literal(row['created_by'])}::uuid)) then
raise exception 'approve_claim: evidence row does not match strict apply payload';
end if;
if not exists (select 1 from public.claim_evidence
where claim_id = {sql_literal(row['claim_id'])}::uuid
and source_id = {sql_literal(row['source_id'])}::uuid
and role = {sql_literal(row['role'])}::evidence_role
and weight is not distinct from {sql_literal(row['weight'])}
and created_by is not distinct from {sql_literal(row['created_by'])}::uuid) then
raise exception 'approve_claim: evidence row does not match strict apply payload';
end if;"""
)
for row in edges:
edge_lock_key = (
f"claim_edge:{row['from_claim']}:{row['to_claim']}:{row['edge_type']}"
)
statements.append(
f"""select pg_advisory_xact_lock(hashtextextended({sql_literal(edge_lock_key)}, 0));
insert into public.claim_edges (from_claim, to_claim, edge_type, weight, created_by)
select {sql_literal(row['from_claim'])}::uuid, {sql_literal(row['to_claim'])}::uuid,
{sql_literal(row['edge_type'])}::edge_type, {sql_literal(row['weight'])},
{sql_literal(row['created_by'])}::uuid
where not exists (
select 1 from public.claim_edges
where from_claim = {sql_literal(row['from_claim'])}::uuid
and to_claim = {sql_literal(row['to_claim'])}::uuid
and edge_type = {sql_literal(row['edge_type'])}::edge_type);"""
)
checks.append(
f""" if exists (select 1 from public.claim_edges
where from_claim = {sql_literal(row['from_claim'])}::uuid
and to_claim = {sql_literal(row['to_claim'])}::uuid
and edge_type = {sql_literal(row['edge_type'])}::edge_type
and (weight is distinct from {sql_literal(row['weight'])}
or created_by is distinct from {sql_literal(row['created_by'])}::uuid)) then
raise exception 'approve_claim: edge row does not match strict apply payload';
end if;
if not exists (select 1 from public.claim_edges
where from_claim = {sql_literal(row['from_claim'])}::uuid
and to_claim = {sql_literal(row['to_claim'])}::uuid
and edge_type = {sql_literal(row['edge_type'])}::edge_type
and weight is not distinct from {sql_literal(row['weight'])}
and created_by is not distinct from {sql_literal(row['created_by'])}::uuid) then
raise exception 'approve_claim: edge row does not match strict apply payload';
end if;"""
)
canonical = "\n\n".join(statements)
ledger = _ledger_and_verify(
proposal_id, applied_by or SERVICE_AGENT_HANDLE, "\n".join(checks), approval
)
return _wrap_txn(canonical, ledger, _approval_guard_sql(proposal_id, approval))
def _wrap_txn(canonical_sql: str, ledger_sql: str, approval_guard_sql: str) -> str:
return (
"begin;\n"
"set local standard_conforming_strings = on;\n"
f"{approval_guard_sql}\n"
f"{canonical_sql}\n"
f"{ledger_sql}\n"
"commit;\n"
)
BUILDERS = { BUILDERS = {
"revise_strategy": build_revise_strategy_sql, "revise_strategy": build_revise_strategy_sql,
"add_edge": build_add_edge_sql, "add_edge": build_add_edge_sql,
"attach_evidence": build_attach_evidence_sql, "attach_evidence": build_attach_evidence_sql,
"approve_claim": build_approve_claim_sql,
} }
def build_apply_sql(proposal: Dict[str, Any], applied_by: Optional[str]) -> str: def build_apply_sql(proposal: dict[str, Any], applied_by: str | None) -> str:
ptype = proposal["proposal_type"] ptype = proposal["proposal_type"]
if ptype not in BUILDERS: if ptype not in BUILDERS:
raise ValueError(f"apply_proposal cannot apply proposal_type {ptype!r}") raise ValueError(f"apply_proposal cannot apply proposal_type {ptype!r}")
@ -312,13 +818,18 @@ def build_apply_sql(proposal: Dict[str, Any], applied_by: Optional[str]) -> str:
apply_payload = payload.get("apply_payload") apply_payload = payload.get("apply_payload")
if apply_payload is None: if apply_payload is None:
raise ValueError( raise ValueError(
"proposal payload has no 'apply_payload' (contract v1). " "proposal payload has no 'apply_payload' strict contract. "
"Run the normalizer to produce apply_payload before applying." "Run the normalizer to produce apply_payload before applying."
) )
return BUILDERS[ptype](apply_payload, proposal["id"], applied_by or SERVICE_AGENT_HANDLE) return BUILDERS[ptype](
apply_payload,
proposal["id"],
applied_by or SERVICE_AGENT_HANDLE,
proposal,
)
def assert_applyable(proposal: Dict[str, Any]) -> None: def assert_applyable(proposal: dict[str, Any]) -> None:
status = proposal.get("status") status = proposal.get("status")
if status == "applied": if status == "applied":
raise SystemExit(f"proposal {proposal['id']} is already applied (idempotent no-op)") raise SystemExit(f"proposal {proposal['id']} is already applied (idempotent no-op)")
@ -363,12 +874,16 @@ def run_psql(args: argparse.Namespace, sql: str, password: str) -> str:
return result.stdout return result.stdout
def load_proposal(args: argparse.Namespace, password: str) -> Dict[str, Any]: def load_proposal(args: argparse.Namespace, password: str) -> dict[str, Any]:
sql = f"""select jsonb_build_object( sql = f"""select jsonb_build_object(
'id', id::text, 'id', id::text,
'proposal_type', proposal_type, 'proposal_type', proposal_type,
'status', status, 'status', status,
'payload', payload)::text 'payload', payload,
'reviewed_by_handle', reviewed_by_handle,
'reviewed_by_agent_id', reviewed_by_agent_id::text,
'reviewed_at', reviewed_at::text,
'review_note', review_note)::text
from kb_stage.kb_proposals from kb_stage.kb_proposals
where id = {sql_literal(args.proposal_id)}::uuid;""" where id = {sql_literal(args.proposal_id)}::uuid;"""
out = run_psql(args, sql, password).strip() out = run_psql(args, sql, password).strip()
@ -377,7 +892,7 @@ def load_proposal(args: argparse.Namespace, password: str) -> Dict[str, Any]:
return json.loads(out) return json.loads(out)
def parse_args(argv: List[str]) -> argparse.Namespace: def parse_args(argv: list[str]) -> argparse.Namespace:
p = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) p = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
p.add_argument("proposal_id", help="UUID of the approved proposal to apply") p.add_argument("proposal_id", help="UUID of the approved proposal to apply")
p.add_argument( p.add_argument(
@ -395,7 +910,7 @@ def parse_args(argv: List[str]) -> argparse.Namespace:
return p.parse_args(argv) return p.parse_args(argv)
def main(argv: Optional[List[str]] = None) -> int: def main(argv: list[str] | None = None) -> int:
args = parse_args(sys.argv[1:] if argv is None else argv) args = parse_args(sys.argv[1:] if argv is None else argv)
if args.dry_run: if args.dry_run:

View file

@ -2,12 +2,12 @@
"""Event-driven worker that lands human-approved proposals into canonical state. """Event-driven worker that lands human-approved proposals into canonical state.
Stage 2-of-the-loop automation: Leo proposes -> HUMAN approves -> WORKER applies Stage 2-of-the-loop automation: Leo proposes -> HUMAN approves -> WORKER applies
-> renderer runs -> Leo reads the new self. -> a supported render hook may run -> Leo reads canonical state.
This is the "natural evolution" engine: it makes an approval in Telegram surface This is the "natural evolution" engine: it makes an approval in Telegram canonical
in Leo's identity without Leo ever applying its own work. It does NOT think, does without Leo ever applying its own work. It does NOT think, does NOT approve, and
NOT approve, and does NOT create proposals -- it only acts on proposals a human does NOT create proposals -- it only acts on proposals a human has already moved
has already moved to ``status='approved'``. to ``status='approved'``.
Governance boundary (why this is safe) Governance boundary (why this is safe)
-------------------------------------- --------------------------------------
@ -24,10 +24,10 @@ Safety posture
* Gated OFF by default. Without ``--enable`` (or ``KB_APPLY_WORKER_ENABLED=1``) the * Gated OFF by default. Without ``--enable`` (or ``KB_APPLY_WORKER_ENABLED=1``) the
worker only REPORTS what it would apply -- it performs no writes. Flip it on only worker only REPORTS what it would apply -- it performs no writes. Flip it on only
after the first apply has been proven by hand. after the first apply has been proven by hand.
* The render step is a configurable hook (``--render-cmd`` / ``KB_APPLY_RENDER_CMD``). * The optional render step is a configurable hook (``--render-cmd`` /
Until the SOUL renderer (PR2) is deployed the hook is simply absent and the worker ``KB_APPLY_RENDER_CMD``). Only ``revise_strategy`` currently has a proven
logs that render was skipped -- applying still works, Leo's canonical is correct, agent-owned render contract. Other apply types update canonical state without
and the rendered SOUL.md just lags until the renderer exists. invoking a generic agent render.
""" """
from __future__ import annotations from __future__ import annotations
@ -38,7 +38,7 @@ import os
import subprocess import subprocess
import sys import sys
from pathlib import Path from pathlib import Path
from typing import Any, Dict, List, Optional from typing import Any
HERE = Path(__file__).resolve().parent HERE = Path(__file__).resolve().parent
sys.path.insert(0, str(HERE)) sys.path.insert(0, str(HERE))
@ -47,23 +47,34 @@ import apply_proposal as ap # noqa: E402 (sibling module, reused verbatim)
# Types the worker is willing to auto-apply. Deliberately the same set the engine # Types the worker is willing to auto-apply. Deliberately the same set the engine
# supports; a proposal type outside this list is ignored (never applied). # supports; a proposal type outside this list is ignored (never applied).
WORKER_TYPES = ("revise_strategy", "add_edge", "attach_evidence") WORKER_TYPES = ap.APPLYABLE_TYPES
# Only a revise_strategy changes an agent's identity spine, so only it triggers a # Only strategy revisions have a proven agent-owned render hook. Canonical claim
# SOUL re-render. Evidence/edge applies change the graph but not the rendered self. # bundles and evidence/edge applies do not trigger a generic identity render.
RENDER_TYPES = ("revise_strategy",) RENDER_TYPES = ("revise_strategy",)
# --------------------------------------------------------------------------- # # --------------------------------------------------------------------------- #
# Pure helpers (unit-tested without a DB) # # Pure helpers (unit-tested without a DB) #
# --------------------------------------------------------------------------- # # --------------------------------------------------------------------------- #
def build_candidate_query(types: tuple = WORKER_TYPES, limit: int = 20) -> str: def build_candidate_query(
types: tuple = WORKER_TYPES,
limit: int = 20,
excluded_ids: tuple = (),
) -> str:
"""SELECT approved, applyable proposals that carry an apply_payload. """SELECT approved, applyable proposals that carry an apply_payload.
Filters on status='approved' at the DB level -- this is the structural Filters on status='approved' at the DB level -- this is the structural
guarantee that the worker cannot act on anything a human has not approved. guarantee that the worker cannot act on anything a human has not approved.
IDs at the failure ceiling are excluded before LIMIT so exhausted rows cannot
occupy every fetched slot and starve later eligible proposals.
""" """
type_list = ", ".join(ap.sql_literal(t) for t in types) type_list = ", ".join(ap.sql_literal(t) for t in types)
excluded = tuple(str(proposal_id) for proposal_id in excluded_ids)
exclusion_clause = ""
if excluded:
excluded_list = ", ".join(ap.sql_literal(proposal_id) for proposal_id in excluded)
exclusion_clause = f"\n and id::text not in ({excluded_list})"
return f"""select jsonb_build_object( return f"""select jsonb_build_object(
'id', id::text, 'id', id::text,
'proposal_type', proposal_type, 'proposal_type', proposal_type,
@ -71,14 +82,14 @@ def build_candidate_query(types: tuple = WORKER_TYPES, limit: int = 20) -> str:
from kb_stage.kb_proposals from kb_stage.kb_proposals
where status = 'approved' where status = 'approved'
and proposal_type in ({type_list}) and proposal_type in ({type_list})
and payload ? 'apply_payload' and payload ? 'apply_payload'{exclusion_clause}
order by created_at asc order by created_at asc
limit {int(limit)};""" limit {int(limit)};"""
def parse_candidates(psql_output: str) -> List[Dict[str, Any]]: def parse_candidates(psql_output: str) -> list[dict[str, Any]]:
"""Parse newline-delimited JSON rows from psql -At output.""" """Parse newline-delimited JSON rows from psql -At output."""
rows: List[Dict[str, Any]] = [] rows: list[dict[str, Any]] = []
for line in psql_output.splitlines(): for line in psql_output.splitlines():
line = line.strip() line = line.strip()
if line: if line:
@ -86,7 +97,7 @@ def parse_candidates(psql_output: str) -> List[Dict[str, Any]]:
return rows return rows
def build_render_command(render_cmd: Optional[str], agent_id: Optional[str]) -> Optional[List[str]]: def build_render_command(render_cmd: str | None, agent_id: str | None) -> list[str] | None:
"""Expand the render-hook template for one agent, or None if no hook configured. """Expand the render-hook template for one agent, or None if no hook configured.
``render_cmd`` is a shell-word template, e.g. ``python3 render_soul.py --agent-id {agent_id}``. ``render_cmd`` is a shell-word template, e.g. ``python3 render_soul.py --agent-id {agent_id}``.
@ -101,7 +112,7 @@ def build_render_command(render_cmd: Optional[str], agent_id: Optional[str]) ->
return [tok.format(agent_id=agent_id) for tok in shlex.split(render_cmd)] return [tok.format(agent_id=agent_id) for tok in shlex.split(render_cmd)]
def load_failure_state(path: Optional[str]) -> Dict[str, int]: def load_failure_state(path: str | None) -> dict[str, int]:
"""Load the persisted {proposal_id: consecutive_failure_count} map. """Load the persisted {proposal_id: consecutive_failure_count} map.
The worker runs oneshot per timer tick, so in-memory failure counts do not The worker runs oneshot per timer tick, so in-memory failure counts do not
@ -118,11 +129,11 @@ def load_failure_state(path: Optional[str]) -> Dict[str, int]:
try: try:
data = json.loads(p.read_text(encoding="utf-8")) data = json.loads(p.read_text(encoding="utf-8"))
return {str(k): int(v) for k, v in data.items()} if isinstance(data, dict) else {} return {str(k): int(v) for k, v in data.items()} if isinstance(data, dict) else {}
except Exception: # noqa: BLE001 -- a bad state file must never wedge the worker except Exception:
return {} return {}
def save_failure_state(path: Optional[str], state: Dict[str, int]) -> None: def save_failure_state(path: str | None, state: dict[str, int]) -> None:
if not path: if not path:
return return
p = Path(path) p = Path(path)
@ -131,15 +142,15 @@ def save_failure_state(path: Optional[str], state: Dict[str, int]) -> None:
def partition_candidates( def partition_candidates(
candidates: List[Dict[str, Any]], candidates: list[dict[str, Any]],
failure_counts: Dict[str, int], failure_counts: dict[str, int],
max_attempts: int, max_attempts: int,
max_per_tick: int, max_per_tick: int,
) -> Dict[str, List[Dict[str, Any]]]: ) -> dict[str, list[dict[str, Any]]]:
"""Split fetched candidates into what to apply now vs. skip. """Split fetched candidates into what to apply now vs. skip.
- ``poisoned``: already at/over the failure ceiling -> never re-attempted, - ``poisoned``: defensive post-fetch check for a candidate already at/over
surfaced loudly for the operator (needs a human fix, not another retry). the failure ceiling. Normal DB fetches exclude these IDs before LIMIT.
- ``to_apply``: the first ``max_per_tick`` non-poisoned candidates -> capped - ``to_apply``: the first ``max_per_tick`` non-poisoned candidates -> capped
so an enabled worker lands applies one-at-a-time and observably, rather so an enabled worker lands applies one-at-a-time and observably, rather
than draining the whole approved queue in a single unobserved tick. than draining the whole approved queue in a single unobserved tick.
@ -159,8 +170,12 @@ def _psql_args(args: argparse.Namespace) -> argparse.Namespace:
) )
def fetch_candidates(args: argparse.Namespace, password: str) -> List[Dict[str, Any]]: def fetch_candidates(
sql = build_candidate_query(limit=args.limit) args: argparse.Namespace,
password: str,
excluded_ids: tuple,
) -> list[dict[str, Any]]:
sql = build_candidate_query(limit=args.limit, excluded_ids=excluded_ids)
out = ap.run_psql(_psql_args(args), sql, password) out = ap.run_psql(_psql_args(args), sql, password)
return parse_candidates(out) return parse_candidates(out)
@ -181,7 +196,7 @@ def apply_one(args: argparse.Namespace, proposal_id: str) -> None:
) )
def render_one(args: argparse.Namespace, agent_id: Optional[str]) -> str: def render_one(args: argparse.Namespace, agent_id: str | None) -> str:
cmd = build_render_command(args.render_cmd, agent_id) cmd = build_render_command(args.render_cmd, agent_id)
if cmd is None: if cmd is None:
return "render skipped (no render-cmd configured; PR2 renderer not deployed)" return "render skipped (no render-cmd configured; PR2 renderer not deployed)"
@ -198,13 +213,26 @@ def render_one(args: argparse.Namespace, agent_id: Optional[str]) -> str:
# --------------------------------------------------------------------------- # # --------------------------------------------------------------------------- #
def run(args: argparse.Namespace) -> int: def run(args: argparse.Namespace) -> int:
password = ap.load_password(args.secrets_file) password = ap.load_password(args.secrets_file)
candidates = fetch_candidates(args, password) failure_counts = load_failure_state(args.failure_state_file)
exhausted_ids = tuple(
proposal_id
for proposal_id, count in failure_counts.items()
if count >= args.max_attempts
)
candidates = fetch_candidates(args, password, exhausted_ids)
for proposal_id in exhausted_ids:
print(
f"EXCLUDE failure-ceiling ID {proposal_id}: failed "
f"{failure_counts[proposal_id]}x >= max-attempts {args.max_attempts}; "
"omitted from candidate SQL before LIMIT",
file=sys.stderr,
)
if not candidates: if not candidates:
print("no approved+applyable proposals; nothing to do") print("no approved+applyable proposals; nothing to do")
return 0 return 0
failure_counts = load_failure_state(args.failure_state_file)
split = partition_candidates(candidates, failure_counts, args.max_attempts, args.max_per_tick) split = partition_candidates(candidates, failure_counts, args.max_attempts, args.max_per_tick)
for c in split["poisoned"]: for c in split["poisoned"]:
@ -235,7 +263,7 @@ def run(args: argparse.Namespace) -> int:
print(f"applied {pid} ({ptype})") print(f"applied {pid} ({ptype})")
if ptype in RENDER_TYPES: if ptype in RENDER_TYPES:
print(" " + render_one(args, agent_id)) print(" " + render_one(args, agent_id))
except Exception as exc: # noqa: BLE001 -- one bad proposal must not stop the batch except Exception as exc:
failures += 1 failures += 1
# Leave the proposal at 'approved' so a fixed one reapplies next tick # Leave the proposal at 'approved' so a fixed one reapplies next tick
# (the rowcount=1 guard makes reapply safe), but bump its failure count # (the rowcount=1 guard makes reapply safe), but bump its failure count
@ -249,7 +277,7 @@ def run(args: argparse.Namespace) -> int:
return 1 if failures else 0 return 1 if failures else 0
def parse_args(argv: List[str]) -> argparse.Namespace: def parse_args(argv: list[str]) -> argparse.Namespace:
p = argparse.ArgumentParser( p = argparse.ArgumentParser(
description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter
) )
@ -295,7 +323,7 @@ def parse_args(argv: List[str]) -> argparse.Namespace:
return p.parse_args(argv) return p.parse_args(argv)
def main(argv: Optional[List[str]] = None) -> int: def main(argv: list[str] | None = None) -> int:
return run(parse_args(sys.argv[1:] if argv is None else argv)) return run(parse_args(sys.argv[1:] if argv is None else argv))

View file

@ -8,13 +8,16 @@ pending, applyable proposal from ``pending_review`` to ``approved`` after a
human/operator supplies a reviewer handle and verbatim review note. The apply human/operator supplies a reviewer handle and verbatim review note. The apply
step remains separate and is performed by ``scripts/apply_proposal.py``. step remains separate and is performed by ``scripts/apply_proposal.py``.
The guardrails intentionally mirror the apply worker: The guardrails intentionally mirror the apply worker while keeping review and
apply credentials separate:
* proposal must be ``pending_review``; * proposal must be ``pending_review``;
* proposal_type must be one of the worker-supported applyable types; * proposal_type must be one of the worker-supported applyable types;
* payload must contain a strict ``apply_payload`` contract; * payload must contain a strict ``apply_payload`` contract;
* the strict payload must build valid apply SQL before approval is recorded; * the strict payload must build valid apply SQL before approval is recorded;
* the UPDATE must affect exactly one row. * the reviewer connects as ``kb_review`` and can only execute the constrained
``kb_stage.approve_strict_proposal`` function;
* ``kb_review`` has no direct proposal UPDATE or canonical write grant.
``--dry-run`` validates the proposal and prints the SQL without writing. ``--dry-run`` validates the proposal and prints the SQL without writing.
""" """
@ -32,6 +35,25 @@ sys.path.insert(0, str(HERE))
import apply_proposal as ap # noqa: E402 import apply_proposal as ap # noqa: E402
DEFAULT_REVIEW_SECRETS_FILE = "/home/teleo/.hermes/profiles/leoclean/secrets/kb-review.env"
DEFAULT_REVIEW_ROLE = "kb_review"
def load_review_password(secrets_file: str) -> str:
path = Path(secrets_file)
if not path.is_file():
raise SystemExit(f"review secrets file not found: {secrets_file}")
for line in path.read_text(encoding="utf-8").splitlines():
line = line.strip()
if not line or line.startswith("#") or "=" not in line:
continue
key, _, value = line.partition("=")
if key.strip() in ("KB_REVIEW_PASSWORD", "KB_REVIEW_DB_PASSWORD", "PGPASSWORD"):
return value.strip().strip('"').strip("'")
raise SystemExit(
f"no KB_REVIEW_PASSWORD/KB_REVIEW_DB_PASSWORD/PGPASSWORD entry in {secrets_file}"
)
def load_review_note(args: argparse.Namespace) -> str: def load_review_note(args: argparse.Namespace) -> str:
if args.review_note and args.review_note_file: if args.review_note and args.review_note_file:
@ -72,40 +94,41 @@ def assert_approvable(proposal: dict[str, Any], reviewed_by: str, review_note: s
f"proposal {proposal_id} has no payload.apply_payload; run/repair normalization before approval" f"proposal {proposal_id} has no payload.apply_payload; run/repair normalization before approval"
) )
# Validate the strict contract before recording approval. This is pure SQL # Validate the exact strict contract before recording approval. The builder
# building: it does not execute canonical writes. # requires review metadata, so validate against a synthetic snapshot that
ap.build_apply_sql(proposal, ap.SERVICE_AGENT_HANDLE) # mirrors what the DB function will stamp. This builds SQL only.
validation_snapshot = dict(proposal)
validation_snapshot.update(
{
"status": "approved",
"reviewed_by_handle": reviewed_by.strip(),
"reviewed_by_agent_id": None,
"reviewed_at": "2000-01-01T00:00:00+00:00",
"review_note": review_note.strip(),
}
)
ap.build_apply_sql(validation_snapshot, ap.SERVICE_AGENT_HANDLE)
def build_approve_sql(proposal_id: str, reviewed_by: str, review_note: str) -> str: def build_approve_sql(
allowed_types_sql = ", ".join(ap.sql_literal(t) for t in ap.APPLYABLE_TYPES) proposal: dict[str, Any], reviewed_by: str, review_note: str
) -> str:
proposal_id = str(proposal["id"])
proposal_type = str(proposal["proposal_type"])
payload = proposal.get("payload")
if not isinstance(payload, dict):
raise ValueError("proposal payload must be an object")
payload_sql = ap.sql_literal(json.dumps(payload, sort_keys=True)) + "::jsonb"
return f""" return f"""
begin; begin;
with reviewed as ( set local standard_conforming_strings = on;
update kb_stage.kb_proposals select jsonb_build_array(kb_stage.approve_strict_proposal(
set status = 'approved', {ap.sql_literal(proposal_id)}::uuid,
reviewed_by_handle = {ap.sql_literal(reviewed_by)}, {ap.sql_literal(proposal_type)},
reviewed_at = now(), {payload_sql},
review_note = {ap.sql_literal(review_note)}, {ap.sql_literal(reviewed_by)},
updated_at = now() {ap.sql_literal(review_note)}
where id = {ap.sql_literal(proposal_id)}::uuid ))::text;
and status = 'pending_review'
and proposal_type in ({allowed_types_sql})
and payload ? 'apply_payload'
returning id, proposal_type, status, reviewed_by_handle, reviewed_at, review_note
)
select coalesce(
jsonb_agg(jsonb_build_object(
'id', id::text,
'proposal_type', proposal_type,
'status', status,
'reviewed_by_handle', reviewed_by_handle,
'reviewed_at', reviewed_at::text,
'review_note', review_note
)),
'[]'::jsonb
)::text
from reviewed;
commit; commit;
""".lstrip() """.lstrip()
@ -129,11 +152,11 @@ def parse_args(argv: list[str]) -> argparse.Namespace:
note.add_argument("--review-note", help="verbatim review note to record") note.add_argument("--review-note", help="verbatim review note to record")
note.add_argument("--review-note-file", help="path containing the verbatim review note") note.add_argument("--review-note-file", help="path containing the verbatim review note")
parser.add_argument("--dry-run", action="store_true", help="print the SQL, do not execute the approval") parser.add_argument("--dry-run", action="store_true", help="print the SQL, do not execute the approval")
parser.add_argument("--secrets-file", default=ap.DEFAULT_SECRETS_FILE) parser.add_argument("--secrets-file", default=DEFAULT_REVIEW_SECRETS_FILE)
parser.add_argument("--container", default=ap.DEFAULT_CONTAINER) parser.add_argument("--container", default=ap.DEFAULT_CONTAINER)
parser.add_argument("--db", default=ap.DEFAULT_DB) parser.add_argument("--db", default=ap.DEFAULT_DB)
parser.add_argument("--host", default=ap.DEFAULT_HOST) parser.add_argument("--host", default=ap.DEFAULT_HOST)
parser.add_argument("--role", default=ap.DEFAULT_ROLE) parser.add_argument("--role", default=DEFAULT_REVIEW_ROLE)
return parser.parse_args(argv) return parser.parse_args(argv)
@ -141,10 +164,10 @@ def main(argv: list[str] | None = None) -> int:
args = parse_args(sys.argv[1:] if argv is None else argv) args = parse_args(sys.argv[1:] if argv is None else argv)
review_note = load_review_note(args) review_note = load_review_note(args)
password = ap.load_password(args.secrets_file) password = load_review_password(args.secrets_file)
proposal = ap.load_proposal(args, password) proposal = ap.load_proposal(args, password)
assert_approvable(proposal, args.reviewed_by, review_note) assert_approvable(proposal, args.reviewed_by, review_note)
sql = build_approve_sql(args.proposal_id, args.reviewed_by.strip(), review_note) sql = build_approve_sql(proposal, args.reviewed_by.strip(), review_note)
if args.dry_run: if args.dry_run:
print(sql) print(sql)

File diff suppressed because it is too large Load diff

View file

@ -3,8 +3,9 @@
Leo and reviewers can approve rich proposal intent before it is mechanically Leo and reviewers can approve rich proposal intent before it is mechanically
safe to apply. The apply worker intentionally accepts only strict contracts: safe to apply. The apply worker intentionally accepts only strict contracts:
``add_edge``, ``attach_evidence``, and ``revise_strategy`` proposals that carry ``add_edge``, ``attach_evidence``, ``revise_strategy``, and atomic
``payload.apply_payload`` with canonical IDs. ``approve_claim`` bundles that carry ``payload.apply_payload`` with canonical
IDs and reviewed row content.
This helper is read-only by default. It inspects a freeform proposal and reports: This helper is read-only by default. It inspects a freeform proposal and reports:
@ -21,6 +22,7 @@ import hashlib
import json import json
import re import re
import sys import sys
from collections import Counter
from pathlib import Path from pathlib import Path
from typing import Any from typing import Any
@ -29,10 +31,16 @@ sys.path.insert(0, str(HERE))
import apply_proposal as ap # noqa: E402 import apply_proposal as ap # noqa: E402
import kb_proposal_review_packet as review_packet # noqa: E402 import kb_proposal_review_packet as review_packet # noqa: E402
import kb_rich_proposal_creation_plan as rich_plan # noqa: E402
UUID_RE = re.compile( UUID_RE = re.compile(
r"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$" r"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"
) )
RICH_CANDIDATE_COLLECTIONS = (
("claim_candidates", "claim_key", "claim"),
("proposed_claims", "claim_key", "claim"),
("source_candidates", "source_key", "source"),
)
def _payload_dict(payload: dict[str, Any], key: str) -> dict[str, Any]: def _payload_dict(payload: dict[str, Any], key: str) -> dict[str, Any]:
@ -94,6 +102,190 @@ def _blocked(kind: str, reason: str, missing: list[str], fragment: Any) -> dict[
} }
def _candidate_inventory(payload: dict[str, Any]) -> dict[str, Any]:
"""Validate rich candidate inputs before the planner can skip or dedupe them."""
inventory: dict[str, Any] = {
"claim_items": [],
"source_items": [],
"collection_counts": {},
"blocked_fragments": [],
}
for collection, key_field, kind in RICH_CANDIDATE_COLLECTIONS:
if collection not in payload:
inventory["collection_counts"][collection] = 0
continue
candidates = payload.get(collection)
if not isinstance(candidates, list):
block = _blocked(
f"{kind}_candidate",
f"payload.{collection} must be a list of objects",
[key_field],
candidates,
)
block["location"] = f"payload.{collection}"
inventory["blocked_fragments"].append(block)
inventory["collection_counts"][collection] = None
continue
inventory["collection_counts"][collection] = len(candidates)
for index, candidate in enumerate(candidates):
location = f"payload.{collection}[{index}]"
if not isinstance(candidate, dict):
block = _blocked(
f"{kind}_candidate",
f"{location} must be an object",
[key_field],
candidate,
)
block["location"] = location
inventory["blocked_fragments"].append(block)
continue
raw_key = candidate.get(key_field)
if not isinstance(raw_key, str) or not raw_key.strip():
block = _blocked(
f"{kind}_candidate",
f"{location} is missing a nonempty string {key_field}",
[key_field],
candidate,
)
block["location"] = location
inventory["blocked_fragments"].append(block)
continue
inventory[f"{kind}_items"].append(
{
"collection": collection,
"index": index,
"key": raw_key.strip(),
"fragment": candidate,
}
)
for kind, key_field in (("claim", "claim_key"), ("source", "source_key")):
by_key: dict[str, list[dict[str, Any]]] = {}
for item in inventory[f"{kind}_items"]:
by_key.setdefault(item["key"], []).append(item)
for candidate_key, occurrences in by_key.items():
if len(occurrences) == 1:
continue
locations = [f"payload.{item['collection']}[{item['index']}]" for item in occurrences]
block = _blocked(
f"{kind}_candidate",
f"duplicate {key_field} {candidate_key!r} at {', '.join(locations)}",
[],
[item["fragment"] for item in occurrences],
)
block["locations"] = locations
inventory["blocked_fragments"].append(block)
return inventory
def _planned_row_accounting(
plan: dict[str, Any], inventory: dict[str, Any]
) -> tuple[dict[str, Any], list[str]]:
"""Reconcile every validated input candidate to exactly one planned row."""
rows = plan.get("planned_rows") if isinstance(plan.get("planned_rows"), dict) else {}
generated = plan.get("generated_ids") if isinstance(plan.get("generated_ids"), dict) else {}
claim_id_map = generated.get("claims") if isinstance(generated.get("claims"), dict) else {}
source_id_map = generated.get("sources") if isinstance(generated.get("sources"), dict) else {}
body_source_id_map = (
generated.get("claim_body_sources") if isinstance(generated.get("claim_body_sources"), dict) else {}
)
claim_rows = rows.get("claims") if isinstance(rows.get("claims"), list) else []
source_rows = rows.get("sources") if isinstance(rows.get("sources"), list) else []
claim_keys = [item["key"] for item in inventory["claim_items"]]
source_keys = [item["key"] for item in inventory["source_items"]]
actual_claim_ids = [row.get("id") for row in claim_rows if isinstance(row, dict)]
actual_source_ids = [row.get("id") for row in source_rows if isinstance(row, dict)]
expected_claim_ids = [claim_id_map.get(key) for key in claim_keys]
expected_candidate_source_ids = [source_id_map.get(key) for key in source_keys]
expected_body_source_ids = [body_source_id_map.get(key) for key in claim_keys]
candidate_source_id_set = {value for value in expected_candidate_source_ids if value is not None}
body_source_id_set = {value for value in expected_body_source_ids if value is not None}
planned_candidate_source_ids = [value for value in actual_source_ids if value in candidate_source_id_set]
planned_body_source_ids = [value for value in actual_source_ids if value in body_source_id_set]
accounting = {
"input_collections": inventory["collection_counts"],
"claim_candidates": {
"input_count": len(claim_keys),
"input_keys": claim_keys,
"planned_row_count": len(claim_rows),
"planned_row_ids": actual_claim_ids,
},
"source_candidates": {
"input_count": len(source_keys),
"input_keys": source_keys,
"expected_row_ids": expected_candidate_source_ids,
"planned_row_count": len(planned_candidate_source_ids),
"planned_row_ids": planned_candidate_source_ids,
},
"generated_claim_body_sources": {
"expected_count": len(claim_keys),
"expected_row_ids": expected_body_source_ids,
"planned_row_count": len(planned_body_source_ids),
"planned_row_ids": planned_body_source_ids,
},
"total_planned_source_rows": len(source_rows),
}
errors: list[str] = []
if set(claim_id_map) != set(claim_keys):
errors.append("planned claim key set does not exactly match validated claim candidates")
if set(source_id_map) != set(source_keys):
errors.append("planned source key set does not exactly match validated source candidates")
if set(body_source_id_map) != set(claim_keys):
errors.append("planned claim-body source key set does not exactly match validated claim candidates")
if any(value is None for value in expected_claim_ids):
errors.append("one or more validated claim candidates has no generated claim id")
if any(value is None for value in expected_candidate_source_ids):
errors.append("one or more validated source candidates has no generated source id")
if any(value is None for value in expected_body_source_ids):
errors.append("one or more validated claim candidates has no generated claim-body source id")
if Counter(actual_claim_ids) != Counter(expected_claim_ids):
errors.append("planned claim rows do not account for every validated claim candidate exactly once")
if Counter(actual_source_ids) != Counter(expected_candidate_source_ids + expected_body_source_ids):
errors.append(
"planned source rows do not account for every validated source candidate and generated claim body exactly once"
)
return accounting, errors
def _normalization_manifest(plan: dict[str, Any], accounting: dict[str, Any]) -> dict[str, Any]:
rows = plan["planned_rows"]
body_source_ids = set(plan["generated_ids"]["claim_body_sources"].values())
claim_type_mappings: list[dict[str, Any]] = []
source_type_mappings: list[dict[str, Any]] = []
for row in rows["claims"]:
mapping = row.get("type_normalization")
if mapping:
claim_type_mappings.append(
{"claim_key": row.get("claim_key"), "claim_id": row.get("id"), **dict(mapping)}
)
for row in rows["sources"]:
mapping = row.get("source_type_normalization")
if mapping:
source_type_mappings.append(
{
"source_key": row.get("source_key"),
"source_id": row.get("id"),
"generated_claim_body_source": row.get("id") in body_source_ids,
**dict(mapping),
}
)
return {
"candidate_accounting": accounting,
"claim_type_mappings": claim_type_mappings,
"source_type_mappings": source_type_mappings,
}
def _edge_candidate(fragment: dict[str, Any], default_from_claim: str | None = None) -> dict[str, Any] | None: def _edge_candidate(fragment: dict[str, Any], default_from_claim: str | None = None) -> dict[str, Any] | None:
edge_type = fragment.get("edge_type") edge_type = fragment.get("edge_type")
from_claim = _first_uuid(fragment, ("from_claim", "from_claim_id", "from_claim_uuid")) or default_from_claim from_claim = _first_uuid(fragment, ("from_claim", "from_claim_id", "from_claim_uuid")) or default_from_claim
@ -137,6 +329,88 @@ def _strict_child(kind: str, apply_payload: dict[str, Any], source_fragment: Any
} }
def _strict_approve_claim_child(
proposal: dict[str, Any], plan: dict[str, Any], normalization_manifest: dict[str, Any]
) -> dict[str, Any] | None:
"""Convert a fully resolved rich graph into one atomic strict child."""
rows = plan["planned_rows"]
if not rows["claims"]:
return None
if plan["blocked_fragments"] or plan["unresolved_design_decisions"]:
return None
agent_id = proposal.get("proposed_by_agent_id")
apply_payload = {
"contract_version": 2,
"source_proposal_id": proposal.get("id"),
"agent_id": agent_id,
"claims": [
{
key: row.get(key)
for key in ("id", "type", "text", "status", "confidence", "tags", "created_by")
}
for row in rows["claims"]
],
"sources": [
{
key: row.get(key)
for key in ("id", "source_type", "url", "storage_path", "excerpt", "hash", "created_by")
}
for row in rows["sources"]
],
"evidence": [
{
"claim_id": row.get("claim_id"),
"source_id": row.get("source_id"),
"role": row.get("role"),
"weight": row.get("weight"),
"created_by": agent_id,
}
for row in rows["claim_evidence"]
],
"edges": [
{
"from_claim": row.get("from_claim"),
"to_claim": row.get("to_claim"),
"edge_type": row.get("edge_type"),
"weight": row.get("weight"),
"created_by": agent_id,
}
for row in rows["claim_edges"]
],
"reasoning_tools": [
{
key: row.get(key)
for key in ("id", "agent_id", "name", "description", "category")
}
for row in rows["reasoning_tools"]
],
"normalization_manifest": normalization_manifest,
}
return _strict_child("approve_claim", apply_payload, proposal.get("payload") or {})
def _rich_plan_blockers(plan: dict[str, Any]) -> list[dict[str, Any]]:
blocked: list[dict[str, Any]] = []
for fragment in plan.get("blocked_fragments") or []:
reasons = fragment.get("reasons") if isinstance(fragment, dict) else None
reason = "; ".join(str(item) for item in reasons) if isinstance(reasons, list) else "rich fragment is blocked"
source_fragment = fragment.get("fragment") if isinstance(fragment, dict) else fragment
kind = fragment.get("kind", "rich_fragment") if isinstance(fragment, dict) else "rich_fragment"
blocked.append(_blocked(kind, reason, [], source_fragment))
for decision in plan.get("unresolved_design_decisions") or []:
if isinstance(decision, dict):
blocked.append(
_blocked(
"design_decision",
str(decision.get("reason") or "rich proposal has an unresolved design decision"),
[str(decision.get("decision") or "reviewed design decision")],
decision,
)
)
return blocked
def _normalize_edges(payload: dict[str, Any]) -> tuple[list[dict[str, Any]], list[dict[str, Any]]]: def _normalize_edges(payload: dict[str, Any]) -> tuple[list[dict[str, Any]], list[dict[str, Any]]]:
strict: list[dict[str, Any]] = [] strict: list[dict[str, Any]] = []
blocked: list[dict[str, Any]] = [] blocked: list[dict[str, Any]] = []
@ -260,11 +534,27 @@ def _dedupe_children(children: list[dict[str, Any]]) -> list[dict[str, Any]]:
return out return out
def normalize_proposal(proposal: dict[str, Any]) -> dict[str, Any]: def normalize_proposal(
proposal: dict[str, Any], mappings: dict[str, Any] | None = None
) -> dict[str, Any]:
payload = proposal.get("payload") or {} payload = proposal.get("payload") or {}
if not isinstance(payload, dict): if not isinstance(payload, dict):
payload = {} payload = {}
inventory = _candidate_inventory(payload)
if inventory["blocked_fragments"]:
return {
"proposal_id": proposal.get("id"),
"normalization_state": "blocked_lossy_rich_normalization",
"strict_child_proposals": [],
"blocked_fragments": inventory["blocked_fragments"],
"blocked_count": len(inventory["blocked_fragments"]),
"strict_child_count": 0,
"next_normalization_action": (
"Repair every malformed, missing-key, or duplicate rich candidate, then rerun normalization."
),
}
if payload.get("apply_payload") and proposal.get("proposal_type") in ap.APPLYABLE_TYPES: if payload.get("apply_payload") and proposal.get("proposal_type") in ap.APPLYABLE_TYPES:
return { return {
"proposal_id": proposal.get("id"), "proposal_id": proposal.get("id"),
@ -276,6 +566,75 @@ def normalize_proposal(proposal: dict[str, Any]) -> dict[str, Any]:
"next_normalization_action": "Proposal already carries a strict apply_payload; use dry-run/apply review.", "next_normalization_action": "Proposal already carries a strict apply_payload; use dry-run/apply review.",
} }
has_rich_candidates = bool(inventory["claim_items"] or inventory["source_items"])
if has_rich_candidates:
plan = rich_plan.build_creation_plan(proposal, mappings)
accounting, accounting_errors = _planned_row_accounting(plan, inventory)
if not inventory["claim_items"]:
accounting_errors.append(
"rich source candidates cannot be emitted by approve_claim without at least one claim candidate"
)
if accounting_errors:
accounting_blocks = [
{
"kind": "candidate_accounting",
"reason": reason,
"missing": [],
"candidate_accounting": accounting,
"fragment": {"fingerprint": _fingerprint(accounting)},
}
for reason in accounting_errors
]
return {
"proposal_id": proposal.get("id"),
"normalization_state": "blocked_lossy_rich_normalization",
"strict_child_proposals": [],
"blocked_fragments": accounting_blocks,
"blocked_count": len(accounting_blocks),
"strict_child_count": 0,
"next_normalization_action": (
"Repair rich planner row accounting so every candidate maps to exactly one planned row, then rerun."
),
}
manifest = _normalization_manifest(plan, accounting)
approve_claim_child = _strict_approve_claim_child(proposal, plan, manifest)
if approve_claim_child:
return {
"proposal_id": proposal.get("id"),
"normalization_state": "strict_children_ready",
"strict_child_proposals": [approve_claim_child],
"blocked_fragments": [],
"blocked_count": 0,
"strict_child_count": 1,
"next_normalization_action": (
"Review and stage the atomic approve_claim child, then use dry-run/apply review."
),
}
blocked_fragments = _rich_plan_blockers(plan)
if not blocked_fragments:
blocked_fragments = [
{
"kind": "rich_plan",
"reason": "rich candidates were accounted for but no atomic strict child was produced",
"missing": [],
"candidate_accounting": accounting,
"fragment": {"fingerprint": _fingerprint(accounting)},
}
]
return {
"proposal_id": proposal.get("id"),
"normalization_state": "blocked_rich_normalization",
"strict_child_proposals": [],
"blocked_fragments": blocked_fragments,
"blocked_count": len(blocked_fragments),
"strict_child_count": 0,
"next_normalization_action": (
"Resolve every blocked rich fragment or design decision, then rerun the atomic normalization."
),
}
strict_children: list[dict[str, Any]] = [] strict_children: list[dict[str, Any]] = []
blocked_fragments: list[dict[str, Any]] = [] blocked_fragments: list[dict[str, Any]] = []
for strict, blocked in ( for strict, blocked in (
@ -325,19 +684,26 @@ def parse_args(argv: list[str]) -> argparse.Namespace:
parser.add_argument("--proposal-id", help="load one live proposal from Postgres") parser.add_argument("--proposal-id", help="load one live proposal from Postgres")
parser.add_argument("--status", default="approved", help="load live proposals by status") parser.add_argument("--status", default="approved", help="load live proposals by status")
parser.add_argument("--limit", type=int, default=20) parser.add_argument("--limit", type=int, default=20)
parser.add_argument("--mapping-json", help="optional reviewed mapping overlay for rich claim graphs")
parser.add_argument("--secrets-file", default=ap.DEFAULT_SECRETS_FILE) parser.add_argument("--secrets-file", default=ap.DEFAULT_SECRETS_FILE)
parser.add_argument("--container", default=ap.DEFAULT_CONTAINER) parser.add_argument("--container", default=ap.DEFAULT_CONTAINER)
parser.add_argument("--db", default=ap.DEFAULT_DB) parser.add_argument("--db", default=ap.DEFAULT_DB)
parser.add_argument("--host", default=ap.DEFAULT_HOST) parser.add_argument("--host", default=ap.DEFAULT_HOST)
parser.add_argument("--role", default=ap.DEFAULT_ROLE) parser.add_argument("--role", default=ap.DEFAULT_ROLE)
parser.add_argument("--output", type=Path, help="optional JSON output path")
return parser.parse_args(argv) return parser.parse_args(argv)
def main(argv: list[str] | None = None) -> int: def main(argv: list[str] | None = None) -> int:
args = parse_args(sys.argv[1:] if argv is None else argv) args = parse_args(sys.argv[1:] if argv is None else argv)
proposals = load_from_input(args.input_json) if args.input_json else load_from_db(args) proposals = load_from_input(args.input_json) if args.input_json else load_from_db(args)
previews = [normalize_proposal(proposal) for proposal in proposals] mappings = json.loads(Path(args.mapping_json).read_text(encoding="utf-8")) if args.mapping_json else None
print(json.dumps({"normalization_previews": previews}, indent=2, sort_keys=True)) previews = [normalize_proposal(proposal, mappings) for proposal in proposals]
rendered = json.dumps({"normalization_previews": previews}, indent=2, sort_keys=True) + "\n"
if args.output:
args.output.parent.mkdir(parents=True, exist_ok=True)
args.output.write_text(rendered, encoding="utf-8")
print(rendered, end="")
return 0 return 0

View file

@ -16,10 +16,11 @@ import os
import shutil import shutil
import subprocess import subprocess
import tempfile import tempfile
from collections.abc import Callable
from dataclasses import dataclass from dataclasses import dataclass
from datetime import datetime, timezone from datetime import datetime, timezone
from pathlib import Path from pathlib import Path
from typing import Any, Callable from typing import Any
REPORT_DIR = Path("docs/reports/leo-working-state-20260709") REPORT_DIR = Path("docs/reports/leo-working-state-20260709")
DEFAULT_OUT = REPORT_DIR / "gcp-db-parity-current-probe-current.json" DEFAULT_OUT = REPORT_DIR / "gcp-db-parity-current-probe-current.json"
@ -155,14 +156,49 @@ def accounts_from_auth_list(auth_list: dict[str, Any], fallback_account: str | N
return accounts return accounts
def flatten_command_results(results: list[dict[str, Any]]) -> list[dict[str, Any]]:
flattened: list[dict[str, Any]] = []
for result in results:
if "stderr_excerpt" in result:
flattened.append(result)
continue
for value in result.values():
if isinstance(value, dict):
flattened.extend(flatten_command_results([value]))
elif isinstance(value, list):
flattened.extend(flatten_command_results([item for item in value if isinstance(item, dict)]))
return flattened
def has_network_dns_error(results: list[dict[str, Any]]) -> bool: def has_network_dns_error(results: list[dict[str, Any]]) -> bool:
needle_set = ("Failed to resolve", "NameResolutionError", "nodename nor servname", "oauth2.googleapis.com") needle_set = (
return any(any(needle in item.get("stderr_excerpt", "") for needle in needle_set) for item in results) "failed to resolve",
"nameresolutionerror",
"nodename nor servname",
"name or service not known",
"temporary failure in name resolution",
"could not resolve host",
)
return any(
any(needle in item.get("stderr_excerpt", "").lower() for needle in needle_set)
for item in flatten_command_results(results)
)
def has_reauth_error(results: list[dict[str, Any]]) -> bool: def has_reauth_error(results: list[dict[str, Any]]) -> bool:
needle_set = ("Reauthentication failed", "cannot prompt during non-interactive execution", "gcloud auth login") needle_set = ("reauthentication failed", "cannot prompt during non-interactive execution", "gcloud auth login")
return any(any(needle in item.get("stderr_excerpt", "") for needle in needle_set) for item in results) return any(
any(needle in item.get("stderr_excerpt", "").lower() for needle in needle_set)
for item in flatten_command_results(results)
)
def classify_failure(results: list[dict[str, Any]]) -> str | None:
if has_network_dns_error(results):
return "dns_unavailable"
if has_reauth_error(results):
return "reauth_required"
return None
def summarize_project(project_result: dict[str, Any]) -> dict[str, Any] | None: def summarize_project(project_result: dict[str, Any]) -> dict[str, Any] | None:
@ -227,7 +263,7 @@ def status_from_results(
command_results.append(instance_readback) command_results.append(instance_readback)
if sql_readback is not None: if sql_readback is not None:
command_results.append(sql_readback) command_results.append(sql_readback)
if has_network_dns_error(command_results): if classify_failure(command_results) == "dns_unavailable":
return "blocked_on_network_dns_before_auth_refresh" return "blocked_on_network_dns_before_auth_refresh"
if selected_account != expected_account: if selected_account != expected_account:
return "blocked_on_wrong_active_account" return "blocked_on_wrong_active_account"
@ -235,7 +271,8 @@ def status_from_results(
return "blocked_on_wrong_active_project" return "blocked_on_wrong_active_project"
selected_token = next((item for item in token_checks if item["account"] == selected_account), None) selected_token = next((item for item in token_checks if item["account"] == selected_account), None)
if selected_token and not selected_token["token_refresh"]["ok"]: if selected_token and not selected_token["token_refresh"]["ok"]:
return "blocked_on_gcloud_reauth" if has_reauth_error([selected_token["token_refresh"]]) else "blocked_on_token_refresh" classification = classify_failure([selected_token])
return "blocked_on_gcloud_reauth" if classification == "reauth_required" else "blocked_on_token_refresh"
if not project_readback["ok"]: if not project_readback["ok"]:
return "blocked_on_project_access" return "blocked_on_project_access"
if instance_readback is not None and not instance_readback["ok"]: if instance_readback is not None and not instance_readback["ok"]:

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,328 @@
#!/usr/bin/env bash
set -euo pipefail
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
source_container="${SOURCE_CONTAINER:-teleo-pg}"
source_db="${SOURCE_DB:-teleo}"
service="${LEO_SERVICE:-leoclean-gateway.service}"
image="${POSTGRES_CANARY_IMAGE:-postgres:16}"
normalization_json=""
output="${repo_root}/docs/reports/leo-working-state-20260709/approve-claim-clone-canary-current.json"
while [[ $# -gt 0 ]]; do
case "$1" in
--normalization-json)
normalization_json="$2"
shift 2
;;
--output)
output="$2"
shift 2
;;
--source-container)
source_container="$2"
shift 2
;;
--source-db)
source_db="$2"
shift 2
;;
--service)
service="$2"
shift 2
;;
*)
echo "unknown argument: $1" >&2
exit 2
;;
esac
done
container="working-leo-approve-claim-$RANDOM-$$"
workdir="/tmp/${container}"
apply_password="isolated-apply-${RANDOM}-${RANDOM}"
review_password="isolated-review-${RANDOM}-${RANDOM}"
cleanup() {
docker rm -f "$container" >/dev/null 2>&1 || true
rm -rf "$workdir"
}
trap cleanup EXIT
source_counts() {
docker exec "$source_container" psql -U postgres -d "$source_db" -Atq \
-v ON_ERROR_STOP=1 -c "begin transaction read only;
select jsonb_build_object(
'public.claims',(select count(*) from public.claims),
'public.sources',(select count(*) from public.sources),
'public.claim_evidence',(select count(*) from public.claim_evidence),
'public.claim_edges',(select count(*) from public.claim_edges),
'public.reasoning_tools',(select count(*) from public.reasoning_tools),
'kb_stage.kb_proposals',(select count(*) from kb_stage.kb_proposals)
)::text;
rollback;"
}
service_state() {
systemctl show "$service" --no-pager \
-p ActiveState -p SubState -p MainPID -p NRestarts -p ExecMainStartTimestamp
}
mkdir -p "$workdir" "$(dirname "$output")"
source_counts_before="$(source_counts)"
service_before="$(service_state)"
docker run -d --name "$container" \
-e POSTGRES_PASSWORD="isolated-postgres-${RANDOM}-${RANDOM}" \
-e POSTGRES_DB=teleo \
"$image" >/dev/null
for _ in $(seq 1 40); do
if docker exec "$container" pg_isready -U postgres -d teleo >/dev/null 2>&1; then
break
fi
sleep 0.25
done
docker exec "$container" pg_isready -U postgres -d teleo >/dev/null
docker exec "$source_container" pg_dump -U postgres -d "$source_db" \
--schema-only --no-owner --no-privileges >"$workdir/schema.sql"
docker exec "$source_container" pg_dump -U postgres -d "$source_db" \
--data-only --no-owner --no-privileges \
--table=public.agents --table=public.claims >"$workdir/reference-data.sql"
docker cp "$workdir/schema.sql" "$container:/tmp/schema.sql"
docker cp "$workdir/reference-data.sql" "$container:/tmp/reference-data.sql"
docker exec -i "$container" psql -U postgres -d postgres -v ON_ERROR_STOP=1 -q <<SQL
create role kb_apply login noinherit nosuperuser nocreatedb nocreaterole
noreplication nobypassrls password '${apply_password}';
create role kb_review login noinherit nosuperuser nocreatedb nocreaterole
noreplication nobypassrls password '${review_password}';
create role kb_gate_owner nologin noinherit nosuperuser nocreatedb nocreaterole
noreplication nobypassrls;
SQL
docker exec "$container" psql -U postgres -d teleo -v ON_ERROR_STOP=1 -q \
-f /tmp/schema.sql
docker exec "$container" psql -U postgres -d teleo -v ON_ERROR_STOP=1 -q \
-f /tmp/reference-data.sql
docker exec "$container" psql -U postgres -d teleo -v ON_ERROR_STOP=1 -q \
-c "insert into public.agents (id, handle, kind)
values ('99999999-9999-9999-9999-999999999999', 'm3ta', 'human')
on conflict (handle) do nothing"
cat >"$workdir/kb-apply.env" <<EOF
KB_APPLY_PASSWORD=${apply_password}
EOF
cat >"$workdir/kb-review.env" <<EOF
KB_REVIEW_PASSWORD=${review_password}
EOF
chmod 600 "$workdir/kb-apply.env" "$workdir/kb-review.env"
normalization_args=()
if [[ -n "$normalization_json" ]]; then
normalization_args=(--normalization-json "$normalization_json")
fi
set +e
python3 "$repo_root/scripts/run_approve_claim_clone_canary.py" \
--container "$container" \
--source-db teleo \
--database-prefix teleo_approve_claim_isolated \
--service "$service" \
--approve-script "$repo_root/scripts/approve_proposal.py" \
--apply-script "$repo_root/scripts/apply_proposal.py" \
--prereqs "$repo_root/scripts/kb_apply_prereqs.sql" \
--review-secrets-file "$workdir/kb-review.env" \
--secrets-file "$workdir/kb-apply.env" \
"${normalization_args[@]}" \
--output "$output" \
>"$workdir/stdout.json" 2>"$workdir/stderr.log"
canary_rc=$?
set -e
set +e
docker rm -f "$container" >/dev/null 2>&1
outer_container_remove_rc=$?
rm -rf "$workdir"
outer_workdir_remove_rc=$?
container_name_readback="$(
docker container ls -a --filter "name=^/${container}$" --format '{{.Names}}' 2>&1
)"
container_name_readback_rc=$?
if [[ -e "$workdir" || -L "$workdir" ]]; then
outer_workdir_lexists=true
else
outer_workdir_lexists=false
fi
set -e
trap - EXIT
source_counts_after="$(source_counts)"
service_after="$(service_state)"
SOURCE_COUNTS_BEFORE="$source_counts_before" \
SOURCE_COUNTS_AFTER="$source_counts_after" \
SOURCE_CONTAINER="$source_container" \
SOURCE_DB="$source_db" \
SERVICE_BEFORE="$service_before" \
SERVICE_AFTER="$service_after" \
REPO_ROOT="$repo_root" \
OUTER_CONTAINER_REMOVE_RC="$outer_container_remove_rc" \
OUTER_WORKDIR_REMOVE_RC="$outer_workdir_remove_rc" \
CONTAINER_NAME_READBACK="$container_name_readback" \
CONTAINER_NAME_READBACK_RC="$container_name_readback_rc" \
OUTER_WORKDIR_LEXISTS="$outer_workdir_lexists" \
CANARY_RC="$canary_rc" \
python3 - "$output" <<'PY'
import hashlib
import json
import os
import sys
from pathlib import Path
path = Path(sys.argv[1])
if not path.is_file():
raise SystemExit("inner canary did not produce a report")
data = json.loads(path.read_text(encoding="utf-8"))
def service(raw: str) -> dict[str, str]:
return dict(line.split("=", 1) for line in raw.splitlines() if "=" in line)
def table_deltas(before: dict[str, int], after: dict[str, int]) -> dict[str, int]:
if set(before) != set(after):
raise SystemExit(
"live source count keys changed during the canary: "
f"before={sorted(before)} after={sorted(after)}"
)
return {key: after[key] - before[key] for key in sorted(before)}
def verify_source_binding(root: Path, records: list[dict[str, object]]) -> dict[str, object]:
readbacks = []
for record in records:
relative_text = str(record.get("repo_relative_path", ""))
relative = Path(relative_text)
if not relative_text or relative.is_absolute() or ".." in relative.parts:
raise SystemExit(f"unsafe repo-relative source path: {relative_text!r}")
candidate = (root / relative).resolve()
try:
candidate.relative_to(root)
except ValueError as exc:
raise SystemExit(
f"source path escaped repository root: {relative_text}"
) from exc
if not candidate.is_file():
readbacks.append(
{
"repo_relative_path": relative.as_posix(),
"expected_sha256": record.get("sha256"),
"actual_sha256": None,
"matches": False,
}
)
continue
content = candidate.read_bytes()
actual_sha256 = hashlib.sha256(content).hexdigest()
readbacks.append(
{
"repo_relative_path": relative.as_posix(),
"expected_sha256": record.get("sha256"),
"actual_sha256": actual_sha256,
"size_bytes": len(content),
"matches": (
actual_sha256 == record.get("sha256")
and len(content) == record.get("size_bytes")
),
}
)
return {
"files": readbacks,
"all_match": bool(readbacks) and all(row["matches"] for row in readbacks),
}
def contains_ephemeral_path(value: object) -> bool:
if isinstance(value, str):
return "/tmp/" in value
if isinstance(value, dict):
return any(contains_ephemeral_path(item) for item in value.values())
if isinstance(value, list):
return any(contains_ephemeral_path(item) for item in value)
return False
before_counts = json.loads(os.environ["SOURCE_COUNTS_BEFORE"])
after_counts = json.loads(os.environ["SOURCE_COUNTS_AFTER"])
live_table_deltas = table_deltas(before_counts, after_counts)
before_service = service(os.environ["SERVICE_BEFORE"])
after_service = service(os.environ["SERVICE_AFTER"])
service_stable = all(
before_service.get(key) == after_service.get(key)
for key in ("ActiveState", "SubState", "MainPID", "NRestarts")
)
container_absent = (
int(os.environ["CONTAINER_NAME_READBACK_RC"]) == 0
and not os.environ["CONTAINER_NAME_READBACK"].strip()
)
workdir_absent = os.environ["OUTER_WORKDIR_LEXISTS"] == "false"
source_binding = verify_source_binding(
Path(os.environ["REPO_ROOT"]).resolve(),
data.get("source_binding", {}).get("files", []),
)
data["source_binding"]["independent_post_cleanup_verification"] = source_binding
source_binding_ok = (
data["source_binding"].get("unchanged_during_run") is True
and source_binding["all_match"] is True
)
stale_ephemeral_paths_absent = not contains_ephemeral_path(data)
inner_runtime_pass = data.get("status") == "pass" and int(os.environ["CANARY_RC"]) == 0
outer_ok = (
before_counts == after_counts
and all(delta == 0 for delta in live_table_deltas.values())
and service_stable
and container_absent
and workdir_absent
and source_binding_ok
and stale_ephemeral_paths_absent
)
data["required_tier"] = "T3_live_readonly"
data["required_tiers"] = ["T2_runtime", "T3_live_readonly"]
data["runtime_scope"] = "isolated_postgres_container_from_readonly_live_schema"
data["outer_isolation"] = {
"source_container": os.environ.get("SOURCE_CONTAINER", "teleo-pg"),
"source_database": os.environ.get("SOURCE_DB", "teleo"),
"source_counts_before": before_counts,
"source_counts_after": after_counts,
"canonical_table_deltas": live_table_deltas,
"source_counts_unchanged": before_counts == after_counts,
"service_before": before_service,
"service_after": after_service,
"service_stable": service_stable,
"cleanup_readback": {
"container_remove_returncode": int(os.environ["OUTER_CONTAINER_REMOVE_RC"]),
"container_name_query_returncode": int(
os.environ["CONTAINER_NAME_READBACK_RC"]
),
"container_name_query_stdout": os.environ["CONTAINER_NAME_READBACK"],
"temporary_container_absent": container_absent,
"workdir_remove_returncode": int(os.environ["OUTER_WORKDIR_REMOVE_RC"]),
"workdir_lexists_after_cleanup": not workdir_absent,
"temporary_workdir_absent": workdir_absent,
},
}
data["checks"]["outer_live_counts_exactly_unchanged"] = before_counts == after_counts
data["checks"]["outer_live_table_deltas_exactly_zero"] = all(
delta == 0 for delta in live_table_deltas.values()
)
data["checks"]["outer_service_stable"] = service_stable
data["checks"]["outer_container_absent_independent_readback"] = container_absent
data["checks"]["outer_workdir_absent_independent_readback"] = workdir_absent
data["checks"]["source_binding_independently_verified"] = source_binding_ok
data["checks"]["stale_ephemeral_paths_absent"] = stale_ephemeral_paths_absent
data["checks"]["outer_isolation_complete"] = outer_ok
if outer_ok and inner_runtime_pass:
data["current_tier"] = "T3_live_readonly"
else:
data["status"] = "fail"
data["current_tier"] = "T2_runtime" if inner_runtime_pass else "T1_model"
path.write_text(json.dumps(data, indent=2, sort_keys=True) + "\n", encoding="utf-8")
raise SystemExit(0 if data["status"] == "pass" else 1)
PY

View file

@ -4,8 +4,8 @@ Pure tests: exercise the SQL builders, payload validation, and dispatch without
a live database. Runnable under pytest or standalone (`python3 tests/test_apply_proposal.py`). a live database. Runnable under pytest or standalone (`python3 tests/test_apply_proposal.py`).
""" """
from pathlib import Path
import sys import sys
from pathlib import Path
try: try:
import pytest import pytest
@ -31,9 +31,24 @@ sys.path.insert(0, str(REPO_ROOT / "scripts"))
import apply_proposal as ap # noqa: E402 import apply_proposal as ap # noqa: E402
def _approval_meta(proposal_type, apply_payload):
return {
"proposal_type": proposal_type,
"payload": {"apply_payload": apply_payload},
"reviewed_by_handle": "m3ta",
"reviewed_by_agent_id": None,
"reviewed_at": "2026-07-10T00:00:00+00:00",
"review_note": "Reviewed exact strict payload.",
}
# --- literals ------------------------------------------------------------- # # --- literals ------------------------------------------------------------- #
def test_sql_literal_escapes_single_quotes(): def test_sql_literal_escapes_single_quotes():
assert ap.sql_literal("O'Brien") == "'O''Brien'" assert ap.sql_literal("O'Brien") == "E'O''Brien'"
def test_sql_literal_escapes_backslashes_independently_of_session_mode():
assert ap.sql_literal("a\\b'c") == "E'a\\\\b''c'"
def test_sql_literal_none_is_null(): def test_sql_literal_none_is_null():
@ -64,7 +79,8 @@ def _revise_payload():
def test_revise_strategy_sql_shape(): def test_revise_strategy_sql_shape():
sql = ap.build_revise_strategy_sql(_revise_payload()["apply_payload"], "pid-1", "m3ta") payload = _revise_payload()["apply_payload"]
sql = ap.build_revise_strategy_sql(payload, "pid-1", "kb-apply", _approval_meta("revise_strategy", payload))
assert sql.startswith("begin;") assert sql.startswith("begin;")
assert sql.rstrip().endswith("commit;") assert sql.rstrip().endswith("commit;")
# deactivate old, insert versioned active, retire nodes, insert new nodes # deactivate old, insert versioned active, retire nodes, insert new nodes
@ -74,7 +90,7 @@ def test_revise_strategy_sql_shape():
assert "update public.strategy_nodes" in sql assert "update public.strategy_nodes" in sql
assert "insert into public.strategy_nodes" in sql assert "insert into public.strategy_nodes" in sql
# ledger + invariant # ledger + invariant
assert "status = 'applied'" in sql assert "kb_stage.finish_approved_proposal" in sql
assert "exactly one active strategy" in sql assert "exactly one active strategy" in sql
# proximate_objectives rendered as jsonb # proximate_objectives rendered as jsonb
assert "::jsonb" in sql assert "::jsonb" in sql
@ -84,45 +100,320 @@ def test_revise_strategy_requires_agent_id():
payload = _revise_payload()["apply_payload"] payload = _revise_payload()["apply_payload"]
del payload["agent_id"] del payload["agent_id"]
with pytest.raises(ValueError): with pytest.raises(ValueError):
ap.build_revise_strategy_sql(payload, "pid-1", None) ap.build_revise_strategy_sql(payload, "pid-1", None, _approval_meta("revise_strategy", payload))
def test_revise_strategy_rejects_bad_node_type(): def test_revise_strategy_rejects_bad_node_type():
payload = _revise_payload()["apply_payload"] payload = _revise_payload()["apply_payload"]
payload["strategy_nodes"][0]["node_type"] = "manifesto" payload["strategy_nodes"][0]["node_type"] = "manifesto"
with pytest.raises(ValueError): with pytest.raises(ValueError):
ap.build_revise_strategy_sql(payload, "pid-1", None) ap.build_revise_strategy_sql(payload, "pid-1", None, _approval_meta("revise_strategy", payload))
# --- add_edge ------------------------------------------------------------- # # --- add_edge ------------------------------------------------------------- #
def test_add_edge_sql_dedup_guard(): def test_add_edge_sql_dedup_guard():
payload = {"from_claim": "aaaa", "to_claim": "bbbb", "edge_type": "supersedes", "weight": 0.9} payload = {"from_claim": "aaaa", "to_claim": "bbbb", "edge_type": "supersedes", "weight": 0.9}
sql = ap.build_add_edge_sql(payload, "pid-2", None) sql = ap.build_add_edge_sql(payload, "pid-2", None, _approval_meta("add_edge", payload))
assert "insert into public.claim_edges" in sql assert "insert into public.claim_edges" in sql
assert "not exists" in sql assert "not exists" in sql
assert "::edge_type" in sql assert "::edge_type" in sql
assert "status = 'applied'" in sql assert "kb_stage.finish_approved_proposal" in sql
assert "pg_advisory_xact_lock" in sql
def test_add_edge_semantic_row_accepts_match_and_rejects_weight_mismatch():
payload = {"from_claim": "aaaa", "to_claim": "bbbb", "edge_type": "supports", "weight": 0.9}
sql = ap.build_add_edge_sql(payload, "pid-2", None, _approval_meta("add_edge", payload))
verify = sql[sql.index("do $verify$") : sql.index("kb_stage.finish_approved_proposal")]
assert "weight is not distinct from 0.9" in verify
assert "weight is distinct from 0.9" in verify
assert "claim_edge row does not match strict apply payload" in verify
assert sql.startswith("begin;")
assert sql.rstrip().endswith("commit;")
assert sql.index("raise exception 'apply_proposal: claim_edge row") < sql.index(
"kb_stage.finish_approved_proposal"
)
def test_add_edge_rejects_self_loop(): def test_add_edge_rejects_self_loop():
payload = {"from_claim": "same", "to_claim": "same", "edge_type": "supports"} payload = {"from_claim": "same", "to_claim": "same", "edge_type": "supports"}
with pytest.raises(ValueError): with pytest.raises(ValueError):
ap.build_add_edge_sql(payload, "pid-2", None) ap.build_add_edge_sql(payload, "pid-2", None, _approval_meta("add_edge", payload))
# --- attach_evidence ------------------------------------------------------ # # --- attach_evidence ------------------------------------------------------ #
def test_attach_evidence_sql_with_source_id(): def test_attach_evidence_sql_with_source_id():
payload = {"evidence": [{"claim_id": "cccc", "source_id": "dddd", "role": "grounds", "weight": 0.78}]} payload = {"evidence": [{"claim_id": "cccc", "source_id": "dddd", "role": "grounds", "weight": 0.78}]}
sql = ap.build_attach_evidence_sql(payload, "pid-3", None) sql = ap.build_attach_evidence_sql(payload, "pid-3", None, _approval_meta("attach_evidence", payload))
assert "insert into public.claim_evidence" in sql assert "insert into public.claim_evidence" in sql
assert "::evidence_role" in sql assert "::evidence_role" in sql
assert "not exists" in sql assert "not exists" in sql
def test_attach_evidence_semantic_row_accepts_match_and_rejects_weight_mismatch():
payload = {
"evidence": [
{"claim_id": "cccc", "source_id": "dddd", "role": "grounds", "weight": 0.78}
]
}
sql = ap.build_attach_evidence_sql(
payload, "pid-3", None, _approval_meta("attach_evidence", payload)
)
verify = sql[sql.index("do $verify$") : sql.index("kb_stage.finish_approved_proposal")]
assert "weight is not distinct from 0.78" in verify
assert "weight is distinct from 0.78" in verify
assert "evidence row % does not match strict apply payload" in verify
assert sql.startswith("begin;")
assert sql.rstrip().endswith("commit;")
assert sql.index("raise exception 'apply_proposal: evidence row") < sql.index(
"kb_stage.finish_approved_proposal"
)
def test_attach_evidence_requires_source_id(): def test_attach_evidence_requires_source_id():
payload = {"evidence": [{"claim_id": "cccc"}]} payload = {"evidence": [{"claim_id": "cccc"}]}
with pytest.raises(ValueError): with pytest.raises(ValueError):
ap.build_attach_evidence_sql(payload, "pid-3", None) ap.build_attach_evidence_sql(payload, "pid-3", None, _approval_meta("attach_evidence", payload))
# --- approve_claim canonical bundle -------------------------------------- #
def _approve_claim_bundle():
claim_a = "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa"
claim_b = "bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb"
source_a = "cccccccc-cccc-4ccc-8ccc-cccccccccccc"
source_b = "dddddddd-dddd-4ddd-8ddd-dddddddddddd"
return {
"contract_version": 2,
"agent_id": "11111111-1111-4111-8111-111111111111",
"claims": [
{
"id": claim_a,
"type": "structural",
"text": "Canonical claim A",
"status": "open",
"confidence": 0.8,
"tags": ["test", "working-leo"],
"created_by": None,
},
{
"id": claim_b,
"type": "concept",
"text": "Canonical claim B",
"status": "open",
"confidence": None,
"tags": [],
"created_by": None,
},
],
"sources": [
{
"id": source_a,
"source_type": "paper",
"url": "https://example.test/a",
"storage_path": None,
"excerpt": "Evidence A",
"hash": "hash-a",
"created_by": None,
},
{
"id": source_b,
"source_type": "observation",
"url": None,
"storage_path": None,
"excerpt": "Evidence B",
"hash": "hash-b",
"created_by": None,
},
],
"evidence": [
{"claim_id": claim_a, "source_id": source_a, "role": "grounds", "weight": 0.9},
{"claim_id": claim_b, "source_id": source_b, "role": "illustrates", "weight": None},
],
"edges": [
{"from_claim": claim_a, "to_claim": claim_b, "edge_type": "supports", "weight": 0.7}
],
"reasoning_tools": [
{
"id": "eeeeeeee-eeee-4eee-8eee-eeeeeeeeeeee",
"agent_id": None,
"name": "Canonical test tool",
"description": "A strict reasoning-tool row.",
"category": "test",
}
],
}
def test_approve_claim_bundle_sql_is_atomic_and_row_verified():
payload = _approve_claim_bundle()
sql = ap.build_approve_claim_sql(
payload,
"99999999-9999-4999-8999-999999999999",
None,
_approval_meta("approve_claim", payload),
)
assert sql.startswith("begin;")
assert sql.rstrip().endswith("commit;")
assert "insert into public.claims" in sql
assert "insert into public.sources" in sql
assert "insert into public.claim_evidence" in sql
assert "insert into public.claim_edges" in sql
assert "insert into public.reasoning_tools" in sql
assert "source hash collision" in sql
assert "claim row does not match strict apply payload" in sql
assert "source row does not match strict apply payload" in sql
assert "reasoning tool row does not match strict apply payload" in sql
assert "kb_stage.finish_approved_proposal" in sql
def test_approve_claim_semantic_rows_accept_matches_and_reject_payload_mismatches():
payload = _approve_claim_bundle()
sql = ap.build_approve_claim_sql(
payload,
"99999999-9999-4999-8999-999999999999",
None,
_approval_meta("approve_claim", payload),
)
verify = sql[sql.index("do $verify$") : sql.index("kb_stage.finish_approved_proposal")]
assert "weight is not distinct from 0.9" in verify
assert "weight is distinct from 0.9" in verify
assert "weight is not distinct from 0.7" in verify
assert "weight is distinct from 0.7" in verify
assert "weight is not distinct from null" in verify
assert "weight is distinct from null" in verify
expected_creator = "E'11111111-1111-4111-8111-111111111111'::uuid"
assert f"created_by is not distinct from {expected_creator}" in verify
assert f"created_by is distinct from {expected_creator}" in verify
assert "approve_claim: evidence row does not match strict apply payload" in verify
assert "approve_claim: edge row does not match strict apply payload" in verify
assert sql.startswith("begin;")
assert sql.rstrip().endswith("commit;")
assert sql.index("raise exception 'approve_claim: evidence row") < sql.index(
"kb_stage.finish_approved_proposal"
)
def test_approve_claim_bundle_rejects_duplicate_source_hashes():
payload = _approve_claim_bundle()
payload["sources"][1]["hash"] = payload["sources"][0]["hash"]
with pytest.raises(ValueError):
ap.build_approve_claim_sql(
payload,
"99999999-9999-4999-8999-999999999999",
None,
_approval_meta("approve_claim", payload),
)
def test_approve_claim_bundle_rejects_invalid_enum_and_self_edge():
payload = _approve_claim_bundle()
payload["claims"][0]["type"] = "vibes"
with pytest.raises(ValueError):
ap.build_approve_claim_sql(
payload,
"99999999-9999-4999-8999-999999999999",
None,
_approval_meta("approve_claim", payload),
)
payload = _approve_claim_bundle()
payload["edges"][0]["to_claim"] = payload["edges"][0]["from_claim"]
with pytest.raises(ValueError):
ap.build_approve_claim_sql(
payload,
"99999999-9999-4999-8999-999999999999",
None,
_approval_meta("approve_claim", payload),
)
def test_approve_claim_rejects_unknown_keys_and_contract_versions():
payload = _approve_claim_bundle()
payload["contract_version"] = 1
with pytest.raises(ValueError):
ap.build_approve_claim_sql(
payload,
"99999999-9999-4999-8999-999999999999",
None,
_approval_meta("approve_claim", payload),
)
payload = _approve_claim_bundle()
payload["contract_version"] = 2
payload["claimz"] = payload.pop("claims")
with pytest.raises(ValueError):
ap.build_approve_claim_sql(
payload,
"99999999-9999-4999-8999-999999999999",
None,
_approval_meta("approve_claim", payload),
)
def test_approve_claim_verifies_superseded_by_and_pins_string_mode():
payload = _approve_claim_bundle()
payload["contract_version"] = 2
payload["claims"][0]["superseded_by"] = None
sql = ap.build_approve_claim_sql(
payload,
"99999999-9999-4999-8999-999999999999",
None,
_approval_meta("approve_claim", payload),
)
assert "superseded_by" in sql
assert "set local standard_conforming_strings = on" in sql.lower()
def test_apply_sql_locks_and_binds_approval_before_canonical_write():
payload = _approve_claim_bundle()
payload["contract_version"] = 2
proposal = {
"id": "99999999-9999-4999-8999-999999999999",
"proposal_type": "approve_claim",
"status": "approved",
"payload": {"apply_payload": payload},
"reviewed_by_handle": "m3ta",
"reviewed_by_agent_id": None,
"reviewed_at": "2026-07-10T00:00:00+00:00",
"review_note": "Reviewed exact strict payload.",
}
sql = ap.build_apply_sql(proposal, "kb-apply")
assert "kb_stage.assert_approved_proposal" in sql
assert "kb_stage.finish_approved_proposal" in sql
assert sql.index("kb_stage.assert_approved_proposal") < sql.index("insert into public.claims")
assert sql.index("insert into public.claims") < sql.index("kb_stage.finish_approved_proposal")
assert "Reviewed exact strict payload." in sql
def test_build_apply_sql_requires_review_provenance():
proposal = {
"id": "99999999-9999-4999-8999-999999999999",
"proposal_type": "approve_claim",
"status": "approved",
"payload": {"apply_payload": _approve_claim_bundle()},
}
with pytest.raises(ValueError):
ap.build_apply_sql(proposal, "kb-apply")
def test_approve_claim_dispatch_and_prerequisite_grants():
proposal = {
"id": "99999999-9999-4999-8999-999999999999",
"proposal_type": "approve_claim",
"payload": {"apply_payload": _approve_claim_bundle()},
}
proposal.update(_approval_meta("approve_claim", proposal["payload"]["apply_payload"]))
sql = ap.build_apply_sql(proposal, None)
assert "insert into public.claims" in sql
assert "approve_claim" in ap.APPLYABLE_TYPES
prereqs = (REPO_ROOT / "scripts" / "kb_apply_prereqs.sql").read_text(encoding="utf-8")
assert "grant select, insert on public.claims to kb_apply" in prereqs.lower()
assert "grant select, insert on public.sources to kb_apply" in prereqs.lower()
assert "grant select, insert on public.reasoning_tools to kb_apply" in prereqs.lower()
# --- dispatch + guards ---------------------------------------------------- # # --- dispatch + guards ---------------------------------------------------- #
@ -153,33 +444,39 @@ def test_assert_applyable_allows_approved():
# --- ledger flip: concurrency guard + FK stamp --------------------------- # # --- ledger flip: concurrency guard + FK stamp --------------------------- #
def test_ledger_flip_asserts_rowcount_one(): def test_ledger_transition_uses_payload_bound_security_functions():
# The flip must guard against a concurrent double-apply by asserting exactly payload = {"from_claim": "a", "to_claim": "b", "edge_type": "supports"}
# one 'approved' row moved, not by re-reading status afterwards. sql = ap.build_add_edge_sql(payload, "pid", None, _approval_meta("add_edge", payload))
sql = ap.build_add_edge_sql({"from_claim": "a", "to_claim": "b", "edge_type": "supports"}, "pid", None) assert "kb_stage.assert_approved_proposal" in sql
assert "get diagnostics" in sql assert "kb_stage.finish_approved_proposal" in sql
assert "flipped <> 1" in sql assert sql.index("kb_stage.assert_approved_proposal") < sql.index("insert into public.claim_edges")
assert "and status = 'approved'" in sql assert sql.index("insert into public.claim_edges") < sql.index("kb_stage.finish_approved_proposal")
prereqs = (REPO_ROOT / "scripts" / "kb_apply_prereqs.sql").read_text(encoding="utf-8").lower()
assert "status = 'approved'" in prereqs
assert "for update" in prereqs
assert "payload = p_payload" in prereqs
def test_ledger_flip_stamps_agent_fk(): def test_ledger_flip_stamps_agent_fk():
sql = ap.build_add_edge_sql({"from_claim": "a", "to_claim": "b", "edge_type": "supports"}, "pid", "kb-apply") payload = {"from_claim": "a", "to_claim": "b", "edge_type": "supports"}
sql = ap.build_add_edge_sql(payload, "pid", "kb-apply", _approval_meta("add_edge", payload))
# Hard resolve into a variable + NOT-NULL assert, never a silent inline # Hard resolve into a variable + NOT-NULL assert, never a silent inline
# subselect that would stamp NULL on an unresolved handle. # subselect that would stamp NULL on an unresolved handle.
assert "select id into resolved_agent_id" in sql assert "kb_stage.finish_approved_proposal" in sql
assert "resolved_agent_id is null then" in sql assert "'kb-apply'" in sql
assert "applied_by_agent_id = resolved_agent_id" in sql
assert "applied_by_handle = 'kb-apply'" in sql
def test_build_apply_sql_defaults_applied_by_to_service_agent(): def test_build_apply_sql_defaults_applied_by_to_service_agent():
payload = {"from_claim": "a", "to_claim": "b", "edge_type": "supports"}
proposal = { proposal = {
"id": "pid", "id": "pid",
"proposal_type": "add_edge", "proposal_type": "add_edge",
"payload": {"apply_payload": {"from_claim": "a", "to_claim": "b", "edge_type": "supports"}}, "payload": {"apply_payload": payload},
} }
proposal.update(_approval_meta("add_edge", payload))
sql = ap.build_apply_sql(proposal, None) sql = ap.build_apply_sql(proposal, None)
assert f"applied_by_handle = '{ap.SERVICE_AGENT_HANDLE}'" in sql assert f"E'{ap.SERVICE_AGENT_HANDLE}'" in sql
def test_load_password_accepts_live_db_password_key(tmp_path): def test_load_password_accepts_live_db_password_key(tmp_path):
@ -197,7 +494,7 @@ if __name__ == "__main__":
try: try:
fn() fn()
print(f"PASS {name}") print(f"PASS {name}")
except Exception: # noqa: BLE001 except Exception:
failures += 1 failures += 1
print(f"FAIL {name}") print(f"FAIL {name}")
traceback.print_exc() traceback.print_exc()

View file

@ -25,7 +25,14 @@ except ImportError: # standalone fallback so the file runs without pytest insta
return return
raise AssertionError(f"expected {exc.__name__} to be raised") raise AssertionError(f"expected {exc.__name__} to be raised")
def _fixture(*_args, **_kwargs):
def decorator(func):
return func
return decorator
pytest.raises = _raises pytest.raises = _raises
pytest.fixture = _fixture
REPO_ROOT = Path(__file__).resolve().parents[1] REPO_ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(REPO_ROOT / "scripts")) sys.path.insert(0, str(REPO_ROOT / "scripts"))
@ -36,11 +43,13 @@ import apply_worker as w # noqa: E402
@pytest.fixture(autouse=True) @pytest.fixture(autouse=True)
def restore_apply_worker_dependencies(): def restore_apply_worker_dependencies():
original_load_password = w.ap.load_password original_load_password = w.ap.load_password
original_load_failure_state = w.load_failure_state
original_fetch_candidates = w.fetch_candidates original_fetch_candidates = w.fetch_candidates
original_apply_one = w.apply_one original_apply_one = w.apply_one
original_render_one = w.render_one original_render_one = w.render_one
yield yield
w.ap.load_password = original_load_password w.ap.load_password = original_load_password
w.load_failure_state = original_load_failure_state
w.fetch_candidates = original_fetch_candidates w.fetch_candidates = original_fetch_candidates
w.apply_one = original_apply_one w.apply_one = original_apply_one
w.render_one = original_render_one w.render_one = original_render_one
@ -68,6 +77,20 @@ def test_candidate_query_lists_worker_types_only():
assert "reject_claim" not in sql assert "reject_claim" not in sql
def test_candidate_query_can_exclude_poisoned_ids_before_limit():
sql = w.build_candidate_query(excluded_ids=("aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",))
assert "id::text not in" in sql.lower()
assert "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa" in sql
assert sql.lower().index("id::text not in") < sql.lower().index("limit 20")
def test_worker_includes_approved_claim_bundles_without_unproven_render_hook():
assert "approve_claim" in w.WORKER_TYPES
assert "approve_claim" not in w.RENDER_TYPES
sql = w.build_candidate_query()
assert "'approve_claim'" in sql
# --- candidate parsing ---------------------------------------------------- # # --- candidate parsing ---------------------------------------------------- #
def test_parse_candidates_skips_blank_lines(): def test_parse_candidates_skips_blank_lines():
out = '{"id": "a", "proposal_type": "add_edge", "agent_id": null}\n\n' out = '{"id": "a", "proposal_type": "add_edge", "agent_id": null}\n\n'
@ -106,7 +129,9 @@ def _args(**over):
def test_report_only_gate_does_not_apply(monkeypatch=None): def test_report_only_gate_does_not_apply(monkeypatch=None):
calls = [] calls = []
w.ap.load_password = lambda _f: "pw" w.ap.load_password = lambda _f: "pw"
w.fetch_candidates = lambda a, p: [{"id": "p1", "proposal_type": "revise_strategy", "agent_id": "ag"}] w.fetch_candidates = lambda a, p, excluded: [
{"id": "p1", "proposal_type": "revise_strategy", "agent_id": "ag"}
]
w.apply_one = lambda a, pid: calls.append(pid) w.apply_one = lambda a, pid: calls.append(pid)
rc = w.run(_args(enable=False)) rc = w.run(_args(enable=False))
@ -117,7 +142,9 @@ def test_report_only_gate_does_not_apply(monkeypatch=None):
def test_enabled_worker_applies_and_renders(): def test_enabled_worker_applies_and_renders():
applied, rendered = [], [] applied, rendered = [], []
w.ap.load_password = lambda _f: "pw" w.ap.load_password = lambda _f: "pw"
w.fetch_candidates = lambda a, p: [{"id": "p1", "proposal_type": "revise_strategy", "agent_id": "ag"}] w.fetch_candidates = lambda a, p, excluded: [
{"id": "p1", "proposal_type": "revise_strategy", "agent_id": "ag"}
]
w.apply_one = lambda a, pid: applied.append(pid) w.apply_one = lambda a, pid: applied.append(pid)
w.render_one = lambda a, agent_id: rendered.append(agent_id) or "ok" w.render_one = lambda a, agent_id: rendered.append(agent_id) or "ok"
@ -130,7 +157,9 @@ def test_enabled_worker_applies_and_renders():
def test_enabled_worker_skips_render_for_add_edge(): def test_enabled_worker_skips_render_for_add_edge():
applied, rendered = [], [] applied, rendered = [], []
w.ap.load_password = lambda _f: "pw" w.ap.load_password = lambda _f: "pw"
w.fetch_candidates = lambda a, p: [{"id": "e1", "proposal_type": "add_edge", "agent_id": None}] w.fetch_candidates = lambda a, p, excluded: [
{"id": "e1", "proposal_type": "add_edge", "agent_id": None}
]
w.apply_one = lambda a, pid: applied.append(pid) w.apply_one = lambda a, pid: applied.append(pid)
w.render_one = lambda a, agent_id: rendered.append(agent_id) or "ok" w.render_one = lambda a, agent_id: rendered.append(agent_id) or "ok"
@ -140,6 +169,21 @@ def test_enabled_worker_skips_render_for_add_edge():
assert rendered == [] # add_edge changes the graph, not the rendered self assert rendered == [] # add_edge changes the graph, not the rendered self
def test_enabled_worker_skips_render_for_approve_claim():
applied, rendered = [], []
w.ap.load_password = lambda _f: "pw"
w.fetch_candidates = lambda a, p, excluded: [
{"id": "c1", "proposal_type": "approve_claim", "agent_id": "ag"}
]
w.apply_one = lambda a, pid: applied.append(pid)
w.render_one = lambda a, agent_id: rendered.append(agent_id) or "ok"
rc = w.run(_args(enable=True))
assert rc == 0
assert applied == ["c1"]
assert rendered == [] # no proven agent-owned approve_claim render contract
# --- per-tick cap + poison-pill ceiling ----------------------------------- # # --- per-tick cap + poison-pill ceiling ----------------------------------- #
def _cands(*ids): def _cands(*ids):
return [{"id": i, "proposal_type": "add_edge", "agent_id": None} for i in ids] return [{"id": i, "proposal_type": "add_edge", "agent_id": None} for i in ids]
@ -160,7 +204,7 @@ def test_partition_skips_poison_pill_over_ceiling():
def test_enabled_worker_caps_applies_at_one_per_tick(): def test_enabled_worker_caps_applies_at_one_per_tick():
applied = [] applied = []
w.ap.load_password = lambda _f: "pw" w.ap.load_password = lambda _f: "pw"
w.fetch_candidates = lambda a, p: _cands("e1", "e2", "e3") w.fetch_candidates = lambda a, p, excluded: _cands("e1", "e2", "e3")
w.apply_one = lambda a, pid: applied.append(pid) w.apply_one = lambda a, pid: applied.append(pid)
rc = w.run(_args(enable=True, max_per_tick=1)) rc = w.run(_args(enable=True, max_per_tick=1))
@ -185,7 +229,7 @@ def test_apply_failure_increments_persisted_count():
path = str(Path(tempfile.mkdtemp()) / "failures.json") path = str(Path(tempfile.mkdtemp()) / "failures.json")
w.ap.load_password = lambda _f: "pw" w.ap.load_password = lambda _f: "pw"
w.fetch_candidates = lambda a, p: _cands("bad") w.fetch_candidates = lambda a, p, excluded: _cands("bad")
def _boom(a, pid): def _boom(a, pid):
raise RuntimeError("deterministic failure") raise RuntimeError("deterministic failure")
@ -196,12 +240,31 @@ def test_apply_failure_increments_persisted_count():
assert w.load_failure_state(path).get("bad") == 1 # count persisted for next tick assert w.load_failure_state(path).get("bad") == 1 # count persisted for next tick
def test_run_loads_failure_state_and_excludes_exhausted_ids_before_fetch():
events = []
poisoned_id = "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa"
w.ap.load_password = lambda _f: "pw"
w.load_failure_state = lambda _path: events.append("load") or {poisoned_id: 3}
def _fetch(args, password, excluded_ids):
events.append(("fetch", excluded_ids))
return []
w.fetch_candidates = _fetch
rc = w.run(_args(max_attempts=3))
assert rc == 0
assert events == ["load", ("fetch", (poisoned_id,))]
if __name__ == "__main__": if __name__ == "__main__":
import traceback import traceback
failures = 0 failures = 0
for name, fn in sorted(globals().items()): for name, fn in sorted(globals().items()):
if name.startswith("test_") and callable(fn): if name.startswith("test_") and callable(fn):
restore = restore_apply_worker_dependencies()
next(restore)
try: try:
fn() fn()
print(f"PASS {name}") print(f"PASS {name}")
@ -209,4 +272,9 @@ if __name__ == "__main__":
failures += 1 failures += 1
print(f"FAIL {name}") print(f"FAIL {name}")
traceback.print_exc() traceback.print_exc()
finally:
try:
next(restore)
except StopIteration:
pass
sys.exit(1 if failures else 0) sys.exit(1 if failures else 0)

View file

@ -82,15 +82,52 @@ def test_assert_approvable_rejects_freeform_without_apply_payload():
def test_build_approve_sql_has_required_guards(): def test_build_approve_sql_has_required_guards():
sql = approve.build_approve_sql("00957f6c-9883-4015-95a4-6b09367efb0e", "m3ta", "O'Brien approves") proposal = _strict_pending()
assert "status = 'approved'" in sql sql = approve.build_approve_sql(proposal, "m3ta", "O'Brien approves")
assert "where id = '00957f6c-9883-4015-95a4-6b09367efb0e'::uuid" in sql assert "kb_stage.approve_strict_proposal" in sql
assert "and status = 'pending_review'" in sql assert "update kb_stage.kb_proposals" not in sql.lower()
assert "and payload ? 'apply_payload'" in sql assert "E'00957f6c-9883-4015-95a4-6b09367efb0e'::uuid" in sql
assert "'O''Brien approves'" in sql assert "E'add_edge'" in sql
assert "'add_edge'" in sql assert "edge_type" in sql
assert "'attach_evidence'" in sql assert "supports" in sql
assert "'revise_strategy'" in sql assert "E'O''Brien approves'" in sql
assert "set local standard_conforming_strings = on" in sql.lower()
prereqs = (REPO_ROOT / "scripts" / "kb_apply_prereqs.sql").read_text(encoding="utf-8")
for proposal_type in ("add_edge", "attach_evidence", "revise_strategy", "approve_claim"):
assert f"'{proposal_type}'" in prereqs
def test_approval_prereqs_bind_exact_payload_and_authenticated_principal():
prereqs = (REPO_ROOT / "scripts" / "kb_apply_prereqs.sql").read_text(encoding="utf-8").lower()
assert "p_expected_proposal_type" in prereqs
assert "p_expected_payload" in prereqs
assert "payload = p_expected_payload" in prereqs
assert "kb_review_principals" in prereqs
assert "session_user::name" in prereqs
assert "kb_proposal_approvals" in prereqs
assert "owner to kb_gate_owner" in prereqs
assert "set search_path = pg_catalog, pg_temp" in prereqs
def test_review_cli_uses_separate_role_and_secret_defaults():
args = approve.parse_args(
[
"00957f6c-9883-4015-95a4-6b09367efb0e",
"--reviewed-by", "m3ta",
"--review-note", "approved",
]
)
assert args.role == "kb_review"
assert args.secrets_file.endswith("kb-review.env")
prereqs = (REPO_ROOT / "scripts" / "kb_apply_prereqs.sql").read_text(encoding="utf-8").lower()
assert "revoke update on kb_stage.kb_proposals from kb_apply" in prereqs
assert "grant execute on function kb_stage.approve_strict_proposal" in prereqs
assert "to kb_review" in prereqs
assert "grant execute on function kb_stage.assert_approved_proposal" in prereqs
assert "grant execute on function kb_stage.finish_approved_proposal" in prereqs
assert "to kb_apply" in prereqs
def test_parse_reviewed_rows_requires_exactly_one_row(): def test_parse_reviewed_rows_requires_exactly_one_row():

View file

@ -0,0 +1,498 @@
from __future__ import annotations
import shutil
import subprocess
import time
import uuid
from collections.abc import Iterator
from pathlib import Path
import pytest
REPO_ROOT = Path(__file__).resolve().parents[1]
MIGRATION = REPO_ROOT / "scripts" / "kb_apply_prereqs.sql"
POSTGRES_IMAGE = "postgres:16-alpine"
DATABASE = "teleo_clone"
BOOTSTRAP_SQL = r"""
create role kb_gate_owner nologin inherit;
create role kb_review login inherit;
create role kb_apply login inherit;
create role broad_writer nologin;
create role rogue_owner nologin;
create role rogue_grantee nologin;
create schema kb_stage;
create table public.agents (
id uuid primary key,
handle text not null unique,
kind text not null
);
create table public.strategies (
id uuid primary key,
agent_id uuid not null,
active boolean not null default true
);
create table public.strategy_nodes (id uuid primary key);
create table public.claim_evidence (
id uuid primary key,
claim_id uuid not null,
source_id uuid not null,
role text not null
);
create table public.claim_edges (id uuid primary key);
create table public.claims (id uuid primary key);
create table public.sources (
id uuid primary key,
hash text not null
);
create table public.reasoning_tools (id uuid primary key);
insert into public.agents (id, handle, kind)
values ('11111111-1111-1111-1111-111111111111', 'm3ta', 'human');
create table kb_stage.kb_proposals (
id uuid primary key,
proposal_type text not null,
status text not null,
payload jsonb not null,
reviewed_by_handle text,
reviewed_by_agent_id uuid,
reviewed_at timestamptz,
review_note text,
applied_by_handle text,
applied_by_agent_id uuid,
applied_at timestamptz,
updated_at timestamptz not null default now()
);
create table kb_stage.kb_review_principals (
db_role name primary key,
reviewed_by_handle text not null,
reviewed_by_agent_id uuid not null references public.agents(id),
active boolean not null default true,
created_at timestamptz not null default now()
);
create table kb_stage.kb_proposal_approvals (
proposal_id uuid primary key references kb_stage.kb_proposals(id),
proposal_type text not null,
payload jsonb not null,
reviewed_by_handle text not null,
reviewed_by_agent_id uuid not null references public.agents(id),
reviewed_by_db_role name not null,
reviewed_at timestamptz not null,
review_note text not null
);
create function kb_stage.approve_strict_proposal(uuid, text, text)
returns jsonb language sql security definer
as $function$ select '{}'::jsonb $function$;
create function kb_stage.approve_strict_proposal(uuid, text, jsonb, text, text)
returns jsonb language sql security definer
as $function$ select '{}'::jsonb $function$;
create function kb_stage.assert_approved_proposal(
uuid, text, jsonb, text, uuid, timestamptz, text
) returns void language sql security definer
as $function$ select $function$;
create function kb_stage.finish_approved_proposal(
uuid, text, jsonb, text, uuid, timestamptz, text, text
) returns jsonb language sql security definer
as $function$ select '{}'::jsonb $function$;
grant all privileges on all tables in schema public
to kb_gate_owner, kb_apply, kb_review;
grant all privileges on all tables in schema kb_stage
to public, kb_gate_owner, kb_apply, kb_review;
grant all privileges on all functions in schema kb_stage
to public, kb_gate_owner, kb_apply, kb_review;
grant update (review_note) on kb_stage.kb_proposals to kb_apply;
grant select (review_note) on kb_stage.kb_proposals to public;
"""
def _psql(container: str, sql: str, *, check: bool = True) -> subprocess.CompletedProcess[str]:
completed = subprocess.run(
[
"docker",
"exec",
"-i",
container,
"psql",
"-X",
"-v",
"ON_ERROR_STOP=1",
"-U",
"postgres",
"-d",
DATABASE,
"-At",
"-F",
"|",
],
input=sql,
text=True,
capture_output=True,
check=False,
)
if check and completed.returncode != 0:
raise AssertionError(
f"psql failed with exit {completed.returncode}\nstdout:\n{completed.stdout}\nstderr:\n{completed.stderr}"
)
return completed
def _apply_migration(container: str, *, check: bool = True) -> subprocess.CompletedProcess[str]:
return _psql(container, MIGRATION.read_text(), check=check)
@pytest.fixture(scope="module")
def migrated_postgres() -> Iterator[str]:
if shutil.which("docker") is None:
pytest.skip("Docker is required for the disposable PostgreSQL migration canary")
container = f"kb-apply-prereqs-{uuid.uuid4().hex[:12]}"
started = subprocess.run(
[
"docker",
"run",
"--rm",
"-d",
"--name",
container,
"-e",
"POSTGRES_PASSWORD=postgres",
"-e",
f"POSTGRES_DB={DATABASE}",
POSTGRES_IMAGE,
],
text=True,
capture_output=True,
check=False,
)
if started.returncode != 0:
pytest.fail(f"could not start disposable PostgreSQL: {started.stderr}")
try:
for _ in range(120):
logs = subprocess.run(
["docker", "logs", container],
text=True,
capture_output=True,
check=False,
)
query = _psql(container, "select 1;", check=False)
ready_transitions = (logs.stdout + logs.stderr).count("database system is ready to accept connections")
if ready_transitions >= 2 and query.returncode == 0:
break
time.sleep(0.25)
else:
pytest.fail("disposable PostgreSQL did not become ready")
_psql(container, BOOTSTRAP_SQL)
_apply_migration(container)
yield container
finally:
subprocess.run(
["docker", "rm", "-f", container],
capture_output=True,
check=False,
)
def _lines(completed: subprocess.CompletedProcess[str]) -> set[str]:
return {line for line in completed.stdout.splitlines() if line}
def _catalog_snapshot(container: str) -> str:
return _psql(
container,
r"""
select 'role|' || rolname || '|' || rolcanlogin || '|' || rolinherit || '|' || rolsuper
from pg_catalog.pg_roles
where rolname in ('kb_gate_owner', 'kb_review', 'kb_apply')
order by rolname;
select 'owner|' || namespace.nspname || '|' || relation.relname || '|' || owner_role.rolname
from pg_catalog.pg_class relation
join pg_catalog.pg_namespace namespace on namespace.oid = relation.relnamespace
join pg_catalog.pg_roles owner_role on owner_role.oid = relation.relowner
where (namespace.nspname, relation.relname) in (
('public', 'agents'), ('public', 'strategies'), ('public', 'strategy_nodes'),
('public', 'claim_evidence'), ('public', 'claim_edges'), ('public', 'claims'),
('public', 'sources'), ('public', 'reasoning_tools'),
('kb_stage', 'kb_proposals'), ('kb_stage', 'kb_review_principals'),
('kb_stage', 'kb_proposal_approvals')
)
order by namespace.nspname, relation.relname;
select 'function|' || procedure.proname || '(' || pg_catalog.oidvectortypes(procedure.proargtypes) ||
')|' || owner_role.rolname || '|' || procedure.prosecdef || '|' || procedure.proconfig::text
from pg_catalog.pg_proc procedure
join pg_catalog.pg_namespace namespace on namespace.oid = procedure.pronamespace
join pg_catalog.pg_roles owner_role on owner_role.oid = procedure.proowner
where namespace.nspname = 'kb_stage'
and procedure.proname in (
'approve_strict_proposal', 'assert_approved_proposal', 'finish_approved_proposal'
)
order by procedure.proname, procedure.proargtypes::text;
select 'table_acl|' || namespace.nspname || '|' || relation.relname || '|' ||
case when acl.grantee = 0 then 'PUBLIC' else grantee_role.rolname end || '|' ||
pg_catalog.upper(acl.privilege_type) || '|' || acl.is_grantable
from pg_catalog.pg_class relation
join pg_catalog.pg_namespace namespace on namespace.oid = relation.relnamespace
cross join lateral pg_catalog.aclexplode(
coalesce(relation.relacl, pg_catalog.acldefault('r', relation.relowner))
) acl
left join pg_catalog.pg_roles grantee_role on grantee_role.oid = acl.grantee
where (namespace.nspname, relation.relname) in (
('public', 'agents'), ('public', 'strategies'), ('public', 'strategy_nodes'),
('public', 'claim_evidence'), ('public', 'claim_edges'), ('public', 'claims'),
('public', 'sources'), ('public', 'reasoning_tools'),
('kb_stage', 'kb_proposals'), ('kb_stage', 'kb_review_principals'),
('kb_stage', 'kb_proposal_approvals')
)
and acl.grantee <> relation.relowner
and (acl.grantee = 0 or grantee_role.rolname in ('kb_gate_owner', 'kb_review', 'kb_apply'))
order by namespace.nspname, relation.relname, grantee_role.rolname, acl.privilege_type;
select 'function_acl|' || procedure.proname || '(' ||
pg_catalog.oidvectortypes(procedure.proargtypes) || ')|' ||
case when acl.grantee = 0 then 'PUBLIC' else pg_catalog.pg_get_userbyid(acl.grantee) end ||
'|' || pg_catalog.upper(acl.privilege_type) || '|' || acl.is_grantable
from pg_catalog.pg_proc procedure
join pg_catalog.pg_namespace namespace on namespace.oid = procedure.pronamespace
cross join lateral pg_catalog.aclexplode(
coalesce(procedure.proacl, pg_catalog.acldefault('f', procedure.proowner))
) acl
where namespace.nspname = 'kb_stage'
and procedure.proname in (
'approve_strict_proposal', 'assert_approved_proposal', 'finish_approved_proposal'
)
and acl.grantee <> procedure.proowner
order by procedure.proname, procedure.proargtypes::text, acl.grantee;
""",
).stdout
def test_upgrade_normalizes_exact_role_ownership_and_acl_matrix(migrated_postgres: str) -> None:
role_rows = _lines(
_psql(
migrated_postgres,
"""
select rolname || '|' || rolcanlogin || '|' || rolinherit || '|' || rolsuper || '|' ||
rolcreatedb || '|' || rolcreaterole || '|' || rolreplication || '|' || rolbypassrls
from pg_catalog.pg_roles
where rolname in ('kb_gate_owner', 'kb_review', 'kb_apply')
order by rolname;
""",
)
)
assert role_rows == {
"kb_apply|true|false|false|false|false|false|false",
"kb_gate_owner|false|false|false|false|false|false|false",
"kb_review|true|false|false|false|false|false|false",
}
assert (
_psql(
migrated_postgres,
"""
select count(*)
from pg_catalog.pg_auth_members membership
join pg_catalog.pg_roles member_role on member_role.oid = membership.member
join pg_catalog.pg_roles granted_role on granted_role.oid = membership.roleid
where member_role.rolname in ('kb_gate_owner', 'kb_review', 'kb_apply')
or granted_role.rolname in ('kb_gate_owner', 'kb_review', 'kb_apply');
""",
).stdout.strip()
== "0"
)
schema_rows = _lines(
_psql(
migrated_postgres,
"""
select protected_role.rolname || '|' || protected_schema.nspname || '|' ||
pg_catalog.has_schema_privilege(protected_role.rolname, protected_schema.nspname, 'USAGE') || '|' ||
pg_catalog.has_schema_privilege(protected_role.rolname, protected_schema.nspname, 'CREATE')
from (values ('kb_gate_owner'), ('kb_review'), ('kb_apply')) protected_role(rolname)
cross join (values ('public'), ('kb_stage')) protected_schema(nspname);
""",
)
)
assert schema_rows == {
f"{role}|{schema}|true|false"
for role in ("kb_gate_owner", "kb_review", "kb_apply")
for schema in ("public", "kb_stage")
}
function_rows = _lines(
_psql(
migrated_postgres,
"""
select procedure.proname || '(' || pg_catalog.oidvectortypes(procedure.proargtypes) || ')|' ||
owner_role.rolname || '|' || procedure.prosecdef || '|' || procedure.proconfig::text
from pg_catalog.pg_proc procedure
join pg_catalog.pg_namespace namespace on namespace.oid = procedure.pronamespace
join pg_catalog.pg_roles owner_role on owner_role.oid = procedure.proowner
where namespace.nspname = 'kb_stage'
and procedure.proname in (
'approve_strict_proposal', 'assert_approved_proposal', 'finish_approved_proposal'
);
""",
)
)
assert function_rows == {
'approve_strict_proposal(uuid, text, jsonb, text, text)|kb_gate_owner|true|{"search_path=pg_catalog, pg_temp"}',
"assert_approved_proposal(uuid, text, jsonb, text, uuid, timestamp with time zone, text)|"
'kb_gate_owner|true|{"search_path=pg_catalog, pg_temp"}',
"finish_approved_proposal(uuid, text, jsonb, text, uuid, timestamp with time zone, text, text)|"
'kb_gate_owner|true|{"search_path=pg_catalog, pg_temp"}',
}
assert (
_psql(
migrated_postgres,
"select pg_catalog.to_regprocedure('kb_stage.approve_strict_proposal(uuid,text,text)') is null;",
).stdout.strip()
== "t"
)
assert (
_psql(
migrated_postgres,
"""
select count(*)
from pg_catalog.pg_attribute attribute
join pg_catalog.pg_class relation on relation.oid = attribute.attrelid
join pg_catalog.pg_namespace namespace on namespace.oid = relation.relnamespace
cross join lateral pg_catalog.aclexplode(attribute.attacl) acl
left join pg_catalog.pg_roles grantee_role on grantee_role.oid = acl.grantee
where (namespace.nspname, relation.relname) in (
('public', 'agents'), ('public', 'strategies'), ('public', 'strategy_nodes'),
('public', 'claim_evidence'), ('public', 'claim_edges'), ('public', 'claims'),
('public', 'sources'), ('public', 'reasoning_tools'),
('kb_stage', 'kb_proposals'), ('kb_stage', 'kb_review_principals'),
('kb_stage', 'kb_proposal_approvals')
)
and (acl.grantee = 0 or grantee_role.rolname in ('kb_gate_owner', 'kb_review', 'kb_apply'));
""",
).stdout.strip()
== "0"
)
for runtime_role, target_role in (
("kb_apply", "broad_writer"),
("kb_apply", "kb_gate_owner"),
("kb_review", "kb_gate_owner"),
):
denied = _psql(
migrated_postgres,
f"set session authorization {runtime_role}; set role {target_role};",
check=False,
)
assert denied.returncode != 0
assert "permission denied to set role" in denied.stderr
snapshot_lines = set(_catalog_snapshot(migrated_postgres).splitlines())
expected_table_acls = {
"table_acl|public|agents|kb_gate_owner|SELECT|false",
"table_acl|public|agents|kb_apply|SELECT|false",
"table_acl|public|agents|kb_review|SELECT|false",
"table_acl|public|strategies|kb_apply|SELECT|false",
"table_acl|public|strategies|kb_apply|INSERT|false",
"table_acl|public|strategies|kb_apply|UPDATE|false",
"table_acl|public|strategy_nodes|kb_apply|SELECT|false",
"table_acl|public|strategy_nodes|kb_apply|INSERT|false",
"table_acl|public|strategy_nodes|kb_apply|UPDATE|false",
"table_acl|public|claim_evidence|kb_apply|SELECT|false",
"table_acl|public|claim_evidence|kb_apply|INSERT|false",
"table_acl|public|claim_edges|kb_apply|SELECT|false",
"table_acl|public|claim_edges|kb_apply|INSERT|false",
"table_acl|public|claims|kb_apply|SELECT|false",
"table_acl|public|claims|kb_apply|INSERT|false",
"table_acl|public|sources|kb_apply|SELECT|false",
"table_acl|public|sources|kb_apply|INSERT|false",
"table_acl|public|reasoning_tools|kb_apply|SELECT|false",
"table_acl|public|reasoning_tools|kb_apply|INSERT|false",
"table_acl|kb_stage|kb_proposals|kb_gate_owner|SELECT|false",
"table_acl|kb_stage|kb_proposals|kb_gate_owner|UPDATE|false",
"table_acl|kb_stage|kb_proposals|kb_apply|SELECT|false",
"table_acl|kb_stage|kb_proposals|kb_review|SELECT|false",
}
assert {line for line in snapshot_lines if line.startswith("table_acl|")} == expected_table_acls
assert {line for line in snapshot_lines if line.startswith("function_acl|")} == {
"function_acl|approve_strict_proposal(uuid, text, jsonb, text, text)|kb_review|EXECUTE|false",
"function_acl|assert_approved_proposal(uuid, text, jsonb, text, uuid, timestamp with time zone, text)|"
"kb_apply|EXECUTE|false",
"function_acl|finish_approved_proposal(uuid, text, jsonb, text, uuid, timestamp with time zone, text, text)|"
"kb_apply|EXECUTE|false",
}
def test_migration_rerun_preserves_catalog_snapshot(migrated_postgres: str) -> None:
before = _catalog_snapshot(migrated_postgres)
_apply_migration(migrated_postgres)
assert _catalog_snapshot(migrated_postgres) == before
def _assert_migration_fails(container: str, expected_error: str) -> None:
completed = _apply_migration(container, check=False)
assert completed.returncode != 0
assert expected_error in completed.stderr
def test_migration_fails_closed_on_protected_role_membership(migrated_postgres: str) -> None:
_psql(migrated_postgres, "grant broad_writer to kb_apply;")
try:
_assert_migration_fails(migrated_postgres, "unexpected protected role membership")
finally:
_psql(migrated_postgres, "revoke broad_writer from kb_apply;")
def test_migration_fails_closed_on_ownership_drift(migrated_postgres: str) -> None:
_psql(migrated_postgres, "alter table kb_stage.kb_proposal_approvals owner to rogue_owner;")
try:
_assert_migration_fails(migrated_postgres, "unexpected protected table owner")
finally:
_psql(migrated_postgres, "alter table kb_stage.kb_proposal_approvals owner to kb_gate_owner;")
def test_migration_fails_closed_on_unexpected_table_grantee(migrated_postgres: str) -> None:
_psql(migrated_postgres, "grant select on kb_stage.kb_proposal_approvals to rogue_grantee;")
try:
_assert_migration_fails(migrated_postgres, "unexpected protected table grantee")
finally:
_psql(migrated_postgres, "revoke all on kb_stage.kb_proposal_approvals from rogue_grantee;")
def test_migration_fails_closed_on_unexpected_column_grantee(migrated_postgres: str) -> None:
_psql(
migrated_postgres,
"grant select (review_note) on kb_stage.kb_proposal_approvals to rogue_grantee;",
)
try:
_assert_migration_fails(migrated_postgres, "unexpected protected table grantee")
finally:
_psql(
migrated_postgres,
"revoke select (review_note) on kb_stage.kb_proposal_approvals from rogue_grantee;",
)
def test_migration_fails_closed_on_unexpected_function_grantee(migrated_postgres: str) -> None:
signature = "kb_stage.assert_approved_proposal(uuid,text,jsonb,text,uuid,timestamptz,text)"
_psql(migrated_postgres, f"grant execute on function {signature} to rogue_grantee;")
try:
_assert_migration_fails(migrated_postgres, "unexpected protected function grantee")
finally:
_psql(migrated_postgres, f"revoke all on function {signature} from rogue_grantee;")
def test_migration_fails_closed_on_unexpected_gate_overload(migrated_postgres: str) -> None:
_psql(
migrated_postgres,
"""
create function kb_stage.assert_approved_proposal(text)
returns void language sql security definer as $function$ select $function$;
""",
)
try:
_assert_migration_fails(migrated_postgres, "unexpected protected function overload")
finally:
_psql(migrated_postgres, "drop function kb_stage.assert_approved_proposal(text);")

View file

@ -11,7 +11,7 @@ sys.path.insert(0, str(REPO_ROOT / "scripts"))
import kb_proposal_normalize as norm # noqa: E402 import kb_proposal_normalize as norm # noqa: E402
def test_freeform_claim_split_is_blocked_until_canonical_ids_exist(): def test_rich_claim_split_stays_atomic_when_one_fragment_is_blocked():
preview = norm.normalize_proposal( preview = norm.normalize_proposal(
{ {
"id": "p-freeform", "id": "p-freeform",
@ -37,12 +37,12 @@ def test_freeform_claim_split_is_blocked_until_canonical_ids_exist():
} }
) )
assert preview["normalization_state"] == "blocked_missing_canonical_ids" assert preview["normalization_state"] == "blocked_rich_normalization"
assert preview["strict_child_count"] == 0 assert preview["strict_child_count"] == 0
assert preview["blocked_count"] == 2 assert preview["strict_child_proposals"] == []
assert preview["blocked_fragments"][0]["missing"] == ["canonical from_claim"] assert preview["blocked_count"] == 1
assert preview["blocked_fragments"][1]["missing"] == ["canonical claim_id", "canonical source_id"] assert "unsupported edge_type" in preview["blocked_fragments"][0]["reason"]
assert "Create or identify canonical claim/source rows" in preview["next_normalization_action"] assert "Resolve every blocked rich fragment" in preview["next_normalization_action"]
def test_exact_edge_fragment_emits_strict_add_edge_child(): def test_exact_edge_fragment_emits_strict_add_edge_child():
@ -129,3 +129,191 @@ def test_existing_apply_payload_is_already_strict():
assert preview["normalization_state"] == "already_strict_apply_payload" assert preview["normalization_state"] == "already_strict_apply_payload"
assert preview["strict_child_count"] == 0 assert preview["strict_child_count"] == 0
assert preview["blocked_count"] == 0 assert preview["blocked_count"] == 0
def test_rich_claim_graph_normalizes_to_one_atomic_approve_claim_bundle():
preview = norm.normalize_proposal(
{
"id": "77777777-7777-4777-8777-777777777777",
"proposal_type": "attach_evidence",
"status": "approved",
"proposed_by_agent_id": "11111111-1111-4111-8111-111111111111",
"source_ref": "telegram:test:approved",
"payload": {
"claim_candidates": [
{
"claim_key": "canonical_test_claim",
"headline": "A reviewed claim becomes canonical after approval.",
"type": "structural",
"confidence": 0.8,
"tags": ["working-leo"],
"evidence": [{"source_key": "canonical_test_source", "role": "grounds"}],
}
],
"proposed_claims": [
{
"claim_key": "second_canonical_test_claim",
"text": "A proposed_claims item is normalized too.",
"type": "normative",
}
],
"source_candidates": [
{
"source_key": "canonical_test_source",
"source_type": "paper",
"url": "https://example.test/canonical",
"key_excerpt": "Reviewed source excerpt.",
}
],
},
}
)
assert preview["normalization_state"] == "strict_children_ready"
assert preview["blocked_count"] == 0
assert preview["strict_child_count"] == 1
child = preview["strict_child_proposals"][0]
assert child["proposal_type"] == "approve_claim"
bundle = child["payload"]["apply_payload"]
assert bundle["agent_id"] == "11111111-1111-4111-8111-111111111111"
assert len(bundle["claims"]) == 2
assert len(bundle["sources"]) == 3 # reviewed source plus two proposal-body sources
assert len(bundle["evidence"]) == 3
assert bundle["edges"] == []
accounting = bundle["normalization_manifest"]["candidate_accounting"]
assert accounting["input_collections"] == {
"claim_candidates": 1,
"proposed_claims": 1,
"source_candidates": 1,
}
assert accounting["claim_candidates"]["input_count"] == 2
assert accounting["claim_candidates"]["planned_row_count"] == 2
assert accounting["source_candidates"]["input_count"] == 1
assert accounting["source_candidates"]["planned_row_count"] == 1
assert accounting["generated_claim_body_sources"]["planned_row_count"] == 2
assert accounting["total_planned_source_rows"] == 3
def test_rich_normalization_blocks_malformed_or_duplicate_candidates():
proposal = {
"id": "77777777-7777-4777-8777-777777777777",
"proposal_type": "attach_evidence",
"status": "approved",
"payload": {
"claim_candidates": [
{"claim_key": "duplicate", "text": "first"},
{"claim_key": "duplicate", "text": "second"},
{"text": "missing key"},
"not-an-object",
],
"proposed_claims": [
{"claim_key": "duplicate", "text": "duplicate across claim collections"},
{"claim_key": " ", "text": "blank key"},
42,
],
"source_candidates": [
{"source_key": "source", "source_type": "paper"},
{"source_key": "source", "source_type": "paper"},
],
},
}
preview = norm.normalize_proposal(proposal)
assert preview["normalization_state"] == "blocked_lossy_rich_normalization"
assert preview["strict_child_count"] == 0
assert preview["strict_child_proposals"] == []
reasons = " ".join(item["reason"] for item in preview["blocked_fragments"])
assert "duplicate" in reasons
assert "missing" in reasons
assert "object" in reasons
def test_rich_normalization_retains_type_mapping_manifest_for_review():
preview = norm.normalize_proposal(
{
"id": "77777777-7777-4777-8777-777777777778",
"proposal_type": "attach_evidence",
"status": "approved",
"payload": {
"claim_candidates": [
{"claim_key": "governance_claim", "text": "Governance process.", "type": "governance"},
{"claim_key": "typed_claim", "text": "Typed detail.", "type": "normative/detail"},
],
"source_candidates": [
{"source_key": "book_source", "source_type": "book", "key_excerpt": "Excerpt"},
{"source_key": "web_source", "url": "https://example.test/source"},
],
},
}
)
child = preview["strict_child_proposals"][0]
manifest = child["payload"]["apply_payload"]["normalization_manifest"]
assert {
(item["claim_key"], item["from"], item["to"])
for item in manifest["claim_type_mappings"]
} == {
("governance_claim", "governance", "meta"),
("typed_claim", "normative/detail", "normative"),
}
assert {
(item["source_key"], item["from"], item["to"])
for item in manifest["source_type_mappings"]
} == {
("book_source", "book", "other"),
("web_source", "url", "article"),
("governance_claim__proposal_body", "proposal_body", "other"),
("typed_claim__proposal_body", "proposal_body", "other"),
}
def test_rich_normalization_rejects_non_list_candidate_collection():
preview = norm.normalize_proposal(
{
"id": "77777777-7777-4777-8777-777777777779",
"proposal_type": "attach_evidence",
"payload": {"claim_candidates": {"claim_key": "not-in-a-list"}},
}
)
assert preview["normalization_state"] == "blocked_lossy_rich_normalization"
assert preview["strict_child_proposals"] == []
assert preview["blocked_fragments"][0]["location"] == "payload.claim_candidates"
assert "list of objects" in preview["blocked_fragments"][0]["reason"]
def test_rich_normalization_fails_closed_when_planner_skips_candidate(monkeypatch):
proposal = {
"id": "77777777-7777-4777-8777-777777777780",
"proposal_type": "attach_evidence",
"payload": {
"claim_candidates": [{"claim_key": "must_be_planned", "text": "Keep me."}],
"source_candidates": [{"source_key": "must_be_planned_too", "source_type": "paper"}],
},
}
lossy_plan = norm.rich_plan.build_creation_plan(proposal)
lossy_plan["planned_rows"]["claims"] = []
monkeypatch.setattr(norm.rich_plan, "build_creation_plan", lambda *_args, **_kwargs: lossy_plan)
preview = norm.normalize_proposal(proposal)
assert preview["normalization_state"] == "blocked_lossy_rich_normalization"
assert preview["strict_child_proposals"] == []
assert preview["strict_child_count"] == 0
assert any("claim rows" in item["reason"] for item in preview["blocked_fragments"])
def test_source_candidates_without_claims_are_not_silently_dropped():
preview = norm.normalize_proposal(
{
"id": "77777777-7777-4777-8777-777777777781",
"proposal_type": "attach_evidence",
"payload": {
"source_candidates": [
{"source_key": "orphan_source", "source_type": "paper", "key_excerpt": "Keep me."}
]
},
}
)
assert preview["normalization_state"] == "blocked_lossy_rich_normalization"
assert preview["strict_child_proposals"] == []
assert "without at least one claim candidate" in preview["blocked_fragments"][0]["reason"]

View file

@ -82,13 +82,10 @@ def test_api_kb_proposals_returns_review_packets_and_counts():
assert data["review_state_counts"]["approved_needs_apply_payload"] == 1 assert data["review_state_counts"]["approved_needs_apply_payload"] == 1
assert data["review_state_counts"]["approved_applyable"] == 1 assert data["review_state_counts"]["approved_applyable"] == 1
assert data["packets"][0]["identity_and_authorization"]["source_ref"] == "telegram:leo:m3taversal:claim-review" assert data["packets"][0]["identity_and_authorization"]["source_ref"] == "telegram:leo:m3taversal:claim-review"
assert data["packets"][0]["normalization_preview"]["normalization_state"] == "blocked_missing_canonical_ids" assert data["packets"][0]["normalization_preview"]["normalization_state"] == "blocked_lossy_rich_normalization"
assert data["packets"][1]["normalization_preview"]["normalization_state"] == "already_strict_apply_payload" assert data["packets"][1]["normalization_preview"]["normalization_state"] == "already_strict_apply_payload"
assert data["packets"][0]["apply_preview"]["state"] == "not_applyable_yet" assert data["packets"][0]["apply_preview"]["state"] == "not_applyable_yet"
assert data["packets"][0]["apply_preview"]["blocked_fragments"][0]["missing"] == [ assert data["packets"][0]["apply_preview"]["blocked_fragments"][0]["missing"] == ["claim_key"]
"canonical from_claim",
"canonical to_claim",
]
assert data["packets"][1]["apply_preview"]["state"] == "strict_apply_payload_ready" assert data["packets"][1]["apply_preview"]["state"] == "strict_apply_payload_ready"
assert data["packets"][1]["apply_preview"]["rows"] == [ assert data["packets"][1]["apply_preview"]["rows"] == [
{ {
@ -128,13 +125,13 @@ def test_page_renders_applyability_and_next_admin_action():
assert "claim-row normalization" in html assert "claim-row normalization" in html
assert "normalize into one or more strict apply_payload" in html assert "normalize into one or more strict apply_payload" in html
assert "Normalization" in html assert "Normalization" in html
assert "blocked_missing_canonical_ids" in html assert "blocked_lossy_rich_normalization" in html
assert 'href="/kb/claims/d0000000-0000-0000-0000-000000000005"' in html assert 'href="/kb/claims/d0000000-0000-0000-0000-000000000005"' in html
assert "<code>d0000000-0000-0000-0000-000000000005</code>" in html assert "<code>d0000000-0000-0000-0000-000000000005</code>" in html
assert "Apply preview" in html assert "Apply preview" in html
assert "not_applyable_yet" in html assert "not_applyable_yet" in html
assert "No canonical row preview is executable yet." in html assert "No canonical row preview is executable yet." in html
assert "canonical from_claim" in html assert "claim_key" in html
def test_page_renders_strict_apply_payload_claim_links(): def test_page_renders_strict_apply_payload_claim_links():

View file

@ -38,11 +38,7 @@ def test_dns_gate_uses_static_config_and_redacts_token_stdout(tmp_path: Path):
"secret-token-should-not-leak", "secret-token-should-not-leak",
"Failed to resolve 'oauth2.googleapis.com' ([Errno 8] nodename nor servname provided)", "Failed to resolve 'oauth2.googleapis.com' ([Errno 8] nodename nor servname provided)",
) )
return probe.CommandResult( return probe.CommandResult(1, "", "Project readback unavailable after token failure")
1,
"",
"HTTPSConnectionPool(host='oauth2.googleapis.com'): Failed to resolve 'oauth2.googleapis.com'",
)
result = probe.probe_gcp_parity( result = probe.probe_gcp_parity(
gcloud_path=Path("/fake/gcloud"), gcloud_path=Path("/fake/gcloud"),
@ -58,10 +54,38 @@ def test_dns_gate_uses_static_config_and_redacts_token_stdout(tmp_path: Path):
assert result["local_gcloud"]["static_active_config"]["account"] == "billy@livingip.xyz" assert result["local_gcloud"]["static_active_config"]["account"] == "billy@livingip.xyz"
assert result["local_gcloud"]["static_active_config"]["project"] == "teleo-501523" assert result["local_gcloud"]["static_active_config"]["project"] == "teleo-501523"
assert result["accounts_tested"][0]["token_refresh"]["stdout_redacted"] is True assert result["accounts_tested"][0]["token_refresh"]["stdout_redacted"] is True
assert probe.classify_failure(result["accounts_tested"]) == "dns_unavailable"
assert "secret-token" not in json.dumps(result) assert "secret-token" not in json.dumps(result)
assert "oauth2.googleapis.com" in result["clear_CTA"] assert "oauth2.googleapis.com" in result["clear_CTA"]
def test_reauth_url_hostname_is_not_classified_as_dns(tmp_path: Path):
config_dir = tmp_path / "gcloud"
write_gcloud_config(config_dir)
def fake_runner(command: list[str], _env: dict[str, str], _timeout: int) -> probe.CommandResult:
if command[1:3] == ["auth", "list"]:
return probe.CommandResult(0, json.dumps([{"account": "billy@livingip.xyz", "status": "ACTIVE"}]), "")
if command[1:3] == ["auth", "print-access-token"]:
return probe.CommandResult(
1,
"",
"Reauthentication failed. Visit https://oauth2.googleapis.com/token or run gcloud auth login.",
)
return probe.CommandResult(1, "", "Project readback unavailable after token failure")
result = probe.probe_gcp_parity(
gcloud_path=Path("/fake/gcloud"),
source_config=config_dir,
runner=fake_runner,
use_temp_config=False,
)
assert probe.classify_failure(result["accounts_tested"]) == "reauth_required"
assert result["status"] == "blocked_on_gcloud_reauth"
assert "gcloud auth login" in result["clear_CTA"]
def test_ready_status_requires_project_vm_and_cloudsql_readbacks(tmp_path: Path): def test_ready_status_requires_project_vm_and_cloudsql_readbacks(tmp_path: Path):
config_dir = tmp_path / "gcloud" config_dir = tmp_path / "gcloud"
write_gcloud_config(config_dir) write_gcloud_config(config_dir)

View file

@ -0,0 +1,91 @@
from __future__ import annotations
import json
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
REPORT_DIR = ROOT / "docs" / "reports" / "leo-working-state-20260709"
MANIFEST = REPORT_DIR / "skill-pack-manifest.json"
def _skill(name: str) -> str:
return (ROOT / ".agents" / "skills" / name / "SKILL.md").read_text(
encoding="utf-8"
)
def test_manifest_indexes_valid_repo_native_skills() -> None:
manifest = json.loads(MANIFEST.read_text(encoding="utf-8"))
assert manifest["status"] == "repo_native_validated"
assert manifest["repo_skill_root"] == ".agents/skills"
assert manifest["validation"] == "tests/test_repo_skill_pack.py"
assert "37/37" in manifest["claim_ceiling"]
for entry in manifest["skills"]:
path = ROOT / entry["path"]
text = path.read_text(encoding="utf-8")
assert path.is_file()
assert text.startswith("---\n")
assert f"name: {entry['name']}\n" in text.split("---\n", 2)[1]
assert "description:" in text.split("---\n", 2)[1]
for relative in manifest["reference_files"]:
assert (ROOT / relative).is_file(), relative
def test_db_change_skill_tracks_guarded_v2_runtime_proof() -> None:
text = _skill("teleo-kb-db-change-workflow")
for required in (
"`approve_claim` v2",
"kb_review",
"kb_apply",
"kb_gate_owner",
"NOINHERIT",
"37/37",
"exact table deltas",
"source hashes",
"trusted operator-only canonical writer",
"run_approve_claim_isolated_container_canary.sh",
):
assert required in text
assert "does not prove byte-for-byte live content" in text
assert "production permission migration" in text
def test_gcp_skill_uses_current_cloud_sql_truth_and_exact_gate() -> None:
text = _skill("teleo-gcp-parity-ops")
squashed = " ".join(text.split())
for required in (
"teleo-pgvector-standby",
"PostgreSQL `16.14`",
"teleo-staging-net",
"public IP disabled",
"Cloud SQL Studio",
"GCP Studio authenticated",
"begin transaction read only",
"DB contents",
):
assert required in text
assert "no enabled existing" in squashed
assert "IAM DB principal" in squashed
assert "Do not submit a raw password" in text
assert "AppleScript" in text
def test_vps_onboarding_and_cory_skills_point_to_current_proof() -> None:
vps = _skill("teleo-vps-runtime-ops")
onboarding = _skill("teleo-leo-onboarding")
cory = _skill("working-leo-cory-outcomes")
assert "reasoning tools `17`" in vps
assert "current source hashes" in vps
assert "gcp-cloud-sql-t3-live-readonly-current.md" in onboarding
assert "GCP Studio authenticated" not in onboarding
assert "37/37" in cory
assert "Approved is not the same as applied" in cory
assert "Next Cory-style follow-up:" in cory