Merge remote-tracking branch 'origin/main' into codex/leo-gcp-runtime-security-20260715

This commit is contained in:
twentyOne2x 2026-07-15 04:40:36 +02:00
commit d299f1cdf5
53 changed files with 6628 additions and 634 deletions

View file

@ -50,7 +50,7 @@ Name the canary before sending:
## Message Discipline ## Message Discipline
Use unique markers such as `WL-LIVE-TG-CORY-YYYYMMDD-Tn`. Use unique markers such as `WL-LIVE-TG-M3TAVERSAL-YYYYMMDD-Tn`.
Ask Leo for machine-checkable reply markers like: Ask Leo for machine-checkable reply markers like:
@ -74,10 +74,14 @@ After each live Telegram canary:
## Current Known Proof ## Current Known Proof
- Live memory and KB audit passed for marker `WL-LIVE-TG-CORY-20260709`. - Live memory and KB audit are retained in
- Live staged write passed for `WL-LIVE-TG-CORY-20260709-T3`. `docs/reports/leo-working-state-20260709/telegram-live-canary-current.json`.
- Readback passed for `WL-LIVE-TG-CORY-20260709-T4`. - Live staged-write proof is retained in
- Open-ended m3taversal-style read-only triage passed for the legacy marker `docs/reports/leo-working-state-20260709/telegram-live-db-write-canary-20260709.json`.
`WL-LIVE-TG-CORY-OPEN-20260709`. - Open-ended m3taversal-standard read-only triage is retained in
`docs/reports/leo-working-state-20260709/telegram-live-open-ended-suite-current.md`.
Historical receipts contain immutable legacy marker IDs. Treat them only as
evidence identifiers; never reuse them as a participant name or future marker.
Use `docs/reports/leo-working-state-20260709/current-truth-index.md` for artifact paths. Use `docs/reports/leo-working-state-20260709/current-truth-index.md` for artifact paths.

View file

@ -25,7 +25,7 @@ Prefer deterministic local surfaces before asking an LLM:
- generated claim indexes from `lib/claim_index.py`; - generated claim indexes from `lib/claim_index.py`;
- search helpers in `lib/search.py`; - search helpers in `lib/search.py`;
- copied SQLite state through `teleo-db-operator`; - copied SQLite state through `teleo-db-operator`;
- retained proof JSON in `.crabbox-results/` or `proof/`. - retained proof JSON at a caller-specified generated-output path.
Read outputs must include file paths, source paths, claim/entity IDs when available, and the exact query used. Read outputs must include file paths, source paths, claim/entity IDs when available, and the exact query used.
@ -80,7 +80,7 @@ If a runtime cannot implement one of these, record the missing tool as a blocker
## Expected Artifact ## Expected Artifact
Write `.crabbox-results/kb-interop-proof.json` or a caller-specified proof path containing: Write `<proof-output>/kb-interop-proof.json` containing:
- runtime name; - runtime name;
- model/provider if known; - model/provider if known;

View file

@ -28,9 +28,10 @@ Create or update:
- `AGENTS.md`: scope, repo boundaries, proof requirements; - `AGENTS.md`: scope, repo boundaries, proof requirements;
- `SOUL.md`: Rio or Theseus identity; - `SOUL.md`: Rio or Theseus identity;
- `TOOLS.md`: bounded tools only; - `TOOLS.md`: bounded tools only;
- `skills/decision-engine-refinement/SKILL.md`; - `<openclaw-workspace>/skills/decision-engine-refinement/SKILL.md`;
- `skills/living-ip-kb-interop/SKILL.md`; - `<openclaw-workspace>/skills/living-ip-kb-interop/SKILL.md`;
- `skills/teleo-db-operator/SKILL.md` only for read-only local copies unless explicitly authorized. - `<openclaw-workspace>/skills/teleo-db-operator/SKILL.md` only for read-only
local copies unless explicitly authorized.
## Tool Policy ## Tool Policy

View file

@ -83,6 +83,22 @@ Do not describe a passing Hermes memory sync as canonical KB parity.
- Persistent GCP `teleo_canonical` remains the older staging copy measured at - Persistent GCP `teleo_canonical` remains the older staging copy measured at
`52,164` rows and `26` proposals. It has not been promoted or cut over. `52,164` rows and `26` proposals. It has not been promoted or cut over.
## Open Least-Privilege Candidate
PR #148, `Scope GCP Leo runtime to least-privilege Cloud SQL access`, is open as
of the 2026-07-15 skill-pack reconciliation. It proposes scoped runtime roles,
secret access, fail-closed Cloud SQL behavior, deployment rollback, and positive
plus negative permission checks. Those branch files and proposed live outcomes
are candidate evidence only. Before using them, run:
```bash
gh pr view 148 --json state,mergedAt,mergeCommit,headRefName,url
```
Until the PR is merged and its runtime receipt passes, use only paths present on
canonical `main`, keep persistent GCP classified as staging, do not promote it,
and do not infer least-privilege cutover from a branch or dry run.
Primary retained proof: Primary retained proof:
- `docs/reports/leo-working-state-20260709/gcp-db-first-working-leo-20260714.md` - `docs/reports/leo-working-state-20260709/gcp-db-first-working-leo-20260714.md`

View file

@ -26,11 +26,13 @@ Prevent incorrect assumptions about where Leo/Teleo code, runtime state, DB stat
- Hermes/leoclean runtime profile. - Hermes/leoclean runtime profile.
- Postgres canonical DB. - Postgres canonical DB.
- `kb_stage` proposal ledger. - `kb_stage` proposal ledger.
- Retained proof artifacts under local `outputs/`. - Caller-specific external output directories, which are retained evidence but
are not repository routes unless copied into the repo evidence pack.
- Synced report artifacts under the VPS profile report directory. - Synced report artifacts under the VPS profile report directory.
## Current Known Path Map ## Current Known Path Map
- Canonical code/deployment repository: GitHub `living-ip/teleo-infrastructure`.
- Repo-local evidence pack: `docs/reports/leo-working-state-20260709/` - Repo-local evidence pack: `docs/reports/leo-working-state-20260709/`
- VPS deploy/source area: `/opt/teleo-eval/workspaces/deploy-infra` - VPS deploy/source area: `/opt/teleo-eval/workspaces/deploy-infra`
- VPS deploy stamp: `/opt/teleo-eval/.last-deploy-sha` - VPS deploy stamp: `/opt/teleo-eval/.last-deploy-sha`

View file

@ -44,7 +44,7 @@ Run the deterministic source-to-proposal canary:
```bash ```bash
.venv/bin/python scripts/run_leo_local_ingestion_proposal_canary.py \ .venv/bin/python scripts/run_leo_local_ingestion_proposal_canary.py \
--fixture fixtures/working-leo/document-ingestion-v1.json \ --fixture fixtures/working-leo/document-ingestion-v1.json \
--output outputs/leo-local-ingestion-proposal-canary-current.json --output /tmp/leo-local-ingestion-proposal-canary-current.json
``` ```
Require the source, claim, evidence, and proposal UUIDs to link exactly; claim Require the source, claim, evidence, and proposal UUIDs to link exactly; claim
@ -131,7 +131,8 @@ Read:
- `docs/reports/leo-working-state-20260709/approve-claim-clone-canary-current.json` - `docs/reports/leo-working-state-20260709/approve-claim-clone-canary-current.json`
- `docs/reports/leo-working-state-20260709/leo-source-composition-clone-checkpoint-current.json` - `docs/reports/leo-working-state-20260709/leo-source-composition-clone-checkpoint-current.json`
- `outputs/leo-local-ingestion-proposal-canary-current.json` - `docs/reports/leo-working-state-20260709/leo-v3-document-source-lifecycle-current.md`
- `docs/reports/leo-working-state-20260709/source-document-compiler-canary-20260713.md`
## Claim Ceiling ## Claim Ceiling

View file

@ -1,13 +1,15 @@
--- ---
name: teleo-leo-onboarding name: teleo-leo-onboarding
description: Use when a worker needs fast context on Teleo/Living IP, Leo, the product architecture, current VPS/GCP split, and the July 9 working-state evidence before doing Leo, Teleo, KB, Telegram, VPS, or GCP work. description: Use when a worker needs fast context on Teleo/Living IP, Leo architecture, VPS/GCP and Hermes runtime boundaries, database provenance, ingestion/apply/reconstruction, identity, testing, security, secrets, or incident recovery before doing Leo or Teleo work.
--- ---
# Teleo / Leo Onboarding # Teleo / Leo Onboarding
## Job ## Job
Orient the worker before action. Build a current, proof-linked understanding of the company/product, Leo's role, the infrastructure surfaces, and the exact claim ceiling. Orient the worker before action. Build a current, proof-linked understanding of
the company/product, Leo's role, the infrastructure surfaces, and the exact
claim ceiling without rediscovering the system from historical chat.
## Trigger Phrases ## Trigger Phrases
@ -17,6 +19,8 @@ Orient the worker before action. Build a current, proof-linked understanding of
- "Fable handoff for Leo" - "Fable handoff for Leo"
- "before working on Teleo infra" - "before working on Teleo infra"
- "explain the architecture" - "explain the architecture"
- "recover or reconstruct Leo"
- "which skill should I use"
## Core Model ## Core Model
@ -46,6 +50,52 @@ Orient the worker before action. Build a current, proof-linked understanding of
- `SOUL.md` is a rendered/runtime artifact. Direct file edits are not canonical - `SOUL.md` is a rendered/runtime artifact. Direct file edits are not canonical
identity changes without DB row and render/sync proof. identity changes without DB row and render/sync proof.
## Clean-Context First Commands
From the repository root, validate the pack without a virtual environment or
network access:
```bash
.agents/skills/teleo-leo-onboarding/scripts/validate_skill_pack.py --root .
```
Install exactly the manifest-indexed skills into an empty temporary agent skill
root with:
```bash
.agents/skills/teleo-leo-onboarding/scripts/install_skill_pack.py \
--root . \
--target /private/tmp/teleo-clean-agent/skills
```
Installed skills still resolve repository-relative routes against the current
`teleo-infrastructure` checkout. Keep the agent working directory at the repo
root. For pytest, use `.venv/bin/python`; if `.venv` is absent, create it from
`README.md` before running tests. Do not guess a bare `python` command.
Run the deterministic isolated acceptance canary with:
```bash
.agents/skills/teleo-leo-onboarding/scripts/run_clean_context_canary.py \
--root . \
--output docs/reports/leo-working-state-20260709/skill-pack-clean-context-canary-current.json
```
## Task Router
| Area | Skill | Canonical first route |
|---|---|---|
| Company, product, architecture | `teleo-leo-onboarding` | `docs/reports/leo-working-state-20260709/current-truth-index.md` |
| VPS runtime and incidents | `teleo-vps-runtime-ops` | Fresh service, deploy, DB, and cleanup readbacks |
| GCP and Cloud SQL | `teleo-gcp-parity-ops` | Current main evidence; keep open PR #148 candidate-only |
| Hermes packaging/runtime | `nousresearch-hermes-agent` | `hermes-agent/` plus the live service-specific ops skill |
| Database provenance | `teleo-infra-provenance` and `teleo-db-operator` | Separate Git, runtime, SQLite, Postgres, and retained proof |
| Source ingestion and proposal/apply | `teleo-kb-db-change-workflow` | Hash-bound source, review, guarded apply, receipt |
| Reconstruction and recovery | `teleo-reconstruction-recovery` | `docs/kb-rebuild-and-recompile.md` |
| Identity and rendered soul | `working-leo-m3taversal-outcomes` | Canonical identity rows before `SOUL.md` |
| Testing and proof tiers | `teleo-proof-handoff` | Exact tier, command, receipt, cleanup, claim ceiling |
| Secrets | `private-password-storage` | Provider login first; never paste or retrieve a secret |
## Read First ## Read First
From the repo root, read: From the repo root, read:
@ -61,6 +111,7 @@ From the repo root, read:
9. `.agents/skills/working-leo-m3taversal-outcomes/SKILL.md` 9. `.agents/skills/working-leo-m3taversal-outcomes/SKILL.md`
10. `docs/reports/leo-working-state-20260709/approve-claim-clone-canary-current.md` 10. `docs/reports/leo-working-state-20260709/approve-claim-clone-canary-current.md`
11. `docs/reports/leo-working-state-20260709/gcp-cloud-sql-t3-live-readonly-current.md` 11. `docs/reports/leo-working-state-20260709/gcp-cloud-sql-t3-live-readonly-current.md`
12. `docs/kb-rebuild-and-recompile.md`
## Required Status Split ## Required Status Split
@ -82,6 +133,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.
- PRs #146 and #147 are merged repository truth. PR #148 remains an open GCP
least-privilege candidate until live `gh pr view 148` readback says otherwise;
never route a deployment from its branch as though it were `main`.
- Use `ssh -o BatchMode=yes teleo-gcp-staging true` to preflight passwordless - Use `ssh -o BatchMode=yes teleo-gcp-staging true` to preflight passwordless
GCP VM access. If the firewall route drifts, rediscover the authenticated GCP VM access. If the firewall route drifts, rediscover the authenticated
non-secret route before declaring a blocker. The OIDC/IAP workflow is still non-secret route before declaring a blocker. The OIDC/IAP workflow is still

View file

@ -0,0 +1,106 @@
#!/usr/bin/env python3
"""Install the manifest-indexed Leo/Teleo skills into an empty skill root."""
from __future__ import annotations
import argparse
import json
import re
import shutil
import subprocess
import sys
import tempfile
from pathlib import Path
DEFAULT_MANIFEST = Path("docs/reports/leo-working-state-20260709/skill-pack-manifest.json")
SKILL_NAME = re.compile(r"[a-z0-9-]{1,64}")
def _tracked_skill_files(root: Path, skill_dir: Path) -> list[Path]:
relative_dir = skill_dir.relative_to(root)
result = subprocess.run(
["git", "-C", str(root), "ls-files", "-z", "--", str(relative_dir)],
check=False,
capture_output=True,
)
if result.returncode != 0:
raise RuntimeError(result.stderr.decode("utf-8", errors="replace"))
files = [root / Path(item.decode("utf-8")) for item in result.stdout.split(b"\0") if item]
if not files:
raise ValueError(f"skill has no tracked files: {relative_dir}")
for source in files:
if source.is_symlink():
raise ValueError(f"skill contains a symlink: {source.relative_to(root)}")
if not source.is_file():
raise ValueError(f"skill route is not a regular file: {source.relative_to(root)}")
source.resolve().relative_to(skill_dir.resolve())
return files
def install(root: Path, manifest_path: Path, target: Path) -> dict[str, object]:
manifest_file = root / manifest_path
manifest = json.loads(manifest_file.read_text(encoding="utf-8"))
skills = manifest["skills"]
if target.exists():
raise ValueError(f"target already exists: {target}")
target.parent.mkdir(parents=True, exist_ok=True)
stage = Path(tempfile.mkdtemp(prefix=".teleo-skill-pack-", dir=target.parent))
installed: list[str] = []
try:
for entry in skills:
name = entry["name"]
if not isinstance(name, str) or SKILL_NAME.fullmatch(name) is None:
raise ValueError(f"invalid skill name: {name!r}")
expected_path = Path(".agents") / "skills" / name / "SKILL.md"
if Path(entry["path"]) != expected_path:
raise ValueError(f"skill path must be {expected_path}: {entry['path']!r}")
if name in installed:
raise ValueError(f"duplicate skill name: {name}")
source = root / expected_path.parent
source.resolve().relative_to((root / ".agents" / "skills").resolve())
if not source.is_dir():
raise FileNotFoundError(source)
destination = stage / name
destination.resolve().relative_to(stage.resolve())
for tracked_source in _tracked_skill_files(root, source):
relative = tracked_source.relative_to(source)
tracked_destination = destination / relative
tracked_destination.resolve().relative_to(destination.resolve())
tracked_destination.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(tracked_source, tracked_destination)
installed.append(name)
stage.rename(target)
except Exception:
shutil.rmtree(stage, ignore_errors=True)
raise
return {
"artifact": "leo_teleo_skill_pack_install",
"status": "pass",
"installed_skill_count": len(installed),
"installed_skills": installed,
"contains_secrets": False,
"production_mutation_authorized": False,
}
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--root", type=Path, default=Path.cwd())
parser.add_argument("--manifest", type=Path, default=DEFAULT_MANIFEST)
parser.add_argument("--target", type=Path, required=True)
args = parser.parse_args()
try:
result = install(args.root.resolve(), args.manifest, args.target.resolve())
except Exception as exc:
result = {"artifact": "leo_teleo_skill_pack_install", "status": "fail", "error": str(exc)}
sys.stdout.write(json.dumps(result, indent=2, sort_keys=True) + "\n")
return 1
sys.stdout.write(json.dumps(result, indent=2, sort_keys=True) + "\n")
return 0
if __name__ == "__main__":
raise SystemExit(main())

View file

@ -0,0 +1,172 @@
#!/usr/bin/env python3
"""Run the deterministic T2 isolated-install acceptance canary for the skill pack."""
from __future__ import annotations
import argparse
import json
import subprocess
import sys
import tempfile
from pathlib import Path
DEFAULT_MANIFEST = Path("docs/reports/leo-working-state-20260709/skill-pack-manifest.json")
SEMANTIC_CONTRACT = {
"authority": (
"teleo-infra-provenance",
"Canonical code/deployment repository: GitHub `living-ip/teleo-infrastructure`.",
"GitHub living-ip/teleo-infrastructure",
),
"vps_database": (
"teleo-leo-onboarding",
"VPS canonical KB: Docker Postgres `teleo-pg`, database `teleo`.",
"teleo-pg / teleo",
),
"gcp_database": (
"teleo-leo-onboarding",
"GCP canonical KB: private Cloud SQL database `teleo_canonical`.",
"private Cloud SQL teleo_canonical; persistent copy remains staging",
),
"live_service": (
"teleo-infra-provenance",
"Live service: `leoclean-gateway.service`",
"leoclean-gateway.service",
),
"merged_reconstruction": (
"teleo-reconstruction-recovery",
"PR #146 is merged",
"PR #146 merged deterministic genesis-plus-ledger reconstruction",
),
"merged_reasoning_verifier": (
"teleo-reconstruction-recovery",
"PR #147 is merged",
"PR #147 merged unseen-reasoning verifier hardening",
),
"candidate_only_gcp": (
"teleo-reconstruction-recovery",
"PR #148 is a separate, open GCP least-privilege candidate",
"PR #148 open candidate only; not canonical main",
),
"next_safe_action": (
"teleo-leo-onboarding",
".agents/skills/teleo-leo-onboarding/scripts/validate_skill_pack.py --root .",
"run the repo-local path validator before any operational action",
),
}
def _run(command: list[str]) -> subprocess.CompletedProcess[str]:
return subprocess.run(command, check=False, capture_output=True, text=True)
def _contains_contract(text: str, required_text: str) -> bool:
"""Compare prose semantically across ordinary Markdown line wrapping."""
return " ".join(required_text.split()) in " ".join(text.split())
def run_canary(root: Path, manifest_path: Path) -> dict[str, object]:
manifest = json.loads((root / manifest_path).read_text(encoding="utf-8"))
installer = root / manifest["installer"]
selected_skills = sorted({contract[0] for contract in SEMANTIC_CONTRACT.values()})
result: dict[str, object]
with tempfile.TemporaryDirectory(prefix="teleo-clean-context-") as temporary_text:
temporary = Path(temporary_text)
installed_root = temporary / "skills"
install_process = _run(
[str(installer), "--root", str(root), "--manifest", str(manifest_path), "--target", str(installed_root)]
)
try:
install_receipt = json.loads(install_process.stdout)
except json.JSONDecodeError:
install_receipt = {"status": "fail", "error": install_process.stdout or install_process.stderr}
semantic_checks: dict[str, bool] = {}
answers: dict[str, str] = {}
evidence_paths: dict[str, str] = {}
for key, (skill_name, required_text, answer) in SEMANTIC_CONTRACT.items():
installed_skill = installed_root / skill_name / "SKILL.md"
text = installed_skill.read_text(encoding="utf-8") if installed_skill.is_file() else ""
semantic_checks[key] = _contains_contract(text, required_text)
answers[key] = answer
evidence_paths[key] = f".agents/skills/{skill_name}/SKILL.md"
installed_validator = installed_root / "teleo-leo-onboarding" / "scripts" / "validate_skill_pack.py"
validator_process = _run([str(installed_validator), "--root", str(root), "--manifest", str(manifest_path)])
try:
validator_receipt = json.loads(validator_process.stdout)
except json.JSONDecodeError:
validator_receipt = {"status": "fail", "error": validator_process.stdout or validator_process.stderr}
problems: list[dict[str, str]] = []
if install_process.returncode != 0 or install_receipt.get("status") != "pass":
problems.append({"kind": "install_failed", "detail": str(install_receipt.get("error", "unknown"))})
if validator_process.returncode != 0 or validator_receipt.get("status") != "pass":
problems.append({"kind": "path_validation_failed", "detail": str(validator_receipt.get("problems", []))})
for key, passed in semantic_checks.items():
if not passed:
problems.append({"kind": "semantic_route_missing", "detail": key})
passed = not problems
result = {
"artifact": "leo_teleo_skill_pack_isolated_install_canary",
"status": "pass" if passed else "fail",
"required_tier": "T2_runtime",
"current_tier": "T2_runtime" if passed else "T1_model",
"proof_scope": "deterministic_isolated_install_and_route_validation",
"fresh_temporary_skill_root": True,
"ambient_skill_root_used": False,
"agent_reasoning_exercised": False,
"installed_skill_count": install_receipt.get("installed_skill_count", 0),
"selected_installed_skills": selected_skills,
"semantic_checks": semantic_checks,
"expected_answers": answers,
"evidence_paths": evidence_paths,
"canary_command": ".agents/skills/teleo-leo-onboarding/scripts/validate_skill_pack.py --root .",
"canary_exit_status": validator_process.returncode,
"canary_receipt": validator_receipt,
"missing_to_reach_required_tier": [] if passed else problems,
"strongest_claim_allowed": (
"T2 deterministic isolated local skill install, route-contract, and path validation only; this artifact "
"does not claim an agent reasoning run, and retained product proofs keep their own VPS, GCP-staging, "
"isolated-clone, Telegram, and production claim ceilings"
),
"contains_secrets": False,
"production_mutation_authorized": False,
"external_runtime_contacted": False,
"problems": problems,
}
result["cleanup_readback"] = {
"temporary_skill_root_removed": not Path(temporary_text).exists(),
"orphan_processes_started": False,
}
if not result["cleanup_readback"]["temporary_skill_root_removed"]:
result["status"] = "fail"
result["current_tier"] = "T1_model"
result["problems"].append({"kind": "cleanup_failed", "detail": "temporary skill root remains"})
return result
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--root", type=Path, default=Path.cwd())
parser.add_argument("--manifest", type=Path, default=DEFAULT_MANIFEST)
parser.add_argument("--output", type=Path)
args = parser.parse_args()
root = args.root.resolve()
result = run_canary(root, args.manifest)
payload = json.dumps(result, indent=2, sort_keys=True) + "\n"
if args.output:
output = args.output if args.output.is_absolute() else root / args.output
output.parent.mkdir(parents=True, exist_ok=True)
output.write_text(payload, encoding="utf-8")
sys.stdout.write(payload)
return 0 if result["status"] == "pass" else 1
if __name__ == "__main__":
raise SystemExit(main())

View file

@ -0,0 +1,248 @@
#!/usr/bin/env python3
"""Validate the repo-native Leo/Teleo skill pack and its local routes."""
from __future__ import annotations
import argparse
import hashlib
import json
import os
import re
import shlex
import subprocess
import sys
from pathlib import Path
DEFAULT_MANIFEST = Path("docs/reports/leo-working-state-20260709/skill-pack-manifest.json")
REPO_PREFIXES = (
".agents/",
".github/",
"deploy/",
"docs/",
"fixtures/",
"hermes-agent/",
"lib/",
"ops/",
"outputs/",
"schemas/",
"scripts/",
"systemd/",
"tests/",
)
PATH_SUFFIXES = (".json", ".jsonl", ".md", ".py", ".sh", ".sql", ".yaml", ".yml")
CODE_SPAN = re.compile(r"`([^`\n]+)`")
MARKDOWN_LINK = re.compile(r"\[[^\]]+\]\(([^)]+)\)")
FENCED_SHELL = re.compile(r"```(?:bash|sh|shell)\n(.*?)```", re.DOTALL)
def _frontmatter(text: str) -> dict[str, str]:
if not text.startswith("---\n"):
return {}
try:
block = text.split("---\n", 2)[1]
except IndexError:
return {}
values: dict[str, str] = {}
for line in block.splitlines():
if ":" not in line:
continue
key, value = line.split(":", 1)
values[key.strip()] = value.strip().strip('"')
return values
def _tracked_paths(root: Path) -> set[Path]:
result = subprocess.run(
["git", "-C", str(root), "ls-files", "-z"],
check=False,
capture_output=True,
)
if result.returncode != 0:
raise RuntimeError(result.stderr.decode("utf-8", errors="replace"))
return {Path(item.decode("utf-8")) for item in result.stdout.split(b"\0") if item}
def _is_tracked(root: Path, path: Path, tracked: set[Path]) -> bool:
relative = path.resolve().relative_to(root.resolve())
if path.is_file():
return relative in tracked
prefix = f"{relative.as_posix().rstrip('/')}/"
return any(item.as_posix().startswith(prefix) for item in tracked)
def _clean_token(token: str) -> str:
token = token.strip().strip("'\"")
token = token.rstrip(",;.)]")
token = re.sub(r":\d+$", "", token)
return token
def _candidate_tokens(span: str) -> list[str]:
try:
tokens = shlex.split(span)
except ValueError:
tokens = span.split()
return [_clean_token(token) for token in tokens]
def _resolve_candidate(root: Path, source: Path, token: str) -> Path | None:
if not token or token in {".md", ".json", ".py", ".sh", ".sql"}:
return None
if any(marker in token for marker in ("*", "<", ">", "...", "${", "$LEDGER")):
return None
if token.startswith(("http://", "https://", "/", "~/")):
return None
if token.startswith(REPO_PREFIXES) or token in {"README.md", "CODEOWNERS"}:
candidate = root / token
elif token.endswith(PATH_SUFFIXES) and "/" in token:
candidate = source.parent / token
else:
return None
try:
candidate.resolve().relative_to(root.resolve())
except ValueError:
return None
return candidate
def _inline_paths(root: Path, source: Path) -> set[Path]:
text = source.read_text(encoding="utf-8")
candidates: set[Path] = set()
spans = CODE_SPAN.findall(text) + MARKDOWN_LINK.findall(text)
for block in FENCED_SHELL.findall(text):
spans.extend(line for line in block.splitlines() if line.strip())
for span in spans:
for token in _candidate_tokens(span):
candidate = _resolve_candidate(root, source, token)
if candidate is not None:
candidates.add(candidate)
return candidates
def validate(root: Path, manifest_path: Path) -> dict[str, object]:
problems: list[dict[str, str]] = []
checked: set[Path] = set()
manifest_file = root / manifest_path
if not manifest_file.is_file():
return {
"artifact": "leo_teleo_skill_pack_path_validation",
"status": "fail",
"problems": [{"kind": "missing_manifest", "path": str(manifest_path)}],
}
manifest = json.loads(manifest_file.read_text(encoding="utf-8"))
tracked = _tracked_paths(root)
if not _is_tracked(root, manifest_file, tracked):
problems.append({"kind": "untracked_manifest", "path": str(manifest_path)})
skills = manifest.get("skills", [])
skill_names = {entry.get("name") for entry in skills}
for entry in skills:
relative = Path(entry["path"])
path = root / relative
checked.add(path)
if not path.is_file():
problems.append({"kind": "missing_skill", "path": str(relative)})
continue
if not _is_tracked(root, path, tracked):
problems.append({"kind": "untracked_skill", "path": str(relative)})
metadata = _frontmatter(path.read_text(encoding="utf-8"))
if metadata.get("name") != entry.get("name"):
problems.append({"kind": "skill_name_mismatch", "path": str(relative)})
if not metadata.get("description"):
problems.append({"kind": "missing_skill_description", "path": str(relative)})
if set(metadata) != {"name", "description"}:
problems.append({"kind": "invalid_skill_frontmatter_keys", "path": str(relative)})
if not re.fullmatch(r"[a-z0-9-]{1,64}", metadata.get("name", "")):
problems.append({"kind": "invalid_skill_name", "path": str(relative)})
if relative.parent.name != metadata.get("name"):
problems.append({"kind": "skill_folder_name_mismatch", "path": str(relative)})
required_coverage = manifest.get("required_coverage", {})
for area, owners in required_coverage.items():
for owner in owners:
if owner not in skill_names:
problems.append({"kind": "unknown_coverage_owner", "path": f"{area}:{owner}"})
reference_paths = list(manifest.get("reference_files", []))
command_paths = list(manifest.get("command_files", []))
scan_paths = list(manifest.get("scan_files", []))
for relative_text in reference_paths + command_paths + scan_paths:
relative = Path(relative_text)
path = root / relative
checked.add(path)
if not path.is_file():
problems.append({"kind": "missing_manifest_path", "path": str(relative)})
elif not _is_tracked(root, path, tracked):
problems.append({"kind": "untracked_manifest_path", "path": str(relative)})
for relative_text in manifest.get("executable_files", []):
relative = Path(relative_text)
path = root / relative
checked.add(path)
if not path.is_file():
problems.append({"kind": "missing_executable", "path": str(relative)})
elif not os.access(path, os.X_OK):
problems.append({"kind": "not_executable", "path": str(relative)})
scan_sources = [root / entry["path"] for entry in skills]
scan_sources += [root / relative for relative in manifest.get("scan_files", [])]
for source in scan_sources:
if not source.is_file():
continue
for candidate in _inline_paths(root, source):
checked.add(candidate)
if not candidate.exists():
problems.append(
{
"kind": "missing_inline_path",
"path": str(candidate.resolve().relative_to(root.resolve())),
"source": str(source.relative_to(root)),
}
)
elif not _is_tracked(root, candidate, tracked):
problems.append(
{
"kind": "untracked_inline_path",
"path": str(candidate.resolve().relative_to(root.resolve())),
"source": str(source.relative_to(root)),
}
)
relative_paths = sorted(str(path.resolve().relative_to(root.resolve())) for path in checked)
digest = hashlib.sha256("\n".join(relative_paths).encode("utf-8")).hexdigest()
return {
"artifact": "leo_teleo_skill_pack_path_validation",
"required_tier": "T2_runtime",
"status": "pass" if not problems else "fail",
"manifest": str(manifest_path),
"skill_count": len(skills),
"coverage_area_count": len(required_coverage),
"checked_path_count": len(relative_paths),
"checked_paths_sha256": digest,
"contains_secrets": False,
"production_mutation_authorized": False,
"problems": problems,
}
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--root", type=Path, default=Path.cwd())
parser.add_argument("--manifest", type=Path, default=DEFAULT_MANIFEST)
parser.add_argument("--output", type=Path)
args = parser.parse_args()
root = args.root.resolve()
result = validate(root, args.manifest)
payload = json.dumps(result, indent=2, sort_keys=True) + "\n"
if args.output:
output = args.output if args.output.is_absolute() else root / args.output
output.parent.mkdir(parents=True, exist_ok=True)
output.write_text(payload, encoding="utf-8")
sys.stdout.write(payload)
return 0 if result["status"] == "pass" else 1
if __name__ == "__main__":
raise SystemExit(main())

View file

@ -0,0 +1,83 @@
---
name: teleo-reconstruction-recovery
description: Use when restoring, rebuilding, recompiling, or incident-recovering Leo's Teleo knowledge database, including snapshot recovery, genesis-plus-ledger replay, source recompilation, parity verification, and honest recovery claim ceilings.
---
# Teleo Reconstruction Recovery
## Job
Choose the smallest recovery path that restores a usable Leo knowledge system
without confusing snapshot restoration, strict ledger replay, and semantic
source recompilation.
## Authority
Read `docs/kb-rebuild-and-recompile.md` first. It is the canonical recovery
contract. Use `docs/reports/leo-working-state-20260709/current-truth-index.md`
only for the newest retained proof point, and refresh live state before making a
current VPS or GCP claim.
PR #146 is merged and owns the deterministic genesis-plus-ledger reconstruction
slice in `ops/run_local_genesis_ledger_rebuild.py`. PR #147 is merged and owns
the current unseen-reasoning verifier in
`scripts/verify_leo_unseen_reasoning_chain.py`. PR #148 is a separate, open GCP
least-privilege candidate; do not treat its branch files or proposed live state
as canonical `main` recovery instructions until it merges.
## Select The Recovery Mode
| Need | Path | Honest result |
|---|---|---|
| Restore the latest verified state quickly | `ops/run_local_canonical_postgres_rebuild.py` | Exact snapshot recovery |
| Prove strict post-genesis applies replay exactly | `ops/run_local_genesis_ledger_rebuild.py` | Isolated insert-only ledger replay |
| Turn one retained source into a reviewed packet | `scripts/compile_kb_source_packet.py` | Deterministic proposal packet, not canonical knowledge |
| Rebuild every row from original sources | Follow the semantic recompilation contract in `docs/kb-rebuild-and-recompile.md` | Partial until every row and receipt is accounted for |
| Diagnose a live incident before recovery | Use `.agents/skills/teleo-vps-runtime-ops/SKILL.md` and `.agents/skills/teleo-infra-provenance/SKILL.md` | Fresh read-only incident map |
## Safe Order
1. Identify the intended source database, snapshot, manifest, commit, and hashes.
2. Keep private dumps, row payloads, source excerpts, and replay material outside
the repository and mode `0600`.
3. Run the selected path in an isolated, network-disabled local container.
4. Verify schema, constraints, roles, counts, row hashes, key queries, and exact
cleanup.
5. Run the relevant focused tests, then the unseen-reasoning verifier when the
restored state is meant to support Leo answers.
6. Separate local reconstruction from VPS restore, GCP restore, service restart,
Telegram delivery, promotion, and production apply. Each is its own proof row.
## Commands
Use the repository virtual environment documented in `README.md`:
```bash
.venv/bin/python ops/run_local_canonical_postgres_rebuild.py --help
.venv/bin/python ops/run_local_genesis_ledger_rebuild.py --help
.venv/bin/python scripts/compile_kb_source_packet.py --help
.venv/bin/python -m pytest -q \
tests/test_run_local_canonical_postgres_rebuild.py \
tests/test_run_local_genesis_ledger_rebuild.py \
tests/test_verify_leo_unseen_reasoning_chain.py
```
The help and focused-test commands are safe local canaries. A real recovery
requires caller-supplied private material and must retain a sanitized receipt
without the material itself.
## Stop Conditions
Stop before any live restore or restart when the source snapshot is not
hash-bound, the source authority is ambiguous, parity fails, private material
would enter Git or logs, cleanup cannot be proved, or the requested operation
would promote GCP or mutate production without exact authorization.
## Claim Ceiling
- Exact snapshot recovery is working when its parity receipt passes.
- The merged genesis-plus-ledger path proves an isolated insert-only slice.
- Semantic source-to-blank-database recompilation remains incomplete until every
canonical row traces to a genesis record or reviewed replay receipt.
- No local receipt proves a live VPS/GCP restore, service restart, Telegram
delivery, production apply, or GCP promotion.

View file

@ -0,0 +1,4 @@
interface:
display_name: "Teleo Reconstruction Recovery"
short_description: "Recover and reconstruct Leo knowledge safely"
default_prompt: "Use $teleo-reconstruction-recovery to choose and verify the safest Leo database recovery path."

View file

@ -62,11 +62,16 @@ A working Leo is a Telegram-facing agent that:
## Current Benchmark Cases ## Current Benchmark Cases
- Marker memory: `WL-LIVE-TG-CORY-20260709`. - Historical marker-memory receipt:
`docs/reports/leo-working-state-20260709/telegram-live-canary-current.json`.
- Truth correction: `approved != applied`. - Truth correction: `approved != applied`.
- Applied canary: proposal `00957f6c-9883-4015-95a4-6b09367efb0e` and edge `c167933e-d513-4f43-9335-d5d8aeb259f2`. - Applied canary: proposal `00957f6c-9883-4015-95a4-6b09367efb0e` and edge `c167933e-d513-4f43-9335-d5d8aeb259f2`.
- 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 receipt:
`docs/reports/leo-working-state-20260709/telegram-live-open-ended-suite-current.md`,
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 - Guarded apply proof: both generic and real Helmer v3 receipts pass `37/37` in
disposable PostgreSQL; production Helmer remains unapplied. disposable PostgreSQL; production Helmer remains unapplied.

View file

@ -0,0 +1,208 @@
name: gcp-observatory-read-adapter
on:
workflow_dispatch:
inputs:
action:
description: Build only, or deploy the protected staging service and run live receipts
required: true
default: build_only
type: choice
options:
- build_only
- deploy_staging
permissions:
contents: read
id-token: write
concurrency:
group: gcp-observatory-read-adapter-${{ github.ref }}
cancel-in-progress: false
env:
PROJECT_ID: teleo-501523
REGION: europe-west6
ZONE: europe-west6-a
REPOSITORY: teleo
IMAGE_NAME: observatory-read-adapter
SERVICE_NAME: observatory-read-adapter-staging
RUNTIME_SERVICE_ACCOUNT: sa-observatory-read-adapter@teleo-501523.iam.gserviceaccount.com
DB_IAM_USER: sa-observatory-read-adapter@teleo-501523.iam
API_KEY_SECRET: observatory-read-api-key-staging
WORKLOAD_IDENTITY_PROVIDER: projects/785938879453/locations/global/workloadIdentityPools/github-actions/providers/living-ip-github
DEPLOY_SERVICE_ACCOUNT: sa-artifact-builder@teleo-501523.iam.gserviceaccount.com
jobs:
build:
name: Test, build, and push adapter image
runs-on: ubuntu-latest
timeout-minutes: 20
outputs:
image_ref: ${{ steps.image.outputs.image_ref }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
cache: pip
- name: Run focused adapter tests
run: |
python -m pip install -e '.[dev]'
python -m pytest tests/test_observatory_read_adapter.py -q
python -m ruff check observatory_read_adapter tests/test_observatory_read_adapter.py
- id: auth
uses: google-github-actions/auth@v3
with:
workload_identity_provider: ${{ env.WORKLOAD_IDENTITY_PROVIDER }}
service_account: ${{ env.DEPLOY_SERVICE_ACCOUNT }}
- uses: google-github-actions/setup-gcloud@v3
with:
project_id: ${{ env.PROJECT_ID }}
- name: Configure Artifact Registry
run: gcloud auth configure-docker "${REGION}-docker.pkg.dev" --quiet
- id: image
name: Build, smoke, and push immutable image
shell: bash
run: |
set -euo pipefail
tag="${GITHUB_SHA::12}"
image="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPOSITORY}/${IMAGE_NAME}:${tag}"
digest="$(gcloud artifacts docker images describe "${image}" \
--project="${PROJECT_ID}" \
--format='value(image_summary.digest)' 2>/dev/null || true)"
if [[ -n "${digest}" ]]; then
docker pull "${image}"
else
docker build -f Dockerfile.observatory-read-adapter -t "${image}" .
fi
docker run --rm --env PYTHONPYCACHEPREFIX=/tmp/pycache \
"${image}" python -m compileall -q /app/observatory_read_adapter
docker run --rm "${image}" python -c 'import observatory_read_adapter; print("adapter-import-ok")'
if [[ -z "${digest}" ]]; then
docker push "${image}" | tee docker-push.log
digest="$(awk '/digest: sha256:/ {print $3}' docker-push.log | tail -1)"
fi
test -n "${digest}"
image_ref="${image}@${digest}"
printf 'image_ref=%s\n' "${image_ref}" >> "${GITHUB_OUTPUT}"
printf 'revision=%s\nimage_ref=%s\n' "${GITHUB_SHA}" "${image_ref}" > observatory-adapter-image.txt
- uses: actions/upload-artifact@v4
with:
name: observatory-adapter-image
path: observatory-adapter-image.txt
if-no-files-found: error
deploy-staging:
name: Deploy and prove staging read path
if: ${{ inputs.action == 'deploy_staging' }}
needs: build
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- id: auth
uses: google-github-actions/auth@v3
with:
workload_identity_provider: ${{ env.WORKLOAD_IDENTITY_PROVIDER }}
service_account: ${{ env.DEPLOY_SERVICE_ACCOUNT }}
- uses: google-github-actions/setup-gcloud@v3
with:
project_id: ${{ env.PROJECT_ID }}
- name: Deploy bounded GCP staging service
shell: bash
env:
IMAGE_REF: ${{ needs.build.outputs.image_ref }}
run: |
set -euo pipefail
gcloud run deploy "${SERVICE_NAME}" \
--project="${PROJECT_ID}" \
--region="${REGION}" \
--image="${IMAGE_REF}" \
--execution-environment=gen2 \
--service-account="${RUNTIME_SERVICE_ACCOUNT}" \
--network=teleo-staging-net \
--subnet=teleo-staging-europe-west6 \
--vpc-egress=private-ranges-only \
--ingress=all \
--allow-unauthenticated \
--port=8080 \
--cpu=1 \
--memory=512Mi \
--concurrency=8 \
--max-instances=2 \
--timeout=15 \
--set-secrets="OBSERVATORY_API_KEY=${API_KEY_SECRET}:latest" \
--set-env-vars="GCP_PROJECT_ID=${PROJECT_ID},CLOUD_SQL_INSTANCE=${PROJECT_ID}:${REGION}:teleo-pgvector-standby,DB_NAME=teleo_canonical,DB_IAM_USER=${DB_IAM_USER},DB_AUTHORIZATION_ROLE=kb_observatory_read,SERVICE_REVISION=${GITHUB_SHA}" \
--labels="livingip-tier=gcp-staging,livingip-surface=observatory-read-adapter" \
--quiet
- name: Capture positive and negative live receipts
shell: bash
run: |
set -euo pipefail
service_url="$(gcloud run services describe "${SERVICE_NAME}" --project="${PROJECT_ID}" --region="${REGION}" --format='value(status.url)')"
revision="$(gcloud run services describe "${SERVICE_NAME}" --project="${PROJECT_ID}" --region="${REGION}" --format='value(status.latestReadyRevisionName)')"
api_key="$(gcloud secrets versions access latest --secret="${API_KEY_SECRET}" --project="${PROJECT_ID}")"
test -n "${service_url}"
test -n "${revision}"
test -n "${api_key}"
curl --fail --silent --show-error \
--header "X-Api-Key: ${api_key}" \
"${service_url}/v1/claims/sample" > positive.json
jq -e '
.schema == "livingip.observatory-canonical-claim.v1"
and .read_only == true
and .provenance.database == "teleo_canonical"
and .provenance.authorization_role == "kb_observatory_read"
and .provenance.transaction_read_only == true
and .provenance.write_privileges_denied == true
and (.canonical.evidence | length) > 0
and .proposal_ledger.distinct_from_canonical == true
' positive.json >/dev/null
anonymous_status="$(curl --silent --output anonymous.json --write-out '%{http_code}' "${service_url}/v1/claims/sample")"
write_status="$(curl --silent --output write.json --write-out '%{http_code}' \
--request POST --header "X-Api-Key: ${api_key}" --header 'Content-Type: application/json' \
--data '{"status":"applied"}' "${service_url}/v1/claims/sample")"
test "${anonymous_status}" = 401
test "${write_status}" = 405
jq -n \
--arg service_url "${service_url}" \
--arg revision "${revision}" \
--arg git_sha "${GITHUB_SHA}" \
--argjson positive "$(cat positive.json)" \
--arg anonymous_status "${anonymous_status}" \
--arg write_status "${write_status}" \
'{
schema: "livingip.observatory-read-adapter-live-receipt.v1",
required_tier: "T3_live_readonly",
service_url: $service_url,
revision: $revision,
git_sha: $git_sha,
positive: $positive,
negative: {
anonymous_http_status: ($anonymous_status | tonumber),
authenticated_post_http_status: ($write_status | tonumber)
},
production_repoint_executed: false
}' > observatory-read-adapter-live-receipt.json
unset api_key
- uses: actions/upload-artifact@v4
with:
name: observatory-read-adapter-live-receipt
path: observatory-read-adapter-live-receipt.json
if-no-files-found: error

View file

@ -0,0 +1,20 @@
FROM python:3.11-slim
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
ENV PORT=8080
WORKDIR /app
COPY pyproject.toml README.md /app/
COPY observatory_read_adapter /app/observatory_read_adapter
COPY lib /app/lib
RUN pip install --no-cache-dir --upgrade pip \
&& pip install --no-cache-dir ".[observatory]" \
&& addgroup --system --gid 10001 adapter \
&& adduser --system --uid 10001 --ingroup adapter --no-create-home adapter
USER 10001:10001
CMD ["python", "-m", "observatory_read_adapter"]

View file

@ -84,8 +84,8 @@ A mirror sync (every 2 minutes) fast-forwards the PR onto Forgejo, where
the pipeline picks it up. From there it's the same flow as agent-authored the pipeline picks it up. From there it's the same flow as agent-authored
PRs — same tiers, same reviewers, same merge rules. PRs — same tiers, same reviewers, same merge rules.
The contributor-facing guide lives in The contributor-facing guide is the
[`teleo-codex/CONTRIBUTING.md`](https://github.com/living-ip/teleo-codex/blob/main/CONTRIBUTING.md). [Teleo Codex contributing guide](https://github.com/living-ip/teleo-codex/blob/main/CONTRIBUTING.md).
## Repository layout ## Repository layout
@ -119,10 +119,26 @@ status report in their Pentagon profile.
## Development ## Development
```bash ```bash
pip install -e ".[dev]" python3 -m venv .venv
pytest .venv/bin/pip install -e ".[dev]"
.venv/bin/python -m pytest
``` ```
## Leo / Teleo Operator Onboarding
Start with the repo-native skill router, not historical chat or a runtime mirror:
```bash
.agents/skills/teleo-leo-onboarding/scripts/validate_skill_pack.py --root .
```
Then read `.agents/skills/teleo-leo-onboarding/SKILL.md` and
`docs/reports/leo-working-state-20260709/current-truth-index.md`. Recovery work
starts at `docs/kb-rebuild-and-recompile.md`. GitHub
`living-ip/teleo-infrastructure` is canonical code/deployment truth; VPS/GCP
runtime, canonical Postgres, proposal staging, Hermes memory, and retained
proofs are separate state surfaces.
## Operations ## Operations
Production deployment runs on a single VPS. Runbook, restart procedures, Production deployment runs on a single VPS. Runbook, restart procedures,

View file

@ -25,11 +25,12 @@ Run:
--output /tmp/teleo-canonical-rebuild-receipt.json --output /tmp/teleo-canonical-rebuild-receipt.json
``` ```
The newest retained 2026-07-14 post-V3 canary restored a fresh, network-isolated The retained 2026-07-14 post-V3 canary restored a fresh, network-isolated
Postgres and then removed it. The restored target matched the source across all Postgres and then removed it. The restored target matched the source across all
39 manifest tables and all 52,167 rows, with no schema, data, constraint, role, 39 manifest tables and all 52,167 rows, with no schema, data, constraint, role,
or performance mismatch. Total runtime was 10.56 seconds, including 2.71 or performance mismatch. The same source snapshot was subsequently restored to
seconds for `pg_restore`. Key rows included: a disposable private-TLS GCP Cloud SQL clone with exact parity, a bounded
no-send reasoning turn, and verified cleanup. Key rows included:
- 1,837 claims; - 1,837 claims;
- 4,145 sources; - 4,145 sources;
@ -38,9 +39,10 @@ seconds for `pg_restore`. Key rows included:
- 17 reasoning tools; - 17 reasoning tools;
- 29 proposals. - 29 proposals.
This snapshot includes the completed V3 source canary. It is ready as the This snapshot includes the completed V3 source canary. Disposable GCP restore
current restore input, but it has not yet been restored to GCP; current GCP parity is proven at that retained point. Persistent GCP `teleo_canonical`
parity therefore remains unproven. remains the older staging copy, so ongoing parity, promotion, and production
cutover remain unproven.
This is the fastest disaster-recovery path. It does not require Leo to This is the fastest disaster-recovery path. It does not require Leo to
re-extract or relearn the corpus. re-extract or relearn the corpus.
@ -134,7 +136,7 @@ kept all database counts and the proposal payload hash unchanged, left the Leo
gateway on the same PID with zero restarts, and removed the temporary private gateway on the same PID with zero restarts, and removed the temporary private
receipt. The full receipt is deliberately not committed because it can contain receipt. The full receipt is deliberately not committed because it can contain
claim bodies or source excerpts; the sanitized proof is retained as claim bodies or source excerpts; the sanitized proof is retained as
`reports/leo-working-state-20260709/kb-apply-replay-receipt-current.json`. `docs/reports/leo-working-state-20260709/kb-apply-replay-receipt-current.json`.
Normal applies mark the SQL hash as `exact_executed_sql`. A later Normal applies mark the SQL hash as `exact_executed_sql`. A later
`--receipt-only` recovery marks it as `reconstructed_current_engine`; it never `--receipt-only` recovery marks it as `reconstructed_current_engine`; it never
@ -146,7 +148,7 @@ not retain every column of the proposal ledger, so exact reconstruction also
needs the full approved proposal row, immutable approval snapshot, and final needs the full approved proposal row, immutable approval snapshot, and final
applied proposal row. applied proposal row.
## Genesis Plus Strict Ledger: Working Insert-Only Slice ## Genesis Plus Strict Ledger: Working Deterministic Slice
`ops/run_local_genesis_ledger_rebuild.py` now executes the first exact `ops/run_local_genesis_ledger_rebuild.py` now executes the first exact
genesis-plus-ledger slice in one command: genesis-plus-ledger slice in one command:
@ -204,27 +206,67 @@ Each referenced material object has exact top-level keys
must contain every current `kb_stage.kb_proposals` column; partial envelopes must contain every current `kb_stage.kb_proposals` column; partial envelopes
are rejected. are rejected.
The command verifies every hash before starting Docker, restores a tmpfs-backed The command verifies every hash before starting Docker, requires a
Postgres with `--network none`, reapplies the current guarded prerequisites, SHA-256-pinned Postgres image (and defaults to a pinned PostgreSQL 16 Alpine
and proves genesis parity. For each entry it seeds the receipt's exact row IDs multi-platform digest), restores it on tmpfs with `--network none`, reapplies
and timestamps in the disposable clone, executes the existing `kb_apply` the current guarded prerequisites, and proves genesis parity. Insert-only
payload-bound guard and ledger transition, checks exact proposal and canonical entries seed the receipt's exact canonical row IDs and timestamps before the
row readbacks, then verifies the complete final parity manifest. Its public existing `kb_apply` payload-bound guard executes. For `revise_strategy`, the
runner seeds only the proposal and approval, captures the target agent's
prestate, executes the real guarded transition, validates the generated delta,
and then replaces only those generated rows with the receipt-pinned IDs and
timestamps. Every path checks exact proposal and canonical row readbacks before
verifying the complete final parity manifest. Its public
mode-`0600` receipt contains hashes, IDs, types, counts, parity summaries, and mode-`0600` receipt contains hashes, IDs, types, counts, parity summaries, and
cleanup proof, but no payloads, rows, SQL, source paths, or command errors. cleanup proof, but no payloads, rows, SQL, source paths, or command errors.
The legacy `seed_exact` summary is the insert-only aggregate of
`proposal_seed_exact` and `canonical_seed_exact`; it is intentionally false for
successful mutating entries, which instead report
`mutating_delta_validated` and `mutating_poststate_normalized`.
Fresh guard bootstrap rows use a fixed baseline timestamp rather than wall
clock time, so repeated clean restores have identical row hashes.
The exact v1 claim ceiling is intentionally narrow: The exact v1 claim ceiling is intentionally bounded:
- `add_edge`, `attach_evidence`, and `approve_claim` strict receipts execute; - `add_edge`, `attach_evidence`, `approve_claim`, and `revise_strategy` strict
receipts execute;
- sequence gaps, hash drift, engine drift, duplicate proposals, and legacy or - sequence gaps, hash drift, engine drift, duplicate proposals, and legacy or
freeform payloads fail before container start; freeform payloads fail before container start;
- `revise_strategy` fails closed because its current receipt omits the prior - `revise_strategy` is accepted only when the receipt pins the exact SQL that
strategies and nodes that the apply engine deactivates or retires; matches the current guarded apply engine. Immediately before each entry, the
runner captures the target agent's strategy/node IDs, active strategy,
non-retired nodes, and maximum version. It then validates the generated
post-minus-pre delta, requires `version = previous maximum + 1`, and replaces
only the generated strategy/node rows with the exact receipt rows;
- the original transaction timestamp is derived from the fresh strategy
`created_at` and must equal every fresh node's `created_at` and `updated_at`.
It must also fall within the immutable, timezone-aware interval
`reviewed_at <= transaction timestamp <= applied_at`.
Only node IDs observed as non-retired before apply receive that timestamp;
already-retired, unrelated-agent, and shared NULL-agent rows stay untouched;
- generated nodes must have no anchors before normalization, preventing a
delete-and-reinsert step from silently cascading future trigger-created rows;
- full proposal before/after rows are mandatory because the current receipt - full proposal before/after rows are mandatory because the current receipt
envelope does not retain proposal origin fields or exact `updated_at`; envelope does not retain proposal origin fields or exact `updated_at`;
- this proves only an isolated local reconstruction. It does not touch or prove - this proves only an isolated local reconstruction. It does not touch or prove
VPS, GCP, Telegram, a live database, or blank-schema source recompilation. VPS, GCP, Telegram, a live database, or blank-schema source recompilation.
The transition contract for `revise_strategy` is final-state deterministic, not
a claim that the v1 receipt independently contains a historical before-image:
```text
exact genesis/pre-entry state
+ exact current/original guarded SQL
+ receipt-pinned fresh strategy and nodes
-> prior active strategy inactive
-> exactly the prior non-retired nodes retired at the original transaction time
-> one receipt-exact active strategy and receipt-exact node set
```
The genesis and final manifests remain mandatory oracles. Any incorrect
prestate, unrelated-row mutation, missing/extra generated row, semantic drift,
version drift, hash drift, or final rowset difference fails the reconstruction.
The source compiler now turns one raw artifact, its strict UTF-8 extraction, The source compiler now turns one raw artifact, its strict UTF-8 extraction,
and a reviewed extraction manifest into a deterministic, hash-bound and a reviewed extraction manifest into a deterministic, hash-bound
`pending_review` proposal bundle: `pending_review` proposal bundle:
@ -270,10 +312,9 @@ neither command applies canonical rows.
The existing full-data clone canary separately proves that a reviewed packet The existing full-data clone canary separately proves that a reviewed packet
can create source, claim, evidence, and edge rows and affect later reasoning. can create source, claim, evidence, and edge rows and affect later reasoning.
The remaining reconstruction work is to retain complete deltas for mutating The remaining reconstruction work is to backfill or explicitly reject legacy
contracts such as `revise_strategy`, backfill or explicitly reject legacy freeform applies and extend beyond genesis recovery to a blank-schema source
freeform applies, and extend beyond genesis recovery to a blank-schema source compiler. The strict ledger runner does not prove that every historical
compiler. The insert-only ledger runner does not prove that every historical
canonical row can be rebuilt semantically from retained sources. canonical row can be rebuilt semantically from retained sources.
## Definition Of Working ## Definition Of Working

View file

@ -0,0 +1,102 @@
# GCP Observatory Read Adapter
## Definition of Working
- Working target: an authenticated GCP staging API returns one canonical claim
with source/evidence provenance and a separately labeled proposal ledger.
- Operator path: `GET /v1/claims/sample` or `GET /v1/claims/{uuid}` with the
server-only `X-Api-Key` value.
- Done means: the response names `teleo_canonical`, the dedicated IAM database
principal and `kb_observatory_read`, reports a read-only transaction and
denied write privileges, and live anonymous/write HTTP attempts return
`401`/`405`.
- Not done: the VPS `teleo-pg`, a public Cloud SQL address, a browser-visible
key, a mock-only receipt, or a production Vercel repoint.
- Required tier: `T3_live_readonly`.
## Existing Connector Boundary
The current `living-ip/livingip-web` main branch reads claims through
`src/lib/api.ts`. It selects `NEXT_PUBLIC_API_URL` and otherwise falls back to
`http://77.42.65.182:8081`, then calls the legacy `/api/claims` routes. That is
the VPS-local/public Observatory path. It is not safe to replace that public
environment variable with this protected API: the response shape differs and
the adapter key must never enter a `NEXT_PUBLIC_` variable or browser bundle.
The existing Teleo route `GET /api/kb/claims` is the compatibility reference
for route-scoped authentication, but its default database/container settings
still describe the VPS-local process. This service is a separate GCP runtime;
it does not change Argus or PR #148's Leo runtime files.
## Runtime Contract
- Cloud Run service: `observatory-read-adapter-staging` in `europe-west6`.
- Direct VPC egress: `teleo-staging-net` / `teleo-staging-europe-west6`.
- Cloud SQL: `teleo-501523:europe-west6:teleo-pgvector-standby`, private IP.
- Database: `teleo_canonical` only.
- Runtime identity: `sa-observatory-read-adapter@teleo-501523.iam.gserviceaccount.com`.
- IAM database user: `sa-observatory-read-adapter@teleo-501523.iam`.
- Database authorization role: `kb_observatory_read` (`NOLOGIN`).
- API authentication: route-scoped key from Secret Manager secret
`observatory-read-api-key-staging`; the value is not in Git or deploy logs.
- No mutation routes exist. Authenticated unsupported methods return `405`.
The Cloud SQL Python Connector uses Application Default Credentials from the
Cloud Run service identity, automatic IAM database authentication, and private
IP. Every request starts a read-only transaction and refuses the response if
the database, IAM principal, authorization membership, required reads, or
effective write-denial checks drift.
## One-Time Staging Bootstrap
These are staging mutations. Run them only with an authenticated GCP operator;
they do not route production traffic.
1. Create the dedicated runtime service account and grant only
`roles/cloudsql.client` and `roles/cloudsql.instanceUser` in project
`teleo-501523`.
2. Add the service account as a Cloud SQL IAM service-account user on
`teleo-pgvector-standby`.
3. From the existing private GCP VM database-admin path, run
`ops/observatory_read_role.sql` against `teleo_canonical` with
`OBSERVATORY_DB_IAM_USER=sa-observatory-read-adapter@teleo-501523.iam`.
Do not read, copy, or retain the administrator password.
4. Create `observatory-read-api-key-staging`, add a generated 32-byte-or-longer
value through stdin, and grant that secret's accessor role only to the
runtime service account and the bounded staging canary identity.
5. Dispatch `.github/workflows/gcp-observatory-read-adapter.yml` with
`action=deploy_staging` from the exact reviewed branch revision.
The workflow builds an immutable image, deploys at most two Cloud Run
instances, and retains the positive response plus anonymous `401` and
authenticated POST `405` receipts. It does not modify Vercel.
## Unexecuted Cutover Packet
Production cutover requires a separate exact authorization and a
`living-ip/livingip-web` change. Do not execute these steps from this lane.
1. Add server-only Vercel secrets `OBSERVATORY_CANONICAL_API_URL` and
`OBSERVATORY_CANONICAL_API_KEY` to a protected preview environment. The URL
targets the GCP service; neither setting may use `NEXT_PUBLIC_`.
2. Add a Next.js server route or server action that calls the GCP API, validates
`livingip.observatory-canonical-claim.v1`, and maps it to the Observatory UI.
The browser calls only the same-origin Next.js route.
3. Prove preview Deployment Protection, anonymous denial, source/evidence
rendering, proposal/live labels, and no browser key exposure.
4. Obtain exact production-repoint authorization naming the reviewed
`livingip-web` commit and Vercel project before changing production values.
## Unexecuted Rollback Packet
1. Revert the authorized `livingip-web` connector commit or disable its
server-side feature flag.
2. Restore the prior production Vercel values and redeploy the last accepted
production revision.
3. Verify `/claims` again uses the prior `/api/claims` contract.
4. Leave the private Cloud SQL instance and adapter role intact while receipts
are reviewed; scale the staging Cloud Run service to zero if isolation is
required.
5. Delete the staging service/role/secret only under a separate cleanup
decision. Rollback never adds a Cloud SQL public IP and never redirects the
adapter to VPS `teleo-pg`.

View file

@ -2,6 +2,30 @@
Use this file before making status claims. Prefer fresh VPS/GCP readbacks when cheap; this directory is the retained July 9 evidence snapshot committed to the Teleo infrastructure repo. Use this file before making status claims. Prefer fresh VPS/GCP readbacks when cheap; this directory is the retained July 9 evidence snapshot committed to the Teleo infrastructure repo.
## Repository And Skill Routing Addendum - 2026-07-15
- PRs #146 and #147 are merged into canonical `main`. The reconstruction source
of truth is `docs/kb-rebuild-and-recompile.md`; the merged deterministic paths
are `ops/run_local_genesis_ledger_rebuild.py`,
`scripts/leo_turn_execution_manifest.py`, and
`scripts/verify_leo_unseen_reasoning_chain.py`.
- PR #148 remains open candidate evidence for least-privilege GCP Cloud SQL
runtime access. Do not use its branch-only runbook, deployment script, role
migration, or proposed live outcome as canonical `main` until the PR is merged
and its retained runtime receipt passes.
- The repo-native onboarding entry point is
`.agents/skills/teleo-leo-onboarding/SKILL.md`. Its standard-library validator
is `.agents/skills/teleo-leo-onboarding/scripts/validate_skill_pack.py`; run it
before relying on any skill route from a clean context.
- The deterministic isolated install/routing/cleanup receipt is
`docs/reports/leo-working-state-20260709/skill-pack-clean-context-canary-current.json`.
- The separate no-history agent onboarding receipt is
`docs/reports/leo-working-state-20260709/skill-pack-clean-context-agent-review-current.json`.
- Exact snapshot restoration is working. The merged genesis-plus-ledger path is
an isolated insert-only recovery slice. Full semantic recompilation from every
original source remains incomplete. None of these local tiers proves a live
VPS/GCP restore, Telegram delivery, production apply, or GCP promotion.
## GCP Database-First Addendum - 2026-07-14 10:07 UTC ## GCP Database-First Addendum - 2026-07-14 10:07 UTC
This addendum supersedes the GCP-pending statements in the earlier July 14 This addendum supersedes the GCP-pending statements in the earlier July 14
@ -135,8 +159,8 @@ sections below override this newer status.
- VPS canonical KB: claims `1837`, sources `4145`, evidence `4670`, edges - VPS canonical KB: claims `1837`, sources `4145`, evidence `4670`, edges
`4916`, proposals `26`; proposal states are applied `2`, approved `3`, `4916`, proposals `26`; proposal states are applied `2`, approved `3`,
pending review `14`, canceled `7`. pending review `14`, canceled `7`.
- VPS direct Cory answers: no-send `DC-01` through `DC-06` strict-score `6/6`. - VPS direct m3taversal-standard answers: no-send `DC-01` through `DC-06` strict-score `6/6`.
- Telegram direct Cory answers: partial. `DC-01` was visibly sent and its live - Telegram direct m3taversal-standard answers: partial. `DC-01` was visibly sent and its live
gateway reply strict-passed server-side, but the reply itself is not yet gateway reply strict-passed server-side, but the reply itself is not yet
captured in Telegram. `DC-02` through `DC-06` are not yet sent. captured in Telegram. `DC-02` through `DC-06` are not yet sent.
- DB composition/application: deterministic source-to-proposal, full-data clone - DB composition/application: deterministic source-to-proposal, full-data clone
@ -146,7 +170,7 @@ sections below override this newer status.
- GCP canonical DB: exact VPS parity is proven across `39` tables and `52,164` - GCP canonical DB: exact VPS parity is proven across `39` tables and `52,164`
rows over private TLS, with zero row/catalog/role/extension/performance rows over private TLS, with zero row/catalog/role/extension/performance
mismatches. Gateway PID `148735`, `NRestarts=0` stayed unchanged. mismatches. Gateway PID `148735`, `NRestarts=0` stayed unchanged.
- GCP Cory replay: six DB-read routes pass. One real no-send model replay - GCP m3taversal-standard replay: six DB-read routes pass. One real no-send model replay
nominally scored `6/6`, but it was rejected because two replies printed false nominally scored `6/6`, but it was rejected because two replies printed false
zero canonical counts. The harness now enables the required `status` read and zero canonical counts. The harness now enables the required `status` read and
rejects any printed count that differs from the canonical status receipt. A rejects any printed count that differs from the canonical status receipt. A
@ -170,16 +194,16 @@ Newest artifacts:
- `docs/reports/leo-working-state-20260709/gcp-canonical-parity-live-20260712.json` - `docs/reports/leo-working-state-20260709/gcp-canonical-parity-live-20260712.json`
- `docs/reports/leo-working-state-20260709/teleo_clone_cory_20260712t1940z-canonical-parity-receipt.json` - `docs/reports/leo-working-state-20260709/teleo_clone_cory_20260712t1940z-canonical-parity-receipt.json`
- `docs/reports/leo-working-state-20260709/telegram-visible-direct-claim-dc01-partial-current.json` - `docs/reports/leo-working-state-20260709/telegram-visible-direct-claim-dc01-partial-current.json`
- `docs/reports/leo-working-state-20260709/gcp-cory-model-replay-current.json` (created only after the hardened rerun) - `docs/reports/leo-working-state-20260709/gcp-db-first-blind-claim-current.json`
- `docs/reports/leo-working-state-20260709/gcp-operator-access-blocker-current.json` - `docs/reports/leo-working-state-20260709/gcp-operator-access-blocker-current.json`
## Definition And Outcomes ## Definition And Outcomes
- Working Leo definition: `docs/reports/leo-working-state-20260709/working-leo-definition-20260709.md` - Working Leo definition: `docs/reports/leo-working-state-20260709/working-leo-definition-20260709.md`
- Cory expected outcomes: `docs/reports/leo-working-state-20260709/cory-expected-working-leo-outcomes-20260709.md` - Legacy-named expected-outcomes evidence: `docs/reports/leo-working-state-20260709/cory-expected-working-leo-outcomes-20260709.md`
- Current state: `docs/reports/leo-working-state-20260709/working-leo-current-state-20260709.md` - Current state: `docs/reports/leo-working-state-20260709/working-leo-current-state-20260709.md`
- Execution plan: `docs/reports/leo-working-state-20260709/working-leo-execution-plan-current.md` - Execution plan: `docs/reports/leo-working-state-20260709/working-leo-execution-plan-current.md`
- Open-ended benchmark spec: `docs/reports/leo-working-state-20260709/working-leo-open-ended-benchmark-spec.json` (5 live-safe open prompts, 9 broader Cory-style outcome scenarios, and 6 no-context direct-claim follow-up prompts for disposable clone/sandbox first) - Open-ended benchmark spec: `docs/reports/leo-working-state-20260709/working-leo-open-ended-benchmark-spec.json` (5 live-safe open prompts, 9 broader m3taversal-standard outcome scenarios, and 6 no-context direct-claim follow-up prompts for disposable clone/sandbox first)
- Open-ended live Telegram canary: `docs/reports/leo-working-state-20260709/telegram-live-open-ended-cory-style-20260709.md` - Open-ended live Telegram canary: `docs/reports/leo-working-state-20260709/telegram-live-open-ended-cory-style-20260709.md`
- Open-ended live Telegram canary JSON: `docs/reports/leo-working-state-20260709/telegram-live-open-ended-cory-style-20260709.json` - Open-ended live Telegram canary JSON: `docs/reports/leo-working-state-20260709/telegram-live-open-ended-cory-style-20260709.json`
- Retained Telegram benchmark score for the open-ended canary: `docs/reports/leo-working-state-20260709/telegram-live-open-ended-benchmark-score-current.md` - Retained Telegram benchmark score for the open-ended canary: `docs/reports/leo-working-state-20260709/telegram-live-open-ended-benchmark-score-current.md`
@ -187,9 +211,9 @@ Newest artifacts:
- Full live-safe open-ended Telegram suite report: `docs/reports/leo-working-state-20260709/telegram-live-open-ended-suite-current.md` - Full live-safe open-ended Telegram suite report: `docs/reports/leo-working-state-20260709/telegram-live-open-ended-suite-current.md`
- Full live-safe open-ended Telegram suite score: `docs/reports/leo-working-state-20260709/telegram-live-open-ended-suite-score-current.md` - Full live-safe open-ended Telegram suite score: `docs/reports/leo-working-state-20260709/telegram-live-open-ended-suite-score-current.md`
- Full live-safe open-ended Telegram suite score JSON: `docs/reports/leo-working-state-20260709/telegram-live-open-ended-suite-score-current.json` - Full live-safe open-ended Telegram suite score JSON: `docs/reports/leo-working-state-20260709/telegram-live-open-ended-suite-score-current.json`
- Full Cory-style outcome/direct-claim sandbox results: `docs/reports/leo-working-state-20260709/working-leo-cory-outcome-sandbox-results-current.md` - Full m3taversal-standard outcome/direct-claim sandbox results: `docs/reports/leo-working-state-20260709/working-leo-cory-outcome-sandbox-results-current.md`
- Full Cory-style outcome/direct-claim sandbox score: `docs/reports/leo-working-state-20260709/working-leo-cory-outcome-sandbox-score-current.md` - Full m3taversal-standard outcome/direct-claim sandbox score: `docs/reports/leo-working-state-20260709/working-leo-cory-outcome-sandbox-score-current.md`
- Full Cory-style outcome sandbox score JSON: `docs/reports/leo-working-state-20260709/working-leo-cory-outcome-sandbox-score-current.json` - Full m3taversal-standard outcome sandbox score JSON: `docs/reports/leo-working-state-20260709/working-leo-cory-outcome-sandbox-score-current.json`
- Live VPS handler direct-claim suite report: `docs/reports/leo-working-state-20260709/telegram-handler-direct-claim-suite-current.json` - Live VPS handler direct-claim suite report: `docs/reports/leo-working-state-20260709/telegram-handler-direct-claim-suite-current.json`
- Live VPS handler direct-claim suite score: `docs/reports/leo-working-state-20260709/telegram-handler-direct-claim-suite-score-current.md` - Live VPS handler direct-claim suite score: `docs/reports/leo-working-state-20260709/telegram-handler-direct-claim-suite-score-current.md`
- Live VPS handler direct-claim suite review: `docs/reports/leo-working-state-20260709/telegram-handler-direct-claim-suite-review-current.md` - Live VPS handler direct-claim suite review: `docs/reports/leo-working-state-20260709/telegram-handler-direct-claim-suite-review-current.md`
@ -311,9 +335,9 @@ Newest artifacts:
- Skill-pack map: `docs/reports/leo-working-state-20260709/leo-db-state-13-skill-pack.svg` - Skill-pack map: `docs/reports/leo-working-state-20260709/leo-db-state-13-skill-pack.svg`
- Helmer packet proof: `docs/reports/leo-working-state-20260709/leo-db-state-14-helmer-packet.svg` - Helmer packet proof: `docs/reports/leo-working-state-20260709/leo-db-state-14-helmer-packet.svg`
- Open-ended Cory-style canary map: `docs/reports/leo-working-state-20260709/leo-db-state-15-open-ended-canary.svg` - Open-ended m3taversal-standard canary map: `docs/reports/leo-working-state-20260709/leo-db-state-15-open-ended-canary.svg`
- Helmer ledger packet proof: `docs/reports/leo-working-state-20260709/leo-db-state-16-helmer-ledger.svg` - Helmer ledger packet proof: `docs/reports/leo-working-state-20260709/leo-db-state-16-helmer-ledger.svg`
- Cory-style outcome benchmark map: `docs/reports/leo-working-state-20260709/leo-db-state-17-cory-outcome-benchmark.svg` - m3taversal-standard outcome benchmark map: `docs/reports/leo-working-state-20260709/leo-db-state-17-cory-outcome-benchmark.svg`
- Leoclean DB versus workspace map: `docs/reports/leo-working-state-20260709/leo-db-state-18-db-vs-workspace.svg` - Leoclean DB versus workspace map: `docs/reports/leo-working-state-20260709/leo-db-state-18-db-vs-workspace.svg`
- Claim/body/metadata schema map: `docs/reports/leo-working-state-20260709/leo-db-state-19-claims-body-metadata.svg` - Claim/body/metadata schema map: `docs/reports/leo-working-state-20260709/leo-db-state-19-claims-body-metadata.svg`
- Cross-surface strategy anchor proof: `docs/reports/leo-working-state-20260709/leo-db-state-20-cross-surface-anchor.svg` - Cross-surface strategy anchor proof: `docs/reports/leo-working-state-20260709/leo-db-state-20-cross-surface-anchor.svg`
@ -331,7 +355,7 @@ Newest artifacts:
- 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.
- VPS Leo live-safe open-ended Telegram suite is live-proven and retained-benchmark-scored for `OE-01` through `OE-05`: full selected coverage, `5/5` scored prompts passing, no overclaim, and no marker/staging/canonical KB rows created by the suite. - VPS Leo live-safe open-ended Telegram suite is live-proven and retained-benchmark-scored for `OE-01` through `OE-05`: full selected coverage, `5/5` scored prompts passing, no overclaim, and no marker/staging/canonical KB rows created by the suite.
- The broader sandbox-first Cory-style benchmark has a full-catalog mixed-evidence score: real retained live Telegram evidence for `OE-01` through `OE-05`, sandbox fixture replies for `CS-01` through `CS-09`, and no-context direct-claim expected-answer/follow-up fixtures for `DC-01` through `DC-06`, `20/20` passing. This is not a live Telegram run for the CS/DC prompts. - The broader sandbox-first m3taversal-standard benchmark has a full-catalog mixed-evidence score: real retained live Telegram evidence for `OE-01` through `OE-05`, sandbox fixture replies for `CS-01` through `CS-09`, and no-context direct-claim expected-answer/follow-up fixtures for `DC-01` through `DC-06`, `20/20` passing. This is not a live Telegram run for the CS/DC prompts.
- After deploy commit `0183a5c805fa3b51d638267afd20f67a1bc3777f`, three fresh VPS GatewayRunner clean-session trials strict-scored `6/6` (`18/18` replies). Every trial posted nothing, made no live profile or production DB change, preserved counts (`1837/4145/4916/4670/26`), removed its temp profile, and left `leoclean-gateway.service` active/running with PID `2403328`, zero restarts, and the same start timestamp. Final cleanup found no matching temp directories. - After deploy commit `0183a5c805fa3b51d638267afd20f67a1bc3777f`, three fresh VPS GatewayRunner clean-session trials strict-scored `6/6` (`18/18` replies). Every trial posted nothing, made no live profile or production DB change, preserved counts (`1837/4145/4916/4670/26`), removed its temp profile, and left `leoclean-gateway.service` active/running with PID `2403328`, zero restarts, and the same start timestamp. Final cleanup found no matching temp directories.
- Intentional restart survival is now live-proven as a separate required proof row, not implied by `NRestarts=0`: `systemctl restart leoclean-gateway.service` returned `0`, service moved from `MainPID=1908033` to `MainPID=2845730`, stayed active/running after restart and after the no-post handler smoke, canonical DB counts stayed unchanged (`1837/4145/4670/4916/26`), no Telegram post or production DB apply ran, no live profile change was recorded, temp profile cleanup succeeded, and the post-restart no-post `DC-01` through `DC-06` handler smoke returned six runtime replies. The retained pre-repair post-restart strict score was `5/6`; after deploying repair SHA `090becae1621436467610e529d6328d70964d11f`, the same no-post direct-claim suite strict-scored `6/6` with `DC-01` through `DC-06` all passing, DB counts unchanged, no Telegram post, no production DB apply, no live profile change, and cleanup confirmed. The raw handler JSON is retained on the VPS at `/tmp/leo-post-skill-repair-direct-claim-current.json` with SHA256 `6fffdbafc18913c4fc62feb00906988dee2e696d279f22dd284951a79451f39c`. - Intentional restart survival is now live-proven as a separate required proof row, not implied by `NRestarts=0`: `systemctl restart leoclean-gateway.service` returned `0`, service moved from `MainPID=1908033` to `MainPID=2845730`, stayed active/running after restart and after the no-post handler smoke, canonical DB counts stayed unchanged (`1837/4145/4670/4916/26`), no Telegram post or production DB apply ran, no live profile change was recorded, temp profile cleanup succeeded, and the post-restart no-post `DC-01` through `DC-06` handler smoke returned six runtime replies. The retained pre-repair post-restart strict score was `5/6`; after deploying repair SHA `090becae1621436467610e529d6328d70964d11f`, the same no-post direct-claim suite strict-scored `6/6` with `DC-01` through `DC-06` all passing, DB counts unchanged, no Telegram post, no production DB apply, no live profile change, and cleanup confirmed. The raw handler JSON is retained on the VPS at `/tmp/leo-post-skill-repair-direct-claim-current.json` with SHA256 `6fffdbafc18913c4fc62feb00906988dee2e696d279f22dd284951a79451f39c`.
- The Telegram-visible direct-claim benchmark was run in group `Leo` (`-5146042086`): all six prompts and replies are visible, and DB/service state is unchanged. The hardened semantic scorer gives `5/6`; `DC-05` incorrectly suggests applying the strict canary `add_edge` path to one of three legacy approved proposals whose payload types do not all match. - The Telegram-visible direct-claim benchmark was run in group `Leo` (`-5146042086`): all six prompts and replies are visible, and DB/service state is unchanged. The hardened semantic scorer gives `5/6`; `DC-05` incorrectly suggests applying the strict canary `add_edge` path to one of three legacy approved proposals whose payload types do not all match.
@ -364,7 +388,7 @@ Newest artifacts:
- 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 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. - 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.
- GCP Console observability now separately proves `teleo-prod-1` is running and that serial output through `2026-07-10T15:57:54Z` contains recurring successful `leoclean-cloudsql-memory-sync.service` cycles. The latest exact `leoclean-gcp-prod-parallel.service` lifecycle event in the retained serial buffer is a Hermes start on July 9 with no later exact stop/failure. Cloud Logging has `92` guest/platform records over seven days but zero matches for `leoclean`, `hermes`, `gateway`, `postgres`, or `teleo-kb`, and Ops Agent is absent. This is bounded unit-activity evidence, not current PID/restart/SHA/profile/model/auth/Telegram/DB-row parity; no-post Cory execution remains unsafe and was not attempted. The agent-created Console tab was closed and no GCP/DB/service/IAM/deploy/credential/Telegram mutation occurred. - GCP Console observability now separately proves `teleo-prod-1` is running and that serial output through `2026-07-10T15:57:54Z` contains recurring successful `leoclean-cloudsql-memory-sync.service` cycles. The latest exact `leoclean-gcp-prod-parallel.service` lifecycle event in the retained serial buffer is a Hermes start on July 9 with no later exact stop/failure. Cloud Logging has `92` guest/platform records over seven days but zero matches for `leoclean`, `hermes`, `gateway`, `postgres`, or `teleo-kb`, and Ops Agent is absent. This is bounded unit-activity evidence, not current PID/restart/SHA/profile/model/auth/Telegram/DB-row parity; no-post m3taversal-standard execution remains unsafe and was not attempted. The agent-created Console tab was closed and no GCP/DB/service/IAM/deploy/credential/Telegram mutation occurred.
- PR #77 auto-deployed to the VPS checkout at merge `1a0abd882d00c1b58fe203f42de42d4eeea61cbf` without restarting Leo: the deploy journal says `No Python changes - services not restarted`, the gateway remained PID `2999690` with `NRestarts=0`, and the apply worker/timer remained disabled and inactive. This proves source synchronization and runtime non-change, not a GCP replay. - PR #77 auto-deployed to the VPS checkout at merge `1a0abd882d00c1b58fe203f42de42d4eeea61cbf` without restarting Leo: the deploy journal says `No Python changes - services not restarted`, the gateway remained PID `2999690` with `NRestarts=0`, and the apply worker/timer remained disabled and inactive. This proves source synchronization and runtime non-change, not a GCP replay.
- A passwordless GCP operator is implemented and locally validated but is not yet bootstrapped in project `teleo-501523`. It uses GitHub OIDC/Workload Identity Federation, separate status and clone-operation service accounts, IAP TCP forwarding, OS Login, five-minute SSH keys, and a root-owned dispatcher limited to `status`, `direct-claim-replay`, and `cleanup-clone`. It stores no Google password, local refresh token, service-account JSON key, or API key. One final authenticated GCP admin session is still required to install and verify the IAM/IAP/OS Login/dispatcher path; the old public `/32` rule must remain enabled until the workflow status artifact passes. - A passwordless GCP operator is implemented and locally validated but is not yet bootstrapped in project `teleo-501523`. It uses GitHub OIDC/Workload Identity Federation, separate status and clone-operation service accounts, IAP TCP forwarding, OS Login, five-minute SSH keys, and a root-owned dispatcher limited to `status`, `direct-claim-replay`, and `cleanup-clone`. It stores no Google password, local refresh token, service-account JSON key, or API key. One final authenticated GCP admin session is still required to install and verify the IAM/IAP/OS Login/dispatcher path; the old public `/32` rule must remain enabled until the workflow status artifact passes.
- The broader GCP working-Leo runner now has a complete `20`-prompt catalog and requires `32` GCP-executable non-tautological composition checks plus lifecycle, role-separation, immutable-source, and secret-redaction gates. Local tests and validation-only execution pass, and an independent VPS source baseline is retained with counts `1837/4145/4670/4916/17/26`; live GCP execution remains unproven because the SHA-bound target-identity receipt, four separated database roles/credentials, and passwordless operator bootstrap are not yet present. This does not inherit or claim the VPS-only `34/34` result. - The broader GCP working-Leo runner now has a complete `20`-prompt catalog and requires `32` GCP-executable non-tautological composition checks plus lifecycle, role-separation, immutable-source, and secret-redaction gates. Local tests and validation-only execution pass, and an independent VPS source baseline is retained with counts `1837/4145/4670/4916/17/26`; live GCP execution remains unproven because the SHA-bound target-identity receipt, four separated database roles/credentials, and passwordless operator bootstrap are not yet present. This does not inherit or claim the VPS-only `34/34` result.

View file

@ -1,18 +1,20 @@
# Fable / Worker Onboarding Prompt - Leo / Teleo # Fable / Worker Onboarding Prompt - Leo / Teleo
current_canary: read the retained evidence index, then produce a repo/VPS/GCP/Telegram working-state map with exact claim ceilings and next executable action. current_canary: validate the repo-native skill routes, then produce a repo/VPS/GCP/Telegram working-state map with exact claim ceilings and next executable action.
Use the Teleo repo skill pack at: Use the Teleo repo skill pack at:
`.agents/skills/` `.agents/skills/`
Load these draft skills first: Run `.agents/skills/teleo-leo-onboarding/scripts/validate_skill_pack.py --root .`,
then load these manifest-indexed skills first:
1. `skills/teleo-leo-onboarding/SKILL.md` 1. `.agents/skills/teleo-leo-onboarding/SKILL.md`
2. `skills/working-leo-m3taversal-outcomes/SKILL.md` 2. `.agents/skills/working-leo-m3taversal-outcomes/SKILL.md`
3. `skills/teleo-kb-db-change-workflow/SKILL.md` 3. `.agents/skills/teleo-kb-db-change-workflow/SKILL.md`
4. `skills/teleo-vps-runtime-ops/SKILL.md` 4. `.agents/skills/teleo-reconstruction-recovery/SKILL.md`
5. `skills/teleo-proof-handoff/SKILL.md` 5. `.agents/skills/teleo-vps-runtime-ops/SKILL.md`
6. `.agents/skills/teleo-proof-handoff/SKILL.md`
Hard constraints: Hard constraints:

View file

@ -1,6 +1,7 @@
# Leo Architecture, Work, And Outcomes # Leo Architecture, Work, And Outcomes
Current as of `2026-07-14 01:40 UTC`. Original delivery summary from `2026-07-14 01:40 UTC`, reconciled with merged
repository truth on 2026-07-15.
This is the executive summary of what Leo is, how its knowledge system works, This is the executive summary of what Leo is, how its knowledge system works,
what changed during the July 10-14 delivery wave, what is genuinely working, what changed during the July 10-14 delivery wave, what is genuinely working,
@ -17,10 +18,16 @@ managed gateway restart. The current broad no-post suite passes `15/15`.
**The whole system is not complete.** The current broad suite has not yet been **The whole system is not complete.** The current broad suite has not yet been
repeated visibly in the real Telegram group through authenticated Google Chrome. repeated visibly in the real Telegram group through authenticated Google Chrome.
The newest post-V3 database copy has not yet been restored and replayed on GCP. The newest post-V3 database copy has since been restored to a disposable private
No V3 production proposal has been canonically applied. A snapshot can rebuild GCP Cloud SQL clone with exact parity and a bounded no-send reasoning replay,
the current database exactly, but the entire historical database cannot yet be then cleaned up. Persistent GCP remains staging and older than that disposable
recompiled from raw documents and review history alone. proof point. No V3 production proposal has been canonically applied. A snapshot
can rebuild the current database exactly, and merged PR #146 adds an exact
insert-only genesis-plus-ledger slice; the entire historical database still
cannot be recompiled from raw documents and review history alone.
Repository reconciliation: PRs #146 and #147 are merged `main` truth. PR #148
remains an open least-privilege GCP candidate and is not canonical runtime proof.
## The Architecture ## The Architecture
@ -255,7 +262,8 @@ left the gateway unchanged, and removed the private receipt afterward.
| Grounded proposal staging | live VPS `pending_review` row | Achieved | | Grounded proposal staging | live VPS `pending_review` row | Achieved |
| Exact snapshot rebuild | isolated local Postgres, `52,167` rows | Achieved | | Exact snapshot rebuild | isolated local Postgres, `52,167` rows | Achieved |
| New strict-apply row receipt | deployed VPS read-only recovery | Achieved | | New strict-apply row receipt | deployed VPS read-only recovery | Achieved |
| Prior GCP database copy and six-question replay | private Cloud SQL and GCP compute | Achieved at the earlier `52,164`-row proof point | | Current disposable GCP copy and ID-free replay | private Cloud SQL and GCP compute, `52,167/52,167` rows | Achieved and cleaned up |
| Genesis plus strict insert-only replay | isolated local Postgres through merged PR #146 | Achieved for supported strict contracts |
## What Is Not Achieved ## What Is Not Achieved
@ -263,9 +271,9 @@ left the gateway unchanged, and removed the private receipt afterward.
| --- | --- | | --- | --- |
| Current broad Telegram-visible proof | Telegram Web in the connected Google Chrome is not authenticated; handler proof is not visible-chat proof | | Current broad Telegram-visible proof | Telegram Web in the connected Google Chrome is not authenticated; handler proof is not visible-chat proof |
| V3 production canonical apply | proposal remains `pending_review`; no review or apply was authorized | | V3 production canonical apply | proposal remains `pending_review`; no review or apply was authorized |
| Current GCP parity | the `52,167`-row post-V3 snapshot has not yet been restored and replayed on GCP |
| Arbitrary automatic document/post/tweet intake | the current source command handles one bounded text-like artifact; general fetch/extract/stage automation is not complete | | Arbitrary automatic document/post/tweet intake | the current source command handles one bounded text-like artifact; general fetch/extract/stage automation is not complete |
| Full source-to-blank-DB recompilation | historical provenance gaps remain and the genesis-plus-ledger replay runner is not finished | | Persistent GCP parity and promotion | the disposable `52,167`-row proof passed, but persistent `teleo_canonical` remains the older staging copy and is not cut over |
| Full source-to-blank-DB recompilation | historical provenance gaps and unsupported mutating contracts remain beyond the merged insert-only genesis-plus-ledger slice |
| General DB-to-identity rendering | no complete scheduled DB-to-`SOUL.md` renderer is proven | | General DB-to-identity rendering | no complete scheduled DB-to-`SOUL.md` renderer is proven |
| Deployed decision matrix | matrix tables and voting flow are absent from the current live schema | | Deployed decision matrix | matrix tables and voting flow are absent from the current live schema |
@ -282,13 +290,13 @@ left the gateway unchanged, and removed the private receipt afterward.
## How This Moves GCP Forward ## How This Moves GCP Forward
- The prior GCP vertical slice proved the architecture can run against private - The current post-V3 snapshot has been restored to a disposable private Cloud
Cloud SQL and real GCP compute. SQL clone with exact parity, bounded no-send replay, and cleanup.
- The current post-V3 dump and manifest are already captured and locally - Persistent `teleo_canonical` remains the older staging copy; promotion,
verified, so GCP work starts from a known input rather than rebuilding one. continuous replication, Telegram delivery, and production cutover are
- The same `15/15` suite and exact V3 question are ready for replay. separate decisions and proof rows.
- The remaining GCP work is operational: authenticate, restore the current copy, - Open PR #148 proposes least-privilege runtime access. Its branch and proposed
verify parity/private connectivity/performance, replay, and clean up. live end state remain candidate evidence until merge and receipt verification.
## How This Moves On-Demand Rebuild Forward ## How This Moves On-Demand Rebuild Forward
@ -311,10 +319,11 @@ verified post-V3 snapshot = genesis epoch
-> broad answer benchmark -> broad answer benchmark
``` ```
The new receipt path closes the future side of this model. The next repo-owned The new receipt path closes the future side of this model. Merged PR #146 now
step is to replay one reviewed `approve_claim` receipt onto a clean genesis clone replays supported strict insert-only receipts on a clean genesis clone and proves
and prove exact post-apply parity. Historical import gaps can then be repaired exact post-apply parity. The next reconstruction work is to retain complete
without allowing new gaps to accumulate. deltas for mutating contracts, handle or reject legacy freeform applies, and
repair historical import gaps without allowing new gaps to accumulate.
## Delivery Chain ## Delivery Chain
@ -333,16 +342,21 @@ The latest coherent delivery sequence is merged:
| `#135` | current post-V3 snapshot and exact rebuild proof | | `#135` | current post-V3 snapshot and exact rebuild proof |
| `#136` | private replay receipts for guarded applies | | `#136` | private replay receipts for guarded applies |
| `#137` | deployed receipt runtime proof | | `#137` | deployed receipt runtime proof |
| `#144` | GCP database-first restore, fail-closed helper, and live restart proof |
| `#146` | unified turn manifest and deterministic genesis-plus-ledger replay |
| `#147` | unseen-reasoning verifier hardened for valid reasoning variance |
PR #148 is open candidate work and is intentionally excluded from the merged
delivery chain above.
## Immediate Next Outcomes ## Immediate Next Outcomes
1. Authenticate Telegram Web in the connected Google Chrome and run visible, 1. Authenticate Telegram Web in the connected Google Chrome and run visible,
out-of-sample m3taversal-style questions. out-of-sample m3taversal-style questions.
2. Authenticate Google Cloud CLI through the connected Chrome flow, restore the 2. Review and merge or reject PR #148, then verify the least-privilege runtime
current post-V3 snapshot to disposable GCP staging, verify parity, replay the receipt before considering any persistent GCP promotion.
broad suite, and remove temporary resources. 3. Extend genesis-plus-receipt reconstruction beyond supported insert-only
3. Complete the genesis-plus-receipt replayer for `approve_claim` bundles and contracts while retaining exact before/after deltas.
prove exact clone-to-replay parity.
4. Run one explicitly reviewed source packet through guarded apply in a 4. Run one explicitly reviewed source packet through guarded apply in a
disposable clone before seeking any production apply authorization. disposable clone before seeking any production apply authorization.
5. Reconstruct the remaining historical source, evidence, and edge provenance. 5. Reconstruct the remaining historical source, evidence, and edge provenance.
@ -363,7 +377,8 @@ The latest coherent delivery sequence is merged:
The last three days produced a stable, testable, database-grounded Leo on the The last three days produced a stable, testable, database-grounded Leo on the
VPS at the handler tier, a real source-to-review path, exact snapshot recovery, VPS at the handler tier, a real source-to-review path, exact snapshot recovery,
and durable replay evidence for future strict applies. They did not complete and durable replay evidence for future strict applies. Subsequent work added
current Telegram-visible broad proof, current GCP parity, a V3 production apply, disposable current GCP parity plus no-send replay and an exact insert-only
general automatic source ingestion, or full historical source-derived database genesis-plus-ledger slice. The system still lacks current Telegram-visible broad
recompilation. proof, persistent GCP parity/promotion, a V3 production apply, general automatic
source ingestion, and full historical source-derived database recompilation.

View file

@ -32,10 +32,19 @@ Always refresh before claiming current state.
## GCP ## GCP
- Project: `teleo-501523` - Project: `teleo-501523`
- Expected active account for project access: `billy@livingip.xyz` - Compute: `teleo-prod-1` in `europe-west6-a`.
- Current retained probe: local gcloud config still selects `billy@livingip.xyz` and `teleo-501523`, but this runner cannot resolve `oauth2.googleapis.com`, so parity is blocked before token refresh/project access can be rechecked. - Private Cloud SQL: `teleo-pgvector-standby`, database `teleo_canonical`.
- Current artifact: `docs/reports/leo-working-state-20260709/gcp-db-parity-current-probe-current.json` - Direct passwordless alias `ssh teleo-gcp-staging` and `sudo -n` were retained as
- Prior auth-capable artifact: `outputs/gcp-db-parity-current-blocker-20260709.json` working on 2026-07-14; always re-run the batch-mode preflight before relying on
them.
- A disposable private-TLS clone matched the current VPS snapshot at `39` tables
and `52,167` rows, then was deleted. Persistent `teleo_canonical` remains the
older staging copy and is not promoted or cut over.
- Current artifacts:
`docs/reports/leo-working-state-20260709/gcp-db-first-working-leo-20260714.md`
and `docs/reports/leo-working-state-20260709/gcp-db-first-parity-current.json`.
- PR #148 is open candidate evidence for scoped runtime access; its branch and
proposed live outcome are not canonical `main`.
## Telegram ## Telegram

View file

@ -0,0 +1,106 @@
{
"artifact": "leo_teleo_skill_pack_fresh_agent_review",
"status": "pass",
"required_tier": "T2_runtime",
"current_tier": "T2_runtime",
"proof_scope": "fresh_context_agent_onboarding_and_stale_claim_rejection",
"evidence_origin": {
"agent": "/root/fresh_skill_canary",
"fork_turns": "none",
"onboarding_authority": "isolated installed skill pack only",
"memory_used": false
},
"answers": {
"canonical_code_deployment_repository_authority": {
"repository": "GitHub living-ip/teleo-infrastructure",
"canonical_branch": "main",
"vps_deploy_source_mirror": "/opt/teleo-eval/workspaces/deploy-infra",
"mirror_is_canonical_authority": false
},
"vps_canonical_database": {
"container": "teleo-pg",
"database": "teleo"
},
"live_vps_gateway_service": "leoclean-gateway.service",
"persistent_gcp_database": {
"instance": "teleo-pgvector-standby",
"database": "teleo_canonical",
"status": "older staging copy",
"retained_measurement": {
"tables": 39,
"rows": 52164,
"proposals": 26
},
"promoted": false,
"cut_over": false
},
"pull_requests": {
"146": "merged into canonical main",
"147": "merged into canonical main",
"148": "open least-privilege GCP candidate; not canonical main"
},
"approved_proposals_equal_applied_proposals": {
"equal": false,
"retained_vps_proposal_counts": {
"total": 29,
"applied": 2,
"approved": 3,
"pending_review": 16,
"canceled": 8
},
"meaning": "Approved proposals remain staged until canonical application and row-level proof."
},
"first_safe_local_validation_command": ".agents/skills/teleo-leo-onboarding/scripts/validate_skill_pack.py --root ."
},
"stale_claims_rejected": {
"pr_148_is_merged": true,
"persistent_gcp_is_promoted": true,
"all_approved_proposals_are_applied": true
},
"validator": {
"command": "/private/tmp/teleo-clean-agent-final-20260715/skills/teleo-leo-onboarding/scripts/validate_skill_pack.py --root /private/tmp/teleo-fleet-20260715/skills-docs-onboarding",
"exit_code": 0,
"status": "pass",
"checked_path_count": 233,
"checked_paths_sha256": "ae919c3e7d8cef19a1782a00c640eeeb66c49dfbce3ad9ceaf30eb96e77bda16",
"coverage_area_count": 13,
"skill_count": 16,
"production_mutation_authorized": false,
"contains_secrets": false,
"problems": []
},
"repository_readback": {
"status_before": [
" M .agents/skills/teleo-leo-onboarding/scripts/install_skill_pack.py",
" M .agents/skills/teleo-leo-onboarding/scripts/run_clean_context_canary.py",
" M tests/test_repo_skill_pack.py",
"?? goal.md"
],
"status_after": [
" M .agents/skills/teleo-leo-onboarding/scripts/install_skill_pack.py",
" M .agents/skills/teleo-leo-onboarding/scripts/run_clean_context_canary.py",
" M tests/test_repo_skill_pack.py",
"?? goal.md"
],
"unchanged": true
},
"files_read": [
".agents/skills/teleo-leo-onboarding/SKILL.md",
".agents/skills/teleo-infra-provenance/SKILL.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/gcp-db-first-working-leo-20260714.md",
"docs/reports/leo-working-state-20260709/working-leo-definition-20260709.md",
"docs/reports/leo-working-state-20260709/approve-claim-clone-canary-current.md"
],
"cleanup_readback": {
"temporary_skill_root_removed": true,
"matching_temporary_roots_remaining": 0,
"orphan_processes_started": false
},
"external_runtime_contacted": false,
"production_mutation": false,
"contains_secrets": false,
"problems": [],
"strongest_claim_allowed": "T2 fresh-context local onboarding and route validation only; this does not prove live VPS, GCP promotion, Telegram delivery, or production proposal application"
}

View file

@ -0,0 +1,71 @@
{
"agent_reasoning_exercised": false,
"ambient_skill_root_used": false,
"artifact": "leo_teleo_skill_pack_isolated_install_canary",
"canary_command": ".agents/skills/teleo-leo-onboarding/scripts/validate_skill_pack.py --root .",
"canary_exit_status": 0,
"canary_receipt": {
"artifact": "leo_teleo_skill_pack_path_validation",
"checked_path_count": 234,
"checked_paths_sha256": "19ed1813f9cb4c19489a180081007842e241a2dcae659a5d873582fbbd50cff6",
"contains_secrets": false,
"coverage_area_count": 13,
"manifest": "docs/reports/leo-working-state-20260709/skill-pack-manifest.json",
"problems": [],
"production_mutation_authorized": false,
"required_tier": "T2_runtime",
"skill_count": 16,
"status": "pass"
},
"cleanup_readback": {
"orphan_processes_started": false,
"temporary_skill_root_removed": true
},
"contains_secrets": false,
"current_tier": "T2_runtime",
"evidence_paths": {
"authority": ".agents/skills/teleo-infra-provenance/SKILL.md",
"candidate_only_gcp": ".agents/skills/teleo-reconstruction-recovery/SKILL.md",
"gcp_database": ".agents/skills/teleo-leo-onboarding/SKILL.md",
"live_service": ".agents/skills/teleo-infra-provenance/SKILL.md",
"merged_reasoning_verifier": ".agents/skills/teleo-reconstruction-recovery/SKILL.md",
"merged_reconstruction": ".agents/skills/teleo-reconstruction-recovery/SKILL.md",
"next_safe_action": ".agents/skills/teleo-leo-onboarding/SKILL.md",
"vps_database": ".agents/skills/teleo-leo-onboarding/SKILL.md"
},
"expected_answers": {
"authority": "GitHub living-ip/teleo-infrastructure",
"candidate_only_gcp": "PR #148 open candidate only; not canonical main",
"gcp_database": "private Cloud SQL teleo_canonical; persistent copy remains staging",
"live_service": "leoclean-gateway.service",
"merged_reasoning_verifier": "PR #147 merged unseen-reasoning verifier hardening",
"merged_reconstruction": "PR #146 merged deterministic genesis-plus-ledger reconstruction",
"next_safe_action": "run the repo-local path validator before any operational action",
"vps_database": "teleo-pg / teleo"
},
"external_runtime_contacted": false,
"fresh_temporary_skill_root": true,
"installed_skill_count": 16,
"missing_to_reach_required_tier": [],
"problems": [],
"production_mutation_authorized": false,
"proof_scope": "deterministic_isolated_install_and_route_validation",
"required_tier": "T2_runtime",
"selected_installed_skills": [
"teleo-infra-provenance",
"teleo-leo-onboarding",
"teleo-reconstruction-recovery"
],
"semantic_checks": {
"authority": true,
"candidate_only_gcp": true,
"gcp_database": true,
"live_service": true,
"merged_reasoning_verifier": true,
"merged_reconstruction": true,
"next_safe_action": true,
"vps_database": true
},
"status": "pass",
"strongest_claim_allowed": "T2 deterministic isolated local skill install, route-contract, and path validation only; this artifact does not claim an agent reasoning run, and retained product proofs keep their own VPS, GCP-staging, isolated-clone, Telegram, and production claim ceilings"
}

View file

@ -1,39 +1,51 @@
{ {
"pack": "leo-teleo-skill-pack", "pack": "leo-teleo-skill-pack",
"created_date": "2026-07-09", "created_date": "2026-07-09",
"last_updated_utc": "2026-07-13T06:45:00Z", "last_updated_utc": "2026-07-15T00:16:53Z",
"status": "repo_native_validated", "status": "repo_native_path_validated",
"contains_secrets": false, "contains_secrets": false,
"production_mutation_authorized": false, "production_mutation_authorized": false,
"repo_skill_root": ".agents/skills", "repo_skill_root": ".agents/skills",
"optional_local_install_target": "/Users/user/.codex/skills", "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. PR #72 source auto-synchronized to the VPS checkout without a gateway restart or profile-source delta; the permission migration was not applied, the apply worker remained disabled/inactive, and Helmer remained unapplied. Current source-composition/apply lifecycle proof is stronger and remains isolated. GCP gateway and exact canonical DB parity are proven across 39 tables and 52,164 rows, and the hardened adapter-free model replay passes 6/6 with exact count consistency. Durable GCP operator identity and retained clone cleanup remain open. Broad blind VPS reasoning is not reliable yet: independent strict judges accepted only 1/12 and 2/12 outright. Current skills require exact m3taversal naming, neutral follow-up labels, and current-v1 schema truth.", "claim_ceiling": "Repo-native onboarding and operations skills at T2 clean-context local runtime. PRs #146 and #147 are merged and own deterministic reconstruction plus unseen-reasoning verification. PR #148 is open candidate evidence only. Generic and Helmer guarded-apply canaries pass 37/37 in disposable PostgreSQL, while production rich packets remain unapplied. The latest disposable GCP clone matched 39 tables and 52,167 rows and a no-send model turn passed 18/18 runtime checks plus 6/6 reasoning outcomes; persistent GCP teleo_canonical remains staging and no promotion, production apply, or current Telegram-visible end-to-end proof is claimed.",
"repository_reconciliation": {
"merged_pull_requests": [146, 147],
"candidate_only_pull_requests": [148],
"canonical_reconstruction_contract": "docs/kb-rebuild-and-recompile.md",
"canonical_turn_manifest": "scripts/leo_turn_execution_manifest.py",
"canonical_unseen_reasoning_verifier": "scripts/verify_leo_unseen_reasoning_chain.py"
},
"skills": [ "skills": [
{ {
"name": "teleo-leo-onboarding", "name": "teleo-leo-onboarding",
"role": "Company/product/architecture orientation for Leo and Teleo work", "role": "Company, product, architecture, task routing, and clean-context entry point",
"path": ".agents/skills/teleo-leo-onboarding/SKILL.md" "path": ".agents/skills/teleo-leo-onboarding/SKILL.md"
}, },
{ {
"name": "working-leo-m3taversal-outcomes", "name": "working-leo-m3taversal-outcomes",
"role": "Definition of working Leo from m3taversal outcomes and tests", "role": "Definition of working Leo, identity discipline, behavior, and proof ceiling",
"path": ".agents/skills/working-leo-m3taversal-outcomes/SKILL.md" "path": ".agents/skills/working-leo-m3taversal-outcomes/SKILL.md"
}, },
{ {
"name": "teleo-vps-runtime-ops", "name": "teleo-vps-runtime-ops",
"role": "VPS runtime navigation, service health, Docker/Postgres checks, report sync", "role": "VPS runtime navigation, incidents, service health, Postgres, and cleanup",
"path": ".agents/skills/teleo-vps-runtime-ops/SKILL.md" "path": ".agents/skills/teleo-vps-runtime-ops/SKILL.md"
}, },
{ {
"name": "teleo-gcp-parity-ops", "name": "teleo-gcp-parity-ops",
"role": "GCP operator access, private Cloud SQL parity, m3taversal replay, rollback, and cleanup", "role": "GCP access, private Cloud SQL parity, no-send replay, rollback, and cleanup",
"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": "Strict proposal normalization, separated review/apply authority, isolated canary, and row-level proof", "role": "Source ingestion, proposal normalization, review, guarded apply, and receipts",
"path": ".agents/skills/teleo-kb-db-change-workflow/SKILL.md" "path": ".agents/skills/teleo-kb-db-change-workflow/SKILL.md"
}, },
{
"name": "teleo-reconstruction-recovery",
"role": "Snapshot recovery, genesis-ledger replay, source recompilation, and incident recovery",
"path": ".agents/skills/teleo-reconstruction-recovery/SKILL.md"
},
{ {
"name": "leo-telegram-canary-ops", "name": "leo-telegram-canary-ops",
"role": "Live Telegram bot testing through authenticated Chrome and Computer Use", "role": "Live Telegram bot testing through authenticated Chrome and Computer Use",
@ -41,29 +53,121 @@
}, },
{ {
"name": "teleo-infra-provenance", "name": "teleo-infra-provenance",
"role": "Repo/deploy/runtime provenance and path confusion prevention", "role": "Repository, deploy, runtime, database, and retained-proof provenance",
"path": ".agents/skills/teleo-infra-provenance/SKILL.md" "path": ".agents/skills/teleo-infra-provenance/SKILL.md"
}, },
{ {
"name": "teleo-proof-handoff", "name": "teleo-proof-handoff",
"role": "Evidence-indexed handoff format for Fable/Codex/Opus workers", "role": "Capability tiers, retained evidence, cleanup, and worker handoff",
"path": ".agents/skills/teleo-proof-handoff/SKILL.md" "path": ".agents/skills/teleo-proof-handoff/SKILL.md"
},
{
"name": "nousresearch-hermes-agent",
"role": "Hermes agent packaging, skills, prompts, memory, model routing, and no-secret policy",
"path": ".agents/skills/nousresearch-hermes-agent/SKILL.md"
},
{
"name": "teleo-db-operator",
"role": "Read-first Teleo pipeline SQLite discovery, audit, backup, and guarded writes",
"path": ".agents/skills/teleo-db-operator/SKILL.md"
},
{
"name": "private-password-storage",
"role": "Device-local credential presence and secure-input handling without chat disclosure",
"path": ".agents/skills/private-password-storage/SKILL.md"
},
{
"name": "crabbox",
"role": "Isolated remote Linux and CI proof without production deployment",
"path": ".agents/skills/crabbox/SKILL.md"
},
{
"name": "decision-engine-refinement",
"role": "Replayable model, evaluator, rubric, and decision-engine quality work",
"path": ".agents/skills/decision-engine-refinement/SKILL.md"
},
{
"name": "living-ip-kb-interop",
"role": "Safe external-agent knowledge read and propose-first writeback contracts",
"path": ".agents/skills/living-ip-kb-interop/SKILL.md"
},
{
"name": "openclaw-agent",
"role": "No-secret Living IP agent packaging for OpenClaw workspaces",
"path": ".agents/skills/openclaw-agent/SKILL.md"
} }
], ],
"required_coverage": {
"company_product_context": ["teleo-leo-onboarding"],
"vps": ["teleo-vps-runtime-ops"],
"gcp": ["teleo-gcp-parity-ops"],
"hermes_runtime": ["nousresearch-hermes-agent", "teleo-vps-runtime-ops"],
"database_provenance": ["teleo-infra-provenance", "teleo-db-operator"],
"ingestion": ["teleo-kb-db-change-workflow", "living-ip-kb-interop"],
"proposal_apply": ["teleo-kb-db-change-workflow"],
"reconstruction": ["teleo-reconstruction-recovery"],
"identity": ["teleo-leo-onboarding", "working-leo-m3taversal-outcomes"],
"testing": ["teleo-leo-onboarding", "teleo-proof-handoff", "crabbox", "decision-engine-refinement"],
"security": ["teleo-vps-runtime-ops", "teleo-gcp-parity-ops", "private-password-storage"],
"secrets": ["private-password-storage"],
"incident_recovery": ["teleo-reconstruction-recovery", "teleo-vps-runtime-ops"]
},
"reference_files": [ "reference_files": [
"docs/reports/leo-working-state-20260709/skill-pack-clean-context-agent-review-current.json",
"docs/reports/leo-working-state-20260709/skill-pack-clean-context-canary-current.json",
"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/gcp-db-first-working-leo-20260714.md",
"docs/reports/leo-working-state-20260709/gcp-db-first-parity-current.json",
"docs/reports/leo-working-state-20260709/gcp-db-first-blind-claim-current.json",
"docs/reports/leo-working-state-20260709/gcp-db-first-cleanup-current.json",
"docs/reports/leo-working-state-20260709/gcp-db-first-live-deploy-restart-current.json",
"docs/reports/leo-working-state-20260709/approve-claim-clone-canary-current.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",
"docs/reports/leo-working-state-20260709/pr72-vps-auto-deploy-runtime-nonchange-current.md",
"docs/reports/leo-working-state-20260709/gcp-leo-runtime-observability-current.md",
"docs/reports/leo-working-state-20260709/gcp-canonical-parity-live-20260712.json",
"docs/reports/leo-working-state-20260709/gcp-operator-access-blocker-current.json",
"docs/reports/leo-working-state-20260709/telegram-visible-direct-claim-dc01-partial-current.json",
"docs/reports/leo-working-state-20260709/working-leo-current-proof-20260712.md", "docs/reports/leo-working-state-20260709/working-leo-current-proof-20260712.md",
"docs/reports/leo-working-state-20260709/working-leo-current-proof-20260712.json" "docs/reports/leo-working-state-20260709/leo-unified-turn-manifest-canary-current.json",
"docs/reports/leo-working-state-20260709/leo-architecture-work-and-outcomes-20260714.md",
"docs/kb-rebuild-and-recompile.md"
], ],
"validation": "tests/test_repo_skill_pack.py" "command_files": [
".agents/skills/teleo-leo-onboarding/scripts/install_skill_pack.py",
".agents/skills/teleo-leo-onboarding/scripts/run_clean_context_canary.py",
".agents/skills/teleo-leo-onboarding/scripts/validate_skill_pack.py",
"ops/run_local_canonical_postgres_rebuild.py",
"ops/run_local_genesis_ledger_rebuild.py",
"ops/capture_vps_canonical_postgres_snapshot.py",
"ops/restore_gcp_generated_postgres_snapshot.py",
"ops/verify_postgres_parity_manifest.py",
"scripts/compile_kb_source_packet.py",
"scripts/approve_proposal.py",
"scripts/apply_proposal.py",
"scripts/leo_turn_execution_manifest.py",
"scripts/verify_leo_unseen_reasoning_chain.py",
"tests/test_repo_skill_pack.py"
],
"scan_files": [
"docs/reports/leo-working-state-20260709/skill-pack-readme.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/fable-leo-teleo-onboarding.md",
"docs/reports/leo-working-state-20260709/leo-architecture-work-and-outcomes-20260714.md",
"README.md",
"docs/kb-rebuild-and-recompile.md"
],
"executable_files": [
".agents/skills/teleo-leo-onboarding/scripts/install_skill_pack.py",
".agents/skills/teleo-leo-onboarding/scripts/run_clean_context_canary.py",
".agents/skills/teleo-leo-onboarding/scripts/validate_skill_pack.py",
".agents/skills/private-password-storage/scripts/private-password-storage"
],
"validator": ".agents/skills/teleo-leo-onboarding/scripts/validate_skill_pack.py",
"installer": ".agents/skills/teleo-leo-onboarding/scripts/install_skill_pack.py",
"isolated_install_canary": ".agents/skills/teleo-leo-onboarding/scripts/run_clean_context_canary.py",
"fresh_agent_receipt": "docs/reports/leo-working-state-20260709/skill-pack-clean-context-agent-review-current.json",
"validation": "tests/test_repo_skill_pack.py",
"validation_commands": [
".agents/skills/teleo-leo-onboarding/scripts/validate_skill_pack.py --root .",
".agents/skills/teleo-leo-onboarding/scripts/run_clean_context_canary.py --root .",
".venv/bin/python -m pytest -q tests/test_repo_skill_pack.py"
]
} }

View file

@ -1,42 +1,91 @@
# Leo / Teleo Skill Pack - 2026-07-09 # Leo / Teleo Skill Pack - 2026-07-15
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: give a new engineer or agent one repo-native route into Leo/Teleo
architecture, operations, database lifecycle, proof tiers, secrets, and recovery
without relying on historical chat or unmerged branch prose.
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. The pack lives under `.agents/skills/`, contains no credentials, and grants no
production database mutation or GCP promotion authority.
## Clean-Context Entry Point
From the repository root:
```bash
.agents/skills/teleo-leo-onboarding/scripts/validate_skill_pack.py --root .
.agents/skills/teleo-leo-onboarding/scripts/install_skill_pack.py \
--root . \
--target /private/tmp/teleo-clean-agent/skills
.agents/skills/teleo-leo-onboarding/scripts/run_clean_context_canary.py --root .
```
The validator uses standard-library Python through its executable shebang. For
pytest, create the repository virtual environment from `README.md` and use
`.venv/bin/python`; do not assume a bare `python` executable exists.
## Skills In This Pack ## Skills In This Pack
1. `teleo-leo-onboarding`: company/product/architecture orientation before touching Leo or Teleo. 1. `teleo-leo-onboarding`: product, architecture, task router, and first canary.
2. `working-leo-m3taversal-outcomes`: m3taversal's expected Leo behavior and the current benchmark. 2. `working-leo-m3taversal-outcomes`: exact identity discipline and working-Leo proof ceiling.
3. `teleo-vps-runtime-ops`: VPS service, paths, Postgres, Docker, report sync, and stability checks. 3. `teleo-vps-runtime-ops`: VPS services, Postgres, incidents, and cleanup.
4. `teleo-gcp-parity-ops`: passwordless GCP access, Cloud SQL parity, no-send replay, rollback, and cleanup. 4. `teleo-gcp-parity-ops`: GCP/Cloud SQL parity, no-send replay, and rollback.
5. `teleo-kb-db-change-workflow`: source composition plus approved proposal to canonical-row workflow with clone rehearsal and rollback. 5. `teleo-kb-db-change-workflow`: source ingestion through guarded proposal/apply.
6. `leo-telegram-canary-ops`: live Telegram/Chrome/Computer Use canaries for Leo bot behavior. 6. `teleo-reconstruction-recovery`: snapshot, ledger, and semantic recovery modes.
7. `teleo-infra-provenance`: repo, deploy, mirror, and runtime provenance map. 7. `leo-telegram-canary-ops`: authorized Leo-specific Telegram canaries.
8. `teleo-proof-handoff`: evidence bundle and Fable/Codex worker handoff protocol. 8. `teleo-infra-provenance`: repository, deploy, runtime, DB, and proof provenance.
9. `teleo-proof-handoff`: tiered evidence and continuation handoffs.
10. `nousresearch-hermes-agent`: Hermes packaging and runtime boundaries.
11. `teleo-db-operator`: read-first pipeline SQLite operation.
12. `private-password-storage`: device-local secure input and presence checks.
13. `crabbox`: isolated remote Linux and CI proof.
14. `decision-engine-refinement`: replayable evaluator and model-quality work.
15. `living-ip-kb-interop`: propose-first external-agent KB access.
16. `openclaw-agent`: no-secret OpenClaw workspace packaging.
The machine-readable topic coverage and exact paths are in
`docs/reports/leo-working-state-20260709/skill-pack-manifest.json`.
## Repository Reconciliation
- PR #146 is merged: deterministic turn manifests and the first exact
genesis-plus-ledger reconstruction slice are canonical `main` behavior.
- PR #147 is merged: the unseen-reasoning verifier accepts valid reasoning
variance while keeping evidence and receipt gates.
- PR #148 is open candidate evidence only. Its proposed least-privilege GCP
files and live end state are not canonical until merged and receipt-verified.
## Current Claim Ceiling ## Current Claim Ceiling
- VPS Leo Telegram memory, KB audit, and staged write canaries are live-proven. - T2 clean-context local skill install, route validation, and focused tests are
- A strict canonical `add_edge` apply canary is live-proven. the onboarding target for this pack.
- Deterministic source composition, full-data clone composition/reasoning, and guarded approved-bundle application are isolated-proven with exact source/evidence/claim links, row deltas, rollback, and cleanup. Production rich packets remain unapplied. - VPS no-send behavior, isolated proposal/apply, snapshot recovery, disposable
- Narrow VPS DB truth and restart behavior are proven. Broad blind reasoning is not yet reliable; exact participant naming, neutral labels, and current-v1 schema guards are now required. GCP parity, and bounded no-send GCP reasoning have retained evidence.
- GCP gateway liveness, private Cloud SQL identity, exact VPS-to-GCP canonical parity, and hardened adapter-free model replay are live-proven. The database matches across `39` tables and `52,164` rows. Durable operator identity and retained replay-clone cleanup remain open because the intended target service accounts are absent. - Persistent GCP `teleo_canonical` remains staging. No GCP promotion or cutover
is authorized or claimed.
- Production rich packets remain unapplied. A current Telegram-visible complete
flow is not proven by this pack.
## Key References ## Key References
- Current whole-system proof: `docs/reports/leo-working-state-20260709/working-leo-current-proof-20260712.md` and `.json` - Fresh-agent onboarding receipt: `docs/reports/leo-working-state-20260709/skill-pack-clean-context-agent-review-current.json`
- Current truth index: `docs/reports/leo-working-state-20260709/current-truth-index.md` - Isolated-install acceptance receipt: `docs/reports/leo-working-state-20260709/skill-pack-clean-context-canary-current.json`
- Operator surface map: `docs/reports/leo-working-state-20260709/operator-surface-map.md` - Current truth: `docs/reports/leo-working-state-20260709/current-truth-index.md`
- Fable onboarding prompt: `docs/reports/leo-working-state-20260709/fable-leo-teleo-onboarding.md` - Architecture/outcomes: `docs/reports/leo-working-state-20260709/leo-architecture-work-and-outcomes-20260714.md`
- PR #72 VPS auto-deploy/runtime readback: `docs/reports/leo-working-state-20260709/pr72-vps-auto-deploy-runtime-nonchange-current.md` - Recovery contract: `docs/kb-rebuild-and-recompile.md`
- GCP canonical parity: `docs/reports/leo-working-state-20260709/gcp-canonical-parity-live-20260712.json` - GCP database-first proof: `docs/reports/leo-working-state-20260709/gcp-db-first-working-leo-20260714.md`
- GCP model replay (legacy artifact filename): `docs/reports/leo-working-state-20260709/gcp-cory-model-replay-current.json` - GCP no-send reasoning receipt: `docs/reports/leo-working-state-20260709/gcp-db-first-blind-claim-current.json`
- Three-day delivery delta: `docs/reports/leo-working-state-20260709/working-leo-three-day-delivery-summary-20260713.md` - Unified turn receipt: `docs/reports/leo-working-state-20260709/leo-unified-turn-manifest-canary-current.json`
- Blind out-of-sample audit: `docs/reports/leo-working-state-20260709/telegram-handler-blind-oos-audit-current.json` - Operator map: `docs/reports/leo-working-state-20260709/operator-surface-map.md`
- GCP operator access gate: `docs/reports/leo-working-state-20260709/gcp-operator-access-blocker-current.json`
- Skill manifest: `skill-pack-manifest.json`
## Discovery ## Team Change Notes
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. - Replaced the missing legacy GCP replay route with the current database-first
no-send receipt.
- Added deterministic install and path-validation commands.
- Added separate T2 receipts for deterministic isolated install/path validation
and fresh-context agent reasoning, stale-claim rejection, and cleanup.
- Added an explicit topic router and recovery skill.
- Added merged-vs-open PR boundaries so branch candidates cannot silently become
source-of-truth instructions.
- Added tests for coverage, route existence, installer behavior, and exact
m3taversal identity prose.

View file

@ -0,0 +1,40 @@
{
"schema": "livingip.observatoryReadAdapterCurrentGate.v1",
"current_canary": "Authenticated GET /v1/claims/sample in GCP staging returns a provenance-bearing teleo_canonical claim, anonymous GET returns 401, and authenticated POST returns 405.",
"permission_profile": "approval_policy_never_with_unrestricted_filesystem_and_network",
"attempted_routes": [
{
"route": "merged GitHub Actions deployment with artifact-builder WIF",
"result": "image build, smoke, and push passed; deploy stopped because run.googleapis.com is disabled"
},
{
"route": "local billy@livingip.xyz gcloud user credential",
"result": "token refresh and service enable both stop at noninteractive Google reauthentication"
},
{
"route": "local application-default credential",
"result": "token refresh stops at noninteractive Google reauthentication"
},
{
"route": "other locally authenticated Google accounts",
"result": "valid tokens but no access to project teleo-501523"
},
{
"route": "fixed read-only GCP IAP status workflow",
"result": "WIF token exchange failed because the teleo-iap-operator provider target is missing or disabled"
},
{
"route": "local browser or computer-use session",
"result": "no browser or computer-use tool is exposed to this task; no foreground takeover was attempted"
}
],
"exact_gate": {
"first_infrastructure_gate": "Cloud Run Admin API run.googleapis.com is disabled in teleo-501523.",
"current_authorization_gate": "The only local account authorized for teleo-501523, billy@livingip.xyz, requires Google password or MFA reauthentication before project APIs and staging IAM resources can be changed.",
"alternate_private_operator_gate": "The read-only teleo-iap-operator WIF provider returns invalid_target and cannot reach its status operation."
},
"why_autonomous_repair_stops": "Google password or MFA reauthentication is a human-only credential boundary, and no authenticated project-admin browser or computer-use surface is available in this task.",
"clear_CTA": "Run `gcloud auth login billy@livingip.xyz --update-adc`, complete Google password or MFA, and then report `GCP reauth complete`.",
"next_non_user_action": "Enable the required staging APIs; create or verify the runtime service account, API-key secret, Cloud SQL IAM database user, and exact read-only role grants; deploy the retained immutable image; capture authenticated GET, anonymous 401, authenticated POST 405, SQL write-denial, exact service/database identity, and cleanup receipts.",
"production_boundary": "Do not change Vercel, production routing, Cloud SQL network exposure, or any write endpoint."
}

View file

@ -0,0 +1,55 @@
{
"schema": "livingip.observatoryReadAdapterStagingReceipt.v1",
"status": "blocked_before_runtime_creation",
"required_runtime_tier": "T3_live_gcp_staging_api",
"current_runtime_tier": "below_T3_build_artifact_only",
"project_id": "teleo-501523",
"region": "europe-west6",
"service_name": "observatory-read-adapter-staging",
"database_name": "teleo_canonical",
"cloud_sql_instance": "teleo-501523:europe-west6:teleo-pgvector-standby",
"authorization_role": "kb_observatory_read",
"runtime_service_account": "sa-observatory-read-adapter@teleo-501523.iam.gserviceaccount.com",
"image": {
"revision": "53c8de0a1959a744e17888df1a70e935dfef0143",
"digest": "sha256:b5c9b2051559730cbb9003d74e5d8e577fa2f43f49b72577f251f2843a0650c5",
"artifact_registry_push": "pass",
"container_smoke": "pass",
"intentional_retained_resource": true
},
"workflow": {
"run_id": 29382683335,
"url": "https://github.com/living-ip/teleo-infrastructure/actions/runs/29382683335",
"build_job": "success",
"deploy_job": "failure",
"failure_class": "SERVICE_DISABLED",
"failure_detail": "run.googleapis.com is disabled in teleo-501523",
"authenticated_as": "sa-artifact-builder@teleo-501523.iam.gserviceaccount.com"
},
"live_receipts": {
"service_url": null,
"authenticated_get_sample": null,
"anonymous_get_401": null,
"authenticated_post_405": null,
"database_write_denial": null,
"database_identity": null
},
"negative_claims": {
"cloud_run_service_created": false,
"cloud_sql_public_exposure_changed": false,
"production_or_vercel_routing_changed": false,
"credentials_logged": false
},
"cleanup": {
"github_jobs_completed": true,
"local_observatory_test_containers": 0,
"local_observatory_test_images": 0,
"orphan_local_build_processes": 0,
"artifact_registry_image_retained_for_next_deploy": true
},
"proof_paths": [
"/private/tmp/observatory-read-adapter-run-29382683335/observatory-adapter-image/observatory-adapter-image.txt",
"/private/tmp/observatory-iap-status-29382844567/gcp-iap-operator-29382844567/result.json"
],
"claim_ceiling": "The immutable staging image exists, but no live API or database query receipt exists. T3 is not complete."
}

View file

@ -0,0 +1,12 @@
{
"fallback_goal_path": "goal.md",
"platform_goal_after": {
"objective": "Complete and verify the private GCP Observatory read adapter defined in this goal file, stopping before production repoint without exact authorization.",
"status": "active",
"thread_id": "019f6350-69fa-71f3-944b-3fc0065f4fec"
},
"platform_goal_before": null,
"platform_goal_repair_action": "create_goal",
"platform_goal_replaced": false,
"why_not_replaced": "No prior platform Goal existed; a new active Goal was created from goal.md."
}

View file

@ -0,0 +1,52 @@
{
"schema": "livingip.workingLeoSourceIngestionScenario.v2",
"artifact": {
"path": "document-ingestion-v2.txt",
"format": "plain_text"
},
"source": {
"identity": "document:working-leo-review-gated-composition-v2",
"source_key": "working_leo_document_ingestion_v2",
"source_type": "article",
"title": "Working Leo review-gated composition note",
"locator": "fixture://working-leo/document-ingestion-v2",
"metadata": {
"author": "Working Leo synthetic canary fixture",
"captured_at": "2026-07-15T00:00:00Z",
"document_kind": "operating_note",
"language": "en",
"synthetic": true
}
},
"extractor": {
"name": "deterministic_fixture_replay",
"version": "2"
},
"extraction": {
"claims": [
{
"claim_key": "staged_proposal_remains_noncanonical",
"type": "structural",
"confidence": 0.75,
"text": "A staged knowledge proposal remains non-canonical until review and guarded apply succeed.",
"body": "A staged proposal remains non-canonical until an operator reviews it and a separate guarded apply succeeds.",
"metadata": {
"needs_research": false
},
"evidence": [
{
"segment_id": "paragraph-2",
"role": "grounds",
"excerpt": "A staged proposal remains non-canonical until an operator reviews it and a separate guarded apply succeeds.",
"metadata": {
"evidence_kind": "exact_quote"
}
}
],
"edges": []
}
],
"duplicates": [],
"conflicts": []
}
}

View file

@ -0,0 +1,5 @@
Working Leo composition note. A newly captured document may produce claim and evidence candidates, but those candidates are not canonical knowledge.
A staged proposal remains non-canonical until an operator reviews it and a separate guarded apply succeeds. The proposal must retain the source content hash, exact evidence excerpt, and source identity so reviewers can trace every planned row back to the captured artifact.
For a staging-only rehearsal, pending_review is the terminal state. No public knowledge-base row should be written.

View file

@ -0,0 +1,3 @@
{"ts":"2026-07-15T09:00:01Z","chat_id":900001,"chat_title":"Synthetic review gate thread","message_id":7301,"type":"message","username":"fixture_analyst","display_name":"Fixture Analyst","user_id":910001,"message":"A pending review does not change canonical knowledge; only a separate guarded apply can do that.","reply_to":null,"synthetic":true}
{"ts":"2026-07-15T09:00:12Z","chat_id":900001,"chat_title":"Synthetic review gate thread","message_id":7302,"type":"message","username":"fixture_operator","display_name":"Fixture Operator","user_id":910002,"message":"Reviewer approval alone makes the proposal canonical immediately; no apply step is needed.","reply_to":7301,"synthetic":true}
{"ts":"2026-07-15T09:00:24Z","chat_id":900001,"chat_title":"Synthetic review gate thread","message_id":7303,"type":"message","username":"fixture_reviewer","display_name":"Fixture Reviewer","user_id":910003,"message":"A pending review does not change canonical knowledge; only a separate guarded apply can do that.","reply_to":7302,"synthetic":true}

View file

@ -0,0 +1,99 @@
{
"schema": "livingip.workingLeoSourceIngestionScenario.v2",
"artifact": {
"path": "telegram-transcript-ingestion-v1.jsonl",
"format": "telegram_jsonl"
},
"source": {
"identity": "fixture:telegram-chat-900001-export-20260715",
"source_key": "telegram_review_gate_exchange_20260715",
"source_type": "transcript",
"title": "Synthetic Telegram review-gate exchange",
"locator": "fixture://working-leo/telegram-transcript-ingestion-v1",
"metadata": {
"captured_at": "2026-07-15T09:01:00Z",
"export_format": "teleo_append_only_telegram_jsonl",
"language": "en",
"synthetic": true
}
},
"extractor": {
"name": "deterministic_telegram_jsonl_replay",
"version": "1"
},
"extraction": {
"claims": [
{
"claim_key": "pending_review_does_not_change_canonical",
"type": "structural",
"confidence": 0.7,
"text": "Pending review alone does not change canonical knowledge.",
"body": "A pending review does not change canonical knowledge; only a separate guarded apply can do that.",
"metadata": {
"needs_research": false
},
"evidence": [
{
"segment_id": "message-900001-7301",
"role": "grounds",
"excerpt": "A pending review does not change canonical knowledge; only a separate guarded apply can do that.",
"metadata": {
"evidence_kind": "exact_message"
}
},
{
"segment_id": "message-900001-7303",
"role": "illustrates",
"excerpt": "A pending review does not change canonical knowledge; only a separate guarded apply can do that.",
"metadata": {
"evidence_kind": "duplicate_exact_message"
}
}
],
"edges": [
{
"edge_type": "contradicts",
"target": "approval_alone_makes_canonical"
}
]
},
{
"claim_key": "approval_alone_makes_canonical",
"type": "empirical",
"confidence": 0.4,
"text": "Reviewer approval alone makes a proposal canonical without apply.",
"body": "Reviewer approval alone makes the proposal canonical immediately; no apply step is needed.",
"metadata": {
"needs_research": true
},
"evidence": [
{
"segment_id": "message-900001-7302",
"role": "grounds",
"excerpt": "Reviewer approval alone makes the proposal canonical immediately; no apply step is needed.",
"metadata": {
"evidence_kind": "exact_message"
}
}
],
"edges": []
}
],
"duplicates": [
{
"segment_id": "message-900001-7303",
"duplicate_of_claim_key": "pending_review_does_not_change_canonical",
"excerpt": "A pending review does not change canonical knowledge; only a separate guarded apply can do that.",
"reason": "Message 7303 repeats message 7301 exactly and is retained as explicit duplicate evidence."
}
],
"conflicts": [
{
"from_claim_key": "pending_review_does_not_change_canonical",
"to_claim_key": "approval_alone_makes_canonical",
"relationship": "contradicts",
"reason": "The two candidate propositions assert incompatible canonical-state transitions."
}
]
}
}

View file

@ -0,0 +1,6 @@
"""Authenticated, read-only Observatory adapter for canonical Teleo claims."""
from .app import create_app
from .config import Settings
__all__ = ["Settings", "create_app"]

View file

@ -0,0 +1,18 @@
from __future__ import annotations
import logging
from aiohttp import web
from .app import create_app
from .config import Settings
def main() -> None:
logging.basicConfig(level=logging.INFO)
settings = Settings.from_env()
web.run_app(create_app(settings), host="0.0.0.0", port=settings.port)
if __name__ == "__main__":
main()

View file

@ -0,0 +1,111 @@
from __future__ import annotations
import asyncio
import hmac
import logging
import uuid
from typing import Any
from aiohttp import web
from .config import Settings
from .db import CloudSqlClaimReader
logger = logging.getLogger("teleo.observatory_read_adapter")
SETTINGS_KEY = web.AppKey("observatory_settings", Settings)
READER_KEY = web.AppKey("observatory_reader", object)
PUBLIC_PATHS = frozenset({"/healthz", "/readyz"})
def _private_json(payload: dict[str, Any], *, status: int = 200) -> web.Response:
response = web.json_response(payload, status=status)
response.headers["Cache-Control"] = "private, no-store, max-age=0"
response.headers["Pragma"] = "no-cache"
response.headers["Vary"] = "X-Api-Key"
response.headers["X-Content-Type-Options"] = "nosniff"
return response
@web.middleware
async def api_key_auth(request: web.Request, handler: Any) -> web.StreamResponse:
if request.path in PUBLIC_PATHS:
return await handler(request)
settings = request.app[SETTINGS_KEY]
provided = request.headers.get("X-Api-Key", "")
try:
authenticated = hmac.compare_digest(
provided.encode("ascii"),
settings.api_key.encode("ascii"),
)
except UnicodeEncodeError:
authenticated = False
if not authenticated:
return _private_json({"error": "unauthorized"}, status=401)
return await handler(request)
async def healthz(_request: web.Request) -> web.Response:
return web.json_response({"status": "ok"})
async def readyz(request: web.Request) -> web.Response:
reader = request.app[READER_KEY]
try:
await asyncio.wait_for(asyncio.to_thread(reader.check_ready), timeout=12.0)
except Exception as exc:
logger.warning("readiness check failed (%s)", type(exc).__name__)
return web.json_response({"status": "unavailable"}, status=503)
return web.json_response({"status": "ready"})
async def get_sample_claim(request: web.Request) -> web.Response:
return await _claim_response(request, None)
async def get_claim(request: web.Request) -> web.Response:
raw_claim_id = request.match_info["claim_id"]
try:
claim_id = str(uuid.UUID(raw_claim_id))
except ValueError:
return _private_json({"error": "invalid_claim_id"}, status=400)
return await _claim_response(request, claim_id)
async def _claim_response(request: web.Request, claim_id: str | None) -> web.Response:
reader = request.app[READER_KEY]
try:
payload = await asyncio.wait_for(
asyncio.to_thread(reader.read_claim, claim_id),
timeout=12.0,
)
except Exception as exc:
logger.error("canonical claim read failed (%s)", type(exc).__name__)
return _private_json({"error": "canonical_claim_unavailable"}, status=503)
if payload is None:
return _private_json({"error": "claim_not_found"}, status=404)
return _private_json(payload)
async def _cleanup(app: web.Application) -> None:
reader = app[READER_KEY]
await asyncio.to_thread(reader.close)
def create_app(
settings: Settings | None = None,
*,
reader: Any | None = None,
) -> web.Application:
settings = settings or Settings.from_env()
settings.validate()
reader = reader or CloudSqlClaimReader(settings)
app = web.Application(middlewares=[api_key_auth], client_max_size=16 * 1024)
app[SETTINGS_KEY] = settings
app[READER_KEY] = reader
app.router.add_get("/healthz", healthz)
app.router.add_get("/readyz", readyz)
app.router.add_get("/v1/claims/sample", get_sample_claim)
app.router.add_get("/v1/claims/{claim_id}", get_claim)
app.on_cleanup.append(_cleanup)
return app

View file

@ -0,0 +1,61 @@
from __future__ import annotations
import os
from dataclasses import dataclass, field
EXPECTED_PROJECT = "teleo-501523"
EXPECTED_REGION = "europe-west6"
EXPECTED_INSTANCE = "teleo-pgvector-standby"
EXPECTED_CONNECTION_NAME = f"{EXPECTED_PROJECT}:{EXPECTED_REGION}:{EXPECTED_INSTANCE}"
EXPECTED_DATABASE = "teleo_canonical"
EXPECTED_AUTHORIZATION_ROLE = "kb_observatory_read"
@dataclass(frozen=True)
class Settings:
project_id: str
instance_connection_name: str
database: str
iam_database_user: str
authorization_role: str
api_key: str = field(repr=False)
service_revision: str = "unknown"
port: int = 8080
@classmethod
def from_env(cls) -> Settings:
settings = cls(
project_id=os.environ.get("GCP_PROJECT_ID", ""),
instance_connection_name=os.environ.get("CLOUD_SQL_INSTANCE", ""),
database=os.environ.get("DB_NAME", ""),
iam_database_user=os.environ.get("DB_IAM_USER", ""),
authorization_role=os.environ.get("DB_AUTHORIZATION_ROLE", ""),
api_key=os.environ.get("OBSERVATORY_API_KEY", ""),
service_revision=os.environ.get("SERVICE_REVISION", "unknown"),
port=int(os.environ.get("PORT", "8080")),
)
settings.validate()
return settings
def validate(self) -> None:
if self.project_id != EXPECTED_PROJECT:
raise ValueError(f"GCP_PROJECT_ID must be {EXPECTED_PROJECT}")
if self.instance_connection_name != EXPECTED_CONNECTION_NAME:
raise ValueError(f"CLOUD_SQL_INSTANCE must be {EXPECTED_CONNECTION_NAME}")
if self.database != EXPECTED_DATABASE:
raise ValueError(f"DB_NAME must be {EXPECTED_DATABASE}")
if self.authorization_role != EXPECTED_AUTHORIZATION_ROLE:
raise ValueError(f"DB_AUTHORIZATION_ROLE must be {EXPECTED_AUTHORIZATION_ROLE}")
if not self.iam_database_user.endswith(f"@{EXPECTED_PROJECT}.iam"):
raise ValueError("DB_IAM_USER must be the scoped Cloud SQL service-account user")
if any(character.isspace() for character in self.iam_database_user):
raise ValueError("DB_IAM_USER must not contain whitespace")
if (
len(self.api_key) < 32
or len(self.api_key) > 512
or not self.api_key.isascii()
or any(character.isspace() for character in self.api_key)
):
raise ValueError("OBSERVATORY_API_KEY must be 32-512 non-whitespace ASCII characters")
if not 1 <= self.port <= 65535:
raise ValueError("PORT is outside the valid TCP port range")

View file

@ -0,0 +1,315 @@
from __future__ import annotations
from collections.abc import Callable
from datetime import date, datetime, timezone
from decimal import Decimal
from typing import Any
from .config import Settings
ConnectionFactory = Callable[[], Any]
class ReaderContractError(RuntimeError):
"""The live database session does not match the adapter's read-only contract."""
def _json_value(value: Any) -> Any:
if isinstance(value, Decimal):
return float(value)
if isinstance(value, (date, datetime)):
return value.isoformat()
if isinstance(value, tuple):
return list(value)
return value
def _column_name(column: Any) -> str:
return str(column.name) if hasattr(column, "name") else str(column[0])
def _row_dict(cursor: Any, row: Any) -> dict[str, Any] | None:
if row is None:
return None
names = [_column_name(column) for column in cursor.description]
return {name: _json_value(value) for name, value in zip(names, row, strict=True)}
def _rows(cursor: Any) -> list[dict[str, Any]]:
names = [_column_name(column) for column in cursor.description]
return [
{name: _json_value(value) for name, value in zip(names, row, strict=True)}
for row in cursor.fetchall()
]
class CloudSqlClaimReader:
def __init__(self, settings: Settings, *, connection_factory: ConnectionFactory | None = None):
self.settings = settings
self._connector: Any | None = None
if connection_factory is not None:
self._connection_factory = connection_factory
return
from google.cloud.sql.connector import Connector, IPTypes
self._connector = Connector(refresh_strategy="LAZY")
def connect() -> Any:
return self._connector.connect(
settings.instance_connection_name,
"pg8000",
user=settings.iam_database_user,
db=settings.database,
enable_iam_auth=True,
ip_type=IPTypes.PRIVATE,
)
self._connection_factory = connect
def close(self) -> None:
if self._connector is not None:
self._connector.close()
def check_ready(self) -> dict[str, Any]:
connection = self._connection_factory()
try:
cursor = connection.cursor()
self._begin_read_only(cursor)
session = self._load_session_contract(cursor)
connection.rollback()
return {
"ready": True,
"database": session["database"],
"authorization_role": self.settings.authorization_role,
"transaction_read_only": True,
}
finally:
connection.close()
def read_claim(self, claim_id: str | None = None) -> dict[str, Any] | None:
connection = self._connection_factory()
try:
cursor = connection.cursor()
self._begin_read_only(cursor)
session = self._load_session_contract(cursor)
claim = self._load_claim(cursor, claim_id)
if claim is None:
connection.rollback()
return None
evidence = self._load_evidence(cursor, claim["id"])
edges = self._load_edges(cursor, claim["id"])
proposal_counts = self._load_proposal_counts(cursor)
related_proposals = self._load_related_proposals(cursor, claim["id"])
recent_proposals = self._load_recent_proposals(cursor)
connection.rollback()
finally:
connection.close()
generated_at = datetime.now(timezone.utc).isoformat()
return {
"schema": "livingip.observatory-canonical-claim.v1",
"generated_at": generated_at,
"read_only": True,
"provenance": {
"gcp_project": self.settings.project_id,
"cloud_sql_instance": self.settings.instance_connection_name,
"database": session["database"],
"database_principal": session["database_principal"],
"authorization_role": self.settings.authorization_role,
"transaction_read_only": True,
"write_privileges_denied": bool(session["required_writes_denied"]),
"service_revision": self.settings.service_revision,
},
"canonical": {
"relation": "public.claims",
"claim": claim,
"evidence_relation": "public.claim_evidence -> public.sources",
"evidence": evidence,
"edge_relation": "public.claim_edges",
"edges": edges,
},
"proposal_ledger": {
"relation": "kb_stage.kb_proposals",
"distinct_from_canonical": True,
"status_counts": proposal_counts,
"related": related_proposals,
"recent": recent_proposals,
"semantics": {
"pending_review": "staged only; not canonical",
"approved": "reviewed intent; not canonical until applied_at is set",
"applied": "proposal receipt only; canonical rows remain authoritative",
},
},
}
@staticmethod
def _begin_read_only(cursor: Any) -> None:
cursor.execute("set transaction read only")
cursor.execute("set local statement_timeout = '5000ms'")
cursor.execute("set local lock_timeout = '1000ms'")
def _load_session_contract(self, cursor: Any) -> dict[str, Any]:
cursor.execute(
"""
select current_database() as database,
current_user as database_principal,
current_setting('transaction_read_only') as transaction_read_only,
pg_has_role(current_user, %s, 'member') as has_authorization_role,
has_table_privilege(current_user, 'public.claims', 'select')
and has_table_privilege(current_user, 'public.sources', 'select')
and has_table_privilege(current_user, 'public.claim_evidence', 'select')
and has_table_privilege(current_user, 'public.claim_edges', 'select')
and has_table_privilege(current_user, 'kb_stage.kb_proposals', 'select')
as has_required_reads,
not (
has_table_privilege(current_user, 'public.claims', 'insert,update,delete,truncate')
or has_table_privilege(current_user, 'public.sources', 'insert,update,delete,truncate')
or has_table_privilege(current_user, 'public.claim_evidence', 'insert,update,delete,truncate')
or has_table_privilege(current_user, 'public.claim_edges', 'insert,update,delete,truncate')
or has_table_privilege(current_user, 'kb_stage.kb_proposals', 'insert,update,delete,truncate')
) as required_writes_denied
""",
(self.settings.authorization_role,),
)
session = _row_dict(cursor, cursor.fetchone())
if session is None:
raise ReaderContractError("database session contract returned no row")
if session["database"] != self.settings.database:
raise ReaderContractError("database session selected an unexpected database")
if session["database_principal"] != self.settings.iam_database_user:
raise ReaderContractError("database session selected an unexpected principal")
if session["transaction_read_only"] != "on":
raise ReaderContractError("database session is not read-only")
if not session["has_authorization_role"] or not session["has_required_reads"]:
raise ReaderContractError("database principal lacks the exact read authorization")
if not session["required_writes_denied"]:
raise ReaderContractError("database principal has an unexpected write privilege")
return session
@staticmethod
def _load_claim(cursor: Any, claim_id: str | None) -> dict[str, Any] | None:
columns = """
select c.id::text as id,
c.type::text as type,
left(c.text, 12000) as text,
c.status::text as status,
c.confidence,
coalesce(c.tags, '{}'::text[]) as tags,
c.superseded_by::text as superseded_by,
c.created_at,
c.updated_at
from public.claims c
"""
if claim_id is not None:
cursor.execute(columns + " where c.id = %s::uuid", (claim_id,))
return _row_dict(cursor, cursor.fetchone())
cursor.execute(
columns
+ """
where exists (
select 1 from public.claim_evidence ce where ce.claim_id = c.id
)
order by coalesce(c.updated_at, c.created_at) desc, c.id desc
limit 1
"""
)
claim = _row_dict(cursor, cursor.fetchone())
if claim is not None:
return claim
cursor.execute(columns + " order by coalesce(c.updated_at, c.created_at) desc, c.id desc limit 1")
return _row_dict(cursor, cursor.fetchone())
@staticmethod
def _load_evidence(cursor: Any, claim_id: str) -> list[dict[str, Any]]:
cursor.execute(
"""
select ce.id::text as evidence_id,
ce.role::text as role,
ce.weight,
ce.created_at as linked_at,
s.id::text as source_id,
s.source_type::text as source_type,
s.url,
s.storage_path,
left(coalesce(s.excerpt, ''), 2400) as excerpt,
s.hash as content_hash,
s.captured_at,
s.created_at as source_created_at
from public.claim_evidence ce
join public.sources s on s.id = ce.source_id
where ce.claim_id = %s::uuid
order by ce.weight desc nulls last, ce.created_at, ce.id
limit 30
""",
(claim_id,),
)
return _rows(cursor)
@staticmethod
def _load_edges(cursor: Any, claim_id: str) -> list[dict[str, Any]]:
cursor.execute(
"""
select e.id::text as edge_id,
case when e.from_claim = %s::uuid then 'outgoing' else 'incoming' end as direction,
e.edge_type::text as edge_type,
e.weight,
case when e.from_claim = %s::uuid then e.to_claim::text else e.from_claim::text end
as connected_claim_id,
e.created_at
from public.claim_edges e
where e.from_claim = %s::uuid or e.to_claim = %s::uuid
order by e.created_at, e.id
limit 40
""",
(claim_id, claim_id, claim_id, claim_id),
)
return _rows(cursor)
@staticmethod
def _load_proposal_counts(cursor: Any) -> dict[str, int]:
cursor.execute(
"select status::text as status, count(*)::int as count "
"from kb_stage.kb_proposals group by status order by status"
)
return {str(status): int(count) for status, count in cursor.fetchall()}
@staticmethod
def _load_related_proposals(cursor: Any, claim_id: str) -> list[dict[str, Any]]:
cursor.execute(
"""
select p.id::text as id,
p.proposal_type::text as proposal_type,
p.status::text as status,
to_jsonb(p)->>'source_ref' as source_ref,
p.reviewed_at,
p.applied_at,
p.updated_at
from kb_stage.kb_proposals p
where p.payload::text like %s
order by p.updated_at desc, p.id desc
limit 20
""",
(f"%{claim_id}%",),
)
return _rows(cursor)
@staticmethod
def _load_recent_proposals(cursor: Any) -> list[dict[str, Any]]:
cursor.execute(
"""
select p.id::text as id,
p.proposal_type::text as proposal_type,
p.status::text as status,
to_jsonb(p)->>'source_ref' as source_ref,
p.reviewed_at,
p.applied_at,
p.updated_at
from kb_stage.kb_proposals p
order by p.updated_at desc, p.id desc
limit 10
"""
)
return _rows(cursor)

View file

@ -0,0 +1,183 @@
\set ON_ERROR_STOP on
\getenv observatory_iam_user OBSERVATORY_DB_IAM_USER
-- Provision only after Cloud SQL has created the dedicated IAM database user.
-- The service-account username omits the .gserviceaccount.com suffix.
select set_config('observatory.iam_user', :'observatory_iam_user', false);
do $preflight$
declare
principal text := current_setting('observatory.iam_user');
begin
if current_database() <> 'teleo_canonical' then
raise exception 'observatory_read_role must run only in teleo_canonical';
end if;
if principal !~ '^[a-z0-9-]+@teleo-501523[.]iam$' then
raise exception 'OBSERVATORY_DB_IAM_USER is not the scoped GCP service-account database user';
end if;
if not exists (select 1 from pg_catalog.pg_roles where rolname = principal) then
raise exception 'Cloud SQL IAM database user does not exist: %', principal;
end if;
end
$preflight$;
select 'create role kb_observatory_read nologin nosuperuser nocreatedb nocreaterole noinherit noreplication nobypassrls connection limit -1'
where not exists (select 1 from pg_catalog.pg_roles where rolname = 'kb_observatory_read')
\gexec
alter role kb_observatory_read
with nologin nosuperuser nocreatedb nocreaterole noinherit noreplication nobypassrls connection limit -1
password null;
alter role kb_observatory_read reset all;
revoke all privileges on database teleo_canonical from kb_observatory_read;
grant connect on database teleo_canonical to kb_observatory_read;
revoke all on schema public, kb_stage from kb_observatory_read;
revoke all privileges on all tables in schema public, kb_stage from kb_observatory_read;
revoke all privileges on all sequences in schema public, kb_stage from kb_observatory_read;
revoke all privileges on all functions in schema public, kb_stage from kb_observatory_read;
revoke all privileges on all procedures in schema public, kb_stage from kb_observatory_read;
select format(
'revoke all privileges (%s) on table %I.%I from kb_observatory_read',
pg_catalog.string_agg(pg_catalog.quote_ident(attribute.attname), ', ' order by attribute.attnum),
namespace.nspname,
relation.relname
)
from pg_catalog.pg_class relation
join pg_catalog.pg_namespace namespace on namespace.oid = relation.relnamespace
join pg_catalog.pg_attribute attribute on attribute.attrelid = relation.oid
where namespace.nspname in ('public', 'kb_stage')
and relation.relkind in ('r', 'p', 'v', 'm', 'f')
and attribute.attnum > 0
and not attribute.attisdropped
group by namespace.nspname, relation.relname
\gexec
grant usage on schema public, kb_stage to kb_observatory_read;
grant select on
public.claims,
public.sources,
public.claim_evidence,
public.claim_edges,
kb_stage.kb_proposals
to kb_observatory_read;
do $membership$
declare
principal text := current_setting('observatory.iam_user');
unexpected text;
begin
select pg_catalog.string_agg(granted.rolname, ', ' order by granted.rolname)
into unexpected
from pg_catalog.pg_auth_members membership
join pg_catalog.pg_roles granted on granted.oid = membership.roleid
join pg_catalog.pg_roles member on member.oid = membership.member
where member.rolname = principal
and granted.rolname <> 'kb_observatory_read';
if unexpected is not null then
raise exception 'dedicated Observatory principal has unexpected role memberships: %', unexpected;
end if;
select pg_catalog.string_agg(member.rolname, ', ' order by member.rolname)
into unexpected
from pg_catalog.pg_auth_members membership
join pg_catalog.pg_roles granted on granted.oid = membership.roleid
join pg_catalog.pg_roles member on member.oid = membership.member
where granted.rolname = 'kb_observatory_read'
and member.rolname <> principal;
if unexpected is not null then
raise exception 'kb_observatory_read has unexpected members: %', unexpected;
end if;
execute pg_catalog.format('grant kb_observatory_read to %I', principal);
execute pg_catalog.format(
'alter role %I in database teleo_canonical set default_transaction_read_only = on',
principal
);
execute pg_catalog.format(
'alter role %I in database teleo_canonical set statement_timeout = %L',
principal,
'5s'
);
execute pg_catalog.format(
'alter role %I in database teleo_canonical set lock_timeout = %L',
principal,
'1s'
);
end
$membership$;
do $verify$
declare
principal text := current_setting('observatory.iam_user');
unexpected text;
begin
if not pg_catalog.pg_has_role(principal, 'kb_observatory_read', 'member') then
raise exception 'Observatory principal is not a member of kb_observatory_read';
end if;
if not exists (
select 1 from pg_catalog.pg_roles
where rolname = principal and rolinherit and not rolsuper and not rolcreatedb
and not rolcreaterole and not rolreplication and not rolbypassrls
) then
raise exception 'Observatory principal has unsafe role attributes or cannot inherit read grants';
end if;
select pg_catalog.string_agg(
pg_catalog.format('%I.%I:%s', namespace.nspname, relation.relname, privilege.name),
', ' order by namespace.nspname, relation.relname, privilege.name
)
into unexpected
from pg_catalog.pg_class relation
join pg_catalog.pg_namespace namespace on namespace.oid = relation.relnamespace
cross join (values ('INSERT'), ('UPDATE'), ('DELETE'), ('TRUNCATE'), ('REFERENCES'), ('TRIGGER')) privilege(name)
where namespace.nspname in ('public', 'kb_stage')
and relation.relkind in ('r', 'p', 'v', 'm', 'f')
and pg_catalog.has_table_privilege(principal, relation.oid, privilege.name);
if unexpected is not null then
raise exception 'Observatory principal has effective table writes: %', unexpected;
end if;
select pg_catalog.string_agg(
pg_catalog.format('%I.%I', namespace.nspname, relation.relname),
', ' order by namespace.nspname, relation.relname
)
into unexpected
from pg_catalog.pg_class relation
join pg_catalog.pg_namespace namespace on namespace.oid = relation.relnamespace
where namespace.nspname in ('public', 'kb_stage')
and relation.relkind in ('r', 'p', 'v', 'm', 'f')
and pg_catalog.has_table_privilege(principal, relation.oid, 'SELECT')
and (namespace.nspname, relation.relname) not in (
('public', 'claims'),
('public', 'sources'),
('public', 'claim_evidence'),
('public', 'claim_edges'),
('kb_stage', 'kb_proposals')
);
if unexpected is not null then
raise exception 'Observatory principal can SELECT outside its allowlist: %', unexpected;
end if;
if not (
pg_catalog.has_table_privilege(principal, 'public.claims', 'SELECT')
and pg_catalog.has_table_privilege(principal, 'public.sources', 'SELECT')
and pg_catalog.has_table_privilege(principal, 'public.claim_evidence', 'SELECT')
and pg_catalog.has_table_privilege(principal, 'public.claim_edges', 'SELECT')
and pg_catalog.has_table_privilege(principal, 'kb_stage.kb_proposals', 'SELECT')
) then
raise exception 'Observatory principal is missing a required read grant';
end if;
end
$verify$;
select jsonb_build_object(
'database', current_database(),
'authorization_role', 'kb_observatory_read',
'database_principal', current_setting('observatory.iam_user'),
'required_reads', true,
'effective_table_writes_denied', true,
'default_transaction_read_only', true
)::text;

View file

@ -1,12 +1,14 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""Deterministically rebuild a disposable Postgres from genesis plus a strict ledger. """Deterministically rebuild a disposable Postgres from genesis plus a strict ledger.
The v1 replay boundary is intentionally narrow. It supports strict, insert-only The v1 replay boundary supports strict ``add_edge``, ``attach_evidence``,
``add_edge``, ``attach_evidence``, and ``approve_claim`` receipts. Each ledger ``approve_claim``, and ``revise_strategy`` receipts. Insert-only actions seed
entry must also retain the exact approved and applied proposal rows plus the their receipt-pinned rows before the guarded transition. ``revise_strategy``
immutable approval snapshot. Legacy/freeform entries and ``revise_strategy`` instead captures the exact prestate identities, executes the exact hash-matched
fail before Docker starts because their retained material is not a complete guarded SQL, validates the generated delta, and normalizes only that delta to the
database delta. receipt-pinned transaction timestamp, IDs, and rows. Every entry must retain the
full approved and applied proposal rows plus its immutable approval snapshot.
Legacy and freeform entries still fail before Docker starts.
""" """
from __future__ import annotations from __future__ import annotations
@ -46,15 +48,18 @@ LEDGER_ARTIFACT = "teleo_genesis_plus_ledger"
MATERIAL_ARTIFACT = "teleo_strict_proposal_replay_material" MATERIAL_ARTIFACT = "teleo_strict_proposal_replay_material"
LEDGER_CONTRACT_VERSION = 1 LEDGER_CONTRACT_VERSION = 1
MATERIAL_CONTRACT_VERSION = 1 MATERIAL_CONTRACT_VERSION = 1
SUPPORTED_PROPOSAL_TYPES = frozenset({"add_edge", "attach_evidence", "approve_claim"}) DEFAULT_REBUILD_IMAGE = (
"docker.io/library/postgres:16-alpine@sha256:57c72fd2a128e416c7fcc499958864df5301e940bca0a56f58fddf30ffc07777"
)
SUPPORTED_PROPOSAL_TYPES = frozenset({"add_edge", "attach_evidence", "approve_claim", "revise_strategy"})
CLAIM_CEILING = ( CLAIM_CEILING = (
"exact genesis plus ordered strict insert-only proposal replay; supported types are " "exact genesis plus ordered strict proposal replay; supported types are add_edge, "
"add_edge, attach_evidence, and approve_claim; full approved/applied proposal rows " "attach_evidence, approve_claim, and revise_strategy; mutating strategy replay "
"and immutable approval snapshots are required" "captures prestate identities and normalizes the exact receipt-pinned poststate; "
"full approved/applied proposal rows and immutable approval snapshots are required"
) )
NOT_PROVEN = ( NOT_PROVEN = (
"legacy or freeform proposal replay", "legacy or freeform proposal replay",
"revise_strategy replay because prior-row mutations are absent from v1 receipts",
"source-corpus recompilation from a blank schema", "source-corpus recompilation from a blank schema",
"VPS, GCP, Telegram, or any live database mutation", "VPS, GCP, Telegram, or any live database mutation",
) )
@ -77,6 +82,7 @@ FIXED_ENGINE_PATHS = frozenset(
) )
SHA256_RE = re.compile(r"[0-9a-f]{64}\Z") SHA256_RE = re.compile(r"[0-9a-f]{64}\Z")
IMAGE_DIGEST_RE = re.compile(r"\S+@sha256:[0-9a-f]{64}\Z")
PROPOSAL_TRANSITION_FIELDS = frozenset( PROPOSAL_TRANSITION_FIELDS = frozenset(
{"status", "applied_by_handle", "applied_by_agent_id", "applied_at", "updated_at"} {"status", "applied_by_handle", "applied_by_agent_id", "applied_at", "updated_at"}
) )
@ -121,6 +127,35 @@ CANONICAL_TABLE_ORDER = (
"claim_evidence", "claim_evidence",
"claim_edges", "claim_edges",
) )
STRATEGY_REQUIRED_FIELDS = frozenset(
{
"id",
"agent_id",
"diagnosis",
"guiding_policy",
"proximate_objectives",
"version",
"active",
"created_at",
}
)
STRATEGY_NODE_REQUIRED_FIELDS = frozenset(
{
"id",
"agent_id",
"node_type",
"title",
"body",
"rank",
"status",
"horizon",
"measure",
"source_ref",
"metadata",
"created_at",
"updated_at",
}
)
class ReconstructionError(RuntimeError): class ReconstructionError(RuntimeError):
@ -308,6 +343,116 @@ def _validate_proposal_rows(
) )
def _parse_aware_timestamp(value: Any, field: str, *, code: str = "invalid_mutating_receipt") -> datetime:
if not isinstance(value, str) or not value:
raise ReconstructionError(code, f"{field} must be an ISO-8601 timestamp")
try:
parsed = datetime.fromisoformat(value.replace("Z", "+00:00"))
except ValueError as exc:
raise ReconstructionError(code, f"{field} must be an ISO-8601 timestamp") from exc
if parsed.tzinfo is None:
raise ReconstructionError(code, f"{field} must include a timezone")
return parsed
def _validated_uuid_list(value: Any, field: str, *, code: str = "invalid_mutating_state") -> list[str]:
if not isinstance(value, list):
raise ReconstructionError(code, f"{field} must be a UUID list")
normalized: list[str] = []
for item in value:
try:
normalized.append(str(uuid.UUID(str(item))))
except (TypeError, ValueError, AttributeError) as exc:
raise ReconstructionError(code, f"{field} must contain only UUIDs") from exc
if len(normalized) != len(set(normalized)):
raise ReconstructionError(code, f"{field} contains duplicate UUIDs")
return normalized
def _validate_revise_strategy_receipt_rows(
proposal: dict[str, Any], canonical_rows: dict[str, Any]
) -> tuple[dict[str, Any], list[dict[str, Any]]]:
payload = proposal.get("payload")
apply_payload = payload.get("apply_payload") if isinstance(payload, dict) else None
if not isinstance(apply_payload, dict):
raise ReconstructionError("invalid_mutating_receipt", "revise_strategy receipt requires a strict apply payload")
try:
agent_id = str(uuid.UUID(str(apply_payload.get("agent_id"))))
except (TypeError, ValueError, AttributeError) as exc:
raise ReconstructionError(
"invalid_mutating_receipt", "revise_strategy receipt requires one agent UUID"
) from exc
strategies = canonical_rows.get("strategies")
nodes = canonical_rows.get("strategy_nodes")
if not isinstance(strategies, list) or len(strategies) != 1 or not isinstance(strategies[0], dict):
raise ReconstructionError("invalid_mutating_receipt", "revise_strategy receipt must pin one fresh strategy row")
expected_nodes = apply_payload.get("strategy_nodes")
if (
not isinstance(expected_nodes, list)
or not isinstance(nodes, list)
or len(nodes) != len(expected_nodes)
or any(not isinstance(row, dict) for row in nodes)
):
raise ReconstructionError(
"invalid_mutating_receipt", "revise_strategy receipt must pin every fresh strategy node row"
)
strategy = strategies[0]
if frozenset(strategy) != STRATEGY_REQUIRED_FIELDS:
raise ReconstructionError(
"invalid_mutating_receipt", "revise_strategy receipt strategy row fields are not canonical"
)
if any(frozenset(row) != STRATEGY_NODE_REQUIRED_FIELDS for row in nodes):
raise ReconstructionError(
"invalid_mutating_receipt", "revise_strategy receipt strategy node row fields are not canonical"
)
strategy_id = _validated_uuid_list([strategy.get("id")], "receipt strategy id", code="invalid_mutating_receipt")[0]
node_ids = _validated_uuid_list(
[row.get("id") for row in nodes],
"receipt strategy node ids",
code="invalid_mutating_receipt",
)
if strategy.get("agent_id") != agent_id or any(row.get("agent_id") != agent_id for row in nodes):
raise ReconstructionError(
"invalid_mutating_receipt", "revise_strategy receipt rows do not belong to the payload agent"
)
if strategy.get("active") is not True or any(row.get("status") != "active" for row in nodes):
raise ReconstructionError(
"invalid_mutating_receipt", "revise_strategy receipt must pin the fresh active poststate"
)
version = strategy.get("version")
if isinstance(version, bool) or not isinstance(version, int) or version < 1:
raise ReconstructionError(
"invalid_mutating_receipt", "revise_strategy receipt strategy version must be positive"
)
transaction_timestamp = strategy.get("created_at")
transaction_time = _parse_aware_timestamp(transaction_timestamp, "receipt strategy created_at")
for row in nodes:
if row.get("created_at") != transaction_timestamp or row.get("updated_at") != transaction_timestamp:
raise ReconstructionError(
"invalid_mutating_receipt",
"revise_strategy fresh strategy and node timestamps must share one transaction timestamp",
)
reviewed_time = _parse_aware_timestamp(proposal.get("reviewed_at"), "receipt proposal reviewed_at")
applied_time = _parse_aware_timestamp(proposal.get("applied_at"), "receipt proposal applied_at")
if reviewed_time > applied_time:
raise ReconstructionError("invalid_mutating_receipt", "revise_strategy proposal approval is after apply time")
if transaction_time < reviewed_time:
raise ReconstructionError(
"invalid_mutating_receipt", "revise_strategy transaction timestamp is before proposal approval"
)
if transaction_time > applied_time:
raise ReconstructionError(
"invalid_mutating_receipt", "revise_strategy transaction timestamp is after proposal apply time"
)
return {**strategy, "id": strategy_id}, [
{**row, "id": normalized_id} for row, normalized_id in zip(nodes, node_ids, strict=True)
]
def _validate_entry_material( def _validate_entry_material(
material: dict[str, Any], material: dict[str, Any],
*, *,
@ -340,11 +485,6 @@ def _validate_entry_material(
if not isinstance(proposal, dict): if not isinstance(proposal, dict):
raise ReconstructionError("invalid_material", "replay receipt has no proposal object") raise ReconstructionError("invalid_material", "replay receipt has no proposal object")
proposal_type = proposal.get("proposal_type") proposal_type = proposal.get("proposal_type")
if proposal_type == "revise_strategy":
raise ReconstructionError(
"unsupported_mutating_receipt",
"revise_strategy receipts omit prior strategy and node mutations and cannot replay exactly",
)
if proposal_type not in SUPPORTED_PROPOSAL_TYPES: if proposal_type not in SUPPORTED_PROPOSAL_TYPES:
raise ReconstructionError( raise ReconstructionError(
"unsupported_proposal_type", "ledger entry proposal type is not supported by v1 reconstruction" "unsupported_proposal_type", "ledger entry proposal type is not supported by v1 reconstruction"
@ -358,6 +498,8 @@ def _validate_entry_material(
canonical_rows = receipt.get("canonical_rows") canonical_rows = receipt.get("canonical_rows")
if not isinstance(apply_metadata, dict) or not isinstance(canonical_rows, dict): if not isinstance(apply_metadata, dict) or not isinstance(canonical_rows, dict):
raise ReconstructionError("invalid_replay_receipt", "replay receipt is missing apply or row material") raise ReconstructionError("invalid_replay_receipt", "replay receipt is missing apply or row material")
if proposal_type == "revise_strategy":
_validate_revise_strategy_receipt_rows(proposal, canonical_rows)
try: try:
rebuilt = replay_receipt.build_replay_receipt( rebuilt = replay_receipt.build_replay_receipt(
proposal, proposal,
@ -382,6 +524,7 @@ def _validate_entry_material(
raise ReconstructionError( raise ReconstructionError(
"replay_material_hash_mismatch", "ledger replay-material pin does not match the private receipt" "replay_material_hash_mismatch", "ledger replay-material pin does not match the private receipt"
) )
receipt = {**receipt, "canonical_rows": rebuilt["canonical_rows"]}
approved = material["approved_proposal"] approved = material["approved_proposal"]
approval = material["approval_snapshot"] approval = material["approval_snapshot"]
@ -394,6 +537,15 @@ def _validate_entry_material(
"current_apply_engine_rejected_material", "current_apply_engine_rejected_material",
"current guarded apply engine rejected the strict replay material", "current guarded apply engine rejected the strict replay material",
) from exc ) from exc
current_apply_sql_sha256 = _sha256_text(current_apply_sql)
if proposal_type == "revise_strategy" and (
apply_metadata.get("source") != "exact_executed_sql"
or apply_metadata.get("apply_sql_sha256") != current_apply_sql_sha256
):
raise ReconstructionError(
"mutating_apply_engine_mismatch",
"revise_strategy replay requires the exact executed SQL to match the current guarded apply engine",
)
return LedgerEntry( return LedgerEntry(
sequence=expected_sequence, sequence=expected_sequence,
@ -405,7 +557,7 @@ def _validate_entry_material(
applied_proposal=applied, applied_proposal=applied,
receipt=receipt, receipt=receipt,
current_apply_sql=current_apply_sql, current_apply_sql=current_apply_sql,
current_apply_sql_sha256=_sha256_text(current_apply_sql), current_apply_sql_sha256=current_apply_sql_sha256,
) )
@ -550,6 +702,266 @@ def build_seed_sql(entry: LedgerEntry) -> str:
return "\n".join(statements) + "\n" return "\n".join(statements) + "\n"
def _uuid_membership_sql(column: str, values: list[str], *, negate: bool = False) -> str:
if not values:
return "true" if negate else "false"
rendered = ", ".join(f"{apply_engine.sql_literal(value)}::uuid" for value in values)
operator = "not in" if negate else "in"
return f"{column} {operator} ({rendered})"
def _uuid_array_sql(values: list[str]) -> str:
if not values:
return "array[]::uuid[]"
rendered = ", ".join(f"{apply_engine.sql_literal(value)}::uuid" for value in values)
return f"array[{rendered}]::uuid[]"
def build_revise_strategy_prestate_sql(entry: LedgerEntry) -> str:
apply_payload = entry.receipt["proposal"]["payload"]["apply_payload"]
agent = apply_engine.sql_literal(apply_payload["agent_id"])
return f"""with strategy_state as (
select coalesce(jsonb_agg(s.id::text order by s.id::text), '[]'::jsonb) as strategy_ids,
coalesce(
jsonb_agg(s.id::text order by s.id::text) filter (where s.active),
'[]'::jsonb
) as active_strategy_ids,
coalesce(max(s.version), 0) as max_strategy_version
from public.strategies s
where s.agent_id = {agent}::uuid
), node_state as (
select coalesce(jsonb_agg(n.id::text order by n.id::text), '[]'::jsonb) as strategy_node_ids,
coalesce(
jsonb_agg(n.id::text order by n.id::text) filter (where n.status <> 'retired'),
'[]'::jsonb
) as non_retired_strategy_node_ids
from public.strategy_nodes n
where n.agent_id = {agent}::uuid
)
select jsonb_build_object(
'strategy_ids', s.strategy_ids,
'active_strategy_ids', s.active_strategy_ids,
'max_strategy_version', s.max_strategy_version,
'strategy_node_ids', n.strategy_node_ids,
'non_retired_strategy_node_ids', n.non_retired_strategy_node_ids
)::text
from strategy_state s cross join node_state n;
"""
def _validate_revise_strategy_prestate(value: Any) -> dict[str, Any]:
expected = frozenset(
{
"strategy_ids",
"active_strategy_ids",
"max_strategy_version",
"strategy_node_ids",
"non_retired_strategy_node_ids",
}
)
state = _require_exact_keys(value, expected, "revise_strategy prestate")
strategy_ids = _validated_uuid_list(state["strategy_ids"], "prestate strategy ids")
active_strategy_ids = _validated_uuid_list(state["active_strategy_ids"], "prestate active strategy ids")
strategy_node_ids = _validated_uuid_list(state["strategy_node_ids"], "prestate strategy node ids")
non_retired_node_ids = _validated_uuid_list(
state["non_retired_strategy_node_ids"], "prestate non-retired strategy node ids"
)
max_version = state["max_strategy_version"]
if isinstance(max_version, bool) or not isinstance(max_version, int) or max_version < 0:
raise ReconstructionError(
"invalid_mutating_state", "prestate maximum strategy version must be a non-negative integer"
)
if len(active_strategy_ids) > 1 or not set(active_strategy_ids).issubset(strategy_ids):
raise ReconstructionError(
"invalid_mutating_state", "prestate active strategies violate the one-active invariant"
)
if not set(non_retired_node_ids).issubset(strategy_node_ids):
raise ReconstructionError(
"invalid_mutating_state", "prestate non-retired strategy nodes are not a subset of all nodes"
)
return {
"strategy_ids": strategy_ids,
"active_strategy_ids": active_strategy_ids,
"max_strategy_version": max_version,
"strategy_node_ids": strategy_node_ids,
"non_retired_strategy_node_ids": non_retired_node_ids,
}
def build_revise_strategy_generated_delta_sql(entry: LedgerEntry, prestate: dict[str, Any]) -> str:
state = _validate_revise_strategy_prestate(prestate)
apply_payload = entry.receipt["proposal"]["payload"]["apply_payload"]
agent = apply_engine.sql_literal(apply_payload["agent_id"])
strategy_filter = _uuid_membership_sql("s.id", state["strategy_ids"], negate=True)
node_filter = _uuid_membership_sql("n.id", state["strategy_node_ids"], negate=True)
return f"""select jsonb_build_object(
'strategies', coalesce((
select jsonb_agg(to_jsonb(s) order by s.id::text)
from public.strategies s
where s.agent_id = {agent}::uuid and {strategy_filter}
), '[]'::jsonb),
'strategy_nodes', coalesce((
select jsonb_agg(to_jsonb(n) order by n.id::text)
from public.strategy_nodes n
where n.agent_id = {agent}::uuid and {node_filter}
), '[]'::jsonb)
)::text;
"""
def _validate_revise_strategy_generated_delta(
entry: LedgerEntry,
prestate: dict[str, Any],
generated_rows: Any,
) -> tuple[dict[str, Any], list[dict[str, Any]]]:
state = _validate_revise_strategy_prestate(prestate)
if not isinstance(generated_rows, dict):
raise ReconstructionError("invalid_mutating_delta", "revise_strategy generated delta must be a row object")
try:
replay_receipt.build_replay_receipt(
entry.receipt["proposal"],
generated_rows,
apply_sql_sha256=entry.current_apply_sql_sha256,
apply_sql_source="exact_executed_sql",
exported_at_utc=entry.receipt.get("exported_at_utc"),
)
except (KeyError, TypeError, ValueError) as exc:
raise ReconstructionError(
"invalid_mutating_delta",
"revise_strategy generated rows do not match the strict payload",
) from exc
strategies = generated_rows.get("strategies")
nodes = generated_rows.get("strategy_nodes")
if not isinstance(strategies, list) or len(strategies) != 1 or not isinstance(strategies[0], dict):
raise ReconstructionError(
"invalid_mutating_delta", "revise_strategy apply did not generate exactly one strategy"
)
if not isinstance(nodes, list) or any(not isinstance(row, dict) for row in nodes):
raise ReconstructionError("invalid_mutating_delta", "revise_strategy apply generated invalid strategy nodes")
strategy = strategies[0]
if strategy.get("version") != state["max_strategy_version"] + 1:
raise ReconstructionError("invalid_mutating_delta", "revise_strategy generated an unexpected strategy version")
transaction_timestamp = strategy.get("created_at")
_parse_aware_timestamp(transaction_timestamp, "generated strategy created_at", code="invalid_mutating_delta")
if any(
row.get("created_at") != transaction_timestamp or row.get("updated_at") != transaction_timestamp
for row in nodes
):
raise ReconstructionError(
"invalid_mutating_delta",
"revise_strategy generated rows do not share one transaction timestamp",
)
_validated_uuid_list([strategy.get("id")], "generated strategy id", code="invalid_mutating_delta")
_validated_uuid_list(
[row.get("id") for row in nodes],
"generated strategy node ids",
code="invalid_mutating_delta",
)
return strategy, nodes
def build_revise_strategy_normalization_sql(
entry: LedgerEntry,
prestate: dict[str, Any],
generated_rows: dict[str, Any],
) -> str:
state = _validate_revise_strategy_prestate(prestate)
generated_strategy, generated_nodes = _validate_revise_strategy_generated_delta(entry, state, generated_rows)
receipt_strategy, receipt_nodes = _validate_revise_strategy_receipt_rows(
entry.receipt["proposal"], entry.receipt["canonical_rows"]
)
if receipt_strategy["version"] != state["max_strategy_version"] + 1:
raise ReconstructionError(
"mutating_receipt_version_mismatch",
"revise_strategy receipt version does not follow the observed prestate",
)
if receipt_strategy["id"] in state["strategy_ids"] or set(row["id"] for row in receipt_nodes).intersection(
state["strategy_node_ids"]
):
raise ReconstructionError(
"mutating_receipt_id_collision", "revise_strategy receipt IDs collide with the observed prestate"
)
apply_payload = entry.receipt["proposal"]["payload"]["apply_payload"]
agent = apply_engine.sql_literal(apply_payload["agent_id"])
generated_strategy_id = str(uuid.UUID(str(generated_strategy["id"])))
generated_node_ids = [str(uuid.UUID(str(row["id"]))) for row in generated_nodes]
previous_active = state["active_strategy_ids"]
changed_node_ids = state["non_retired_strategy_node_ids"]
transaction_timestamp = apply_engine.sql_literal(receipt_strategy["created_at"])
generated_node_array = _uuid_array_sql(generated_node_ids)
previous_active_array = _uuid_array_sql(previous_active)
changed_node_array = _uuid_array_sql(changed_node_ids)
prior_strategy_assertion = ""
if previous_active:
prior_strategy_assertion = f"""
if (select count(*) from public.strategies
where agent_id = {agent}::uuid and id = any(previous_active_ids) and not active) <> {len(previous_active)} then
raise exception 'revise_strategy prior active strategy transition mismatch';
end if;"""
prior_node_normalization = ""
if changed_node_ids:
prior_node_normalization = f"""
if (select count(*) from public.strategy_nodes
where agent_id = {agent}::uuid and id = any(changed_node_ids) and status = 'retired') <> {len(changed_node_ids)} then
raise exception 'revise_strategy prior node transition mismatch';
end if;
update public.strategy_nodes
set status = 'retired', updated_at = {transaction_timestamp}::timestamptz
where agent_id = {agent}::uuid and id = any(changed_node_ids);
get diagnostics changed_rows = row_count;
if changed_rows <> {len(changed_node_ids)} then
raise exception 'revise_strategy prior node normalization mismatch';
end if;"""
return f"""begin;
set local standard_conforming_strings = on;
do $reconstruction$
declare
changed_rows integer;
generated_strategy_id constant uuid := {apply_engine.sql_literal(generated_strategy_id)}::uuid;
generated_node_ids constant uuid[] := {generated_node_array};
previous_active_ids constant uuid[] := {previous_active_array};
changed_node_ids constant uuid[] := {changed_node_array};
begin{prior_strategy_assertion}
if exists (
select 1 from public.strategy_node_anchors
where from_node_id = any(generated_node_ids) or to_node_id = any(generated_node_ids)
) then
raise exception 'revise_strategy generated nodes unexpectedly have anchors';
end if;
delete from public.strategy_nodes
where agent_id = {agent}::uuid and id = any(generated_node_ids);
get diagnostics changed_rows = row_count;
if changed_rows <> {len(generated_node_ids)} then
raise exception 'revise_strategy generated node delta mismatch';
end if;
delete from public.strategies
where agent_id = {agent}::uuid and id = generated_strategy_id;
get diagnostics changed_rows = row_count;
if changed_rows <> 1 then
raise exception 'revise_strategy generated strategy delta mismatch';
end if;{prior_node_normalization}
insert into public.strategies
select seeded.* from jsonb_populate_record(
null::public.strategies, {_jsonb_literal(receipt_strategy)}
) seeded;
insert into public.strategy_nodes
select seeded.* from jsonb_populate_recordset(
null::public.strategy_nodes, {_jsonb_literal(receipt_nodes)}
) seeded;
if (select count(*) from public.strategies
where agent_id = {agent}::uuid and active) <> 1 then
raise exception 'revise_strategy normalized one-active invariant mismatch';
end if;
end
$reconstruction$;
commit;
"""
def build_proposal_readback_sql(proposal_id: str) -> str: def build_proposal_readback_sql(proposal_id: str) -> str:
literal = apply_engine.sql_literal(proposal_id) literal = apply_engine.sql_literal(proposal_id)
return f"""select jsonb_build_object( return f"""select jsonb_build_object(
@ -694,7 +1106,12 @@ def _entry_summary(entry: LedgerEntry) -> dict[str, Any]:
"current_apply_sql_sha256": entry.current_apply_sql_sha256, "current_apply_sql_sha256": entry.current_apply_sql_sha256,
"expected_row_counts": entry.receipt["expected_row_counts"], "expected_row_counts": entry.receipt["expected_row_counts"],
"seed_exact": False, "seed_exact": False,
"proposal_seed_exact": False,
"canonical_seed_exact": False,
"guarded_apply_executed": False, "guarded_apply_executed": False,
"mutating_prestate_captured": False,
"mutating_delta_validated": False,
"mutating_poststate_normalized": False,
"proposal_exact": False, "proposal_exact": False,
"canonical_rows_exact": False, "canonical_rows_exact": False,
"status": "pending", "status": "pending",
@ -707,6 +1124,7 @@ def apply_entry(
entry: LedgerEntry, entry: LedgerEntry,
summary: dict[str, Any], summary: dict[str, Any],
) -> None: ) -> None:
mutating_prestate: dict[str, Any] | None = None
try: try:
_psql( _psql(
args, args,
@ -733,6 +1151,19 @@ def apply_entry(
code="entry_seed_mismatch", code="entry_seed_mismatch",
action=f"ledger entry {entry.sequence} proposal seed", action=f"ledger entry {entry.sequence} proposal seed",
) )
summary["proposal_seed_exact"] = True
if entry.applied_proposal["proposal_type"] == "revise_strategy":
mutating_prestate = _validate_revise_strategy_prestate(
_read_json(
args,
container,
build_revise_strategy_prestate_sql(entry),
code="mutating_prestate_readback_failed",
action=f"ledger entry {entry.sequence} revise_strategy prestate readback",
)
)
summary["mutating_prestate_captured"] = True
else:
seeded_rows = _read_json( seeded_rows = _read_json(
args, args,
container, container,
@ -746,7 +1177,8 @@ def apply_entry(
code="entry_seed_mismatch", code="entry_seed_mismatch",
action=f"ledger entry {entry.sequence} canonical seed", action=f"ledger entry {entry.sequence} canonical seed",
) )
summary["seed_exact"] = True summary["canonical_seed_exact"] = True
summary["seed_exact"] = summary["proposal_seed_exact"] and summary["canonical_seed_exact"]
_psql( _psql(
args, args,
@ -759,6 +1191,27 @@ def apply_entry(
) )
summary["guarded_apply_executed"] = True summary["guarded_apply_executed"] = True
if mutating_prestate is not None:
generated_rows = _read_json(
args,
container,
build_revise_strategy_generated_delta_sql(entry, mutating_prestate),
code="mutating_delta_readback_failed",
action=f"ledger entry {entry.sequence} revise_strategy generated delta readback",
)
normalization_sql = build_revise_strategy_normalization_sql(entry, mutating_prestate, generated_rows)
summary["mutating_delta_validated"] = True
_psql(
args,
container,
normalization_sql,
role="postgres",
timeout=args.apply_timeout,
code="mutating_poststate_normalization_failed",
action=f"ledger entry {entry.sequence} revise_strategy exact poststate normalization",
)
summary["mutating_poststate_normalized"] = True
_psql( _psql(
args, args,
container, container,
@ -1199,7 +1652,7 @@ def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
parser.add_argument("--ledger", required=True, type=Path) parser.add_argument("--ledger", required=True, type=Path)
parser.add_argument("--ledger-sha256", required=True) parser.add_argument("--ledger-sha256", required=True)
parser.add_argument("--database", default="teleo") parser.add_argument("--database", default="teleo")
parser.add_argument("--image", default=canonical_rebuild.DEFAULT_IMAGE) parser.add_argument("--image", default=DEFAULT_REBUILD_IMAGE)
parser.add_argument("--container-prefix", default="teleo-genesis-ledger-rebuild") parser.add_argument("--container-prefix", default="teleo-genesis-ledger-rebuild")
parser.add_argument("--tmpfs-mb", default=1024, type=int) parser.add_argument("--tmpfs-mb", default=1024, type=int)
parser.add_argument("--startup-timeout", default=60.0, type=float) parser.add_argument("--startup-timeout", default=60.0, type=float)
@ -1218,8 +1671,8 @@ def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
parser.error("--database must be a safe PostgreSQL identifier up to 63 characters") parser.error("--database must be a safe PostgreSQL identifier up to 63 characters")
if not canonical_rebuild.CONTAINER_PREFIX_RE.fullmatch(args.container_prefix): if not canonical_rebuild.CONTAINER_PREFIX_RE.fullmatch(args.container_prefix):
parser.error("--container-prefix must be 1-40 Docker-safe characters") parser.error("--container-prefix must be 1-40 Docker-safe characters")
if not args.image or any(character.isspace() for character in args.image): if not IMAGE_DIGEST_RE.fullmatch(args.image):
parser.error("--image must be a non-empty Docker image reference without whitespace") parser.error("--image must be pinned by a lowercase sha256 digest")
if not args.docker_bin or any(character.isspace() for character in args.docker_bin): if not args.docker_bin or any(character.isspace() for character in args.docker_bin):
parser.error("--docker-bin must be one executable name or path without whitespace") parser.error("--docker-bin must be one executable name or path without whitespace")
if not 64 <= args.tmpfs_mb <= 65536: if not 64 <= args.tmpfs_mb <= 65536:

View file

@ -18,9 +18,12 @@ dev = [
"pytest-asyncio>=0.23", "pytest-asyncio>=0.23",
"ruff>=0.3", "ruff>=0.3",
] ]
observatory = [
"cloud-sql-python-connector[pg8000]>=1.20.4,<1.21",
]
[tool.setuptools] [tool.setuptools]
packages = ["lib"] packages = ["lib", "observatory_read_adapter"]
[tool.ruff] [tool.ruff]
target-version = "py311" target-version = "py311"

View file

@ -315,8 +315,9 @@ alter table kb_stage.kb_review_principals owner to kb_gate_owner;
alter table kb_stage.kb_proposal_approvals owner to kb_gate_owner; alter table kb_stage.kb_proposal_approvals owner to kb_gate_owner;
insert into kb_stage.kb_review_principals insert into kb_stage.kb_review_principals
(db_role, reviewed_by_handle, reviewed_by_agent_id, active) (db_role, reviewed_by_handle, reviewed_by_agent_id, active, created_at)
select 'kb_review'::name, a.handle, a.id, true select 'kb_review'::name, a.handle, a.id, true,
'1970-01-01 00:00:00+00'::timestamptz
from public.agents a from public.agents a
where a.handle = 'm3ta' where a.handle = 'm3ta'
on conflict (db_role) do nothing; on conflict (db_role) do nothing;

View file

@ -34,7 +34,7 @@ DEFAULT_MODEL = "anthropic/claude-sonnet-4.5"
EXTRACTOR_VERSION = "2" EXTRACTOR_VERSION = "2"
DEFAULT_OPENROUTER_URL = "https://openrouter.ai/api/v1/chat/completions" DEFAULT_OPENROUTER_URL = "https://openrouter.ai/api/v1/chat/completions"
DEFAULT_KEY_FILE = Path("/opt/teleo-eval/secrets/openrouter-key") DEFAULT_KEY_FILE = Path("/opt/teleo-eval/secrets/openrouter-key")
TEXT_SUFFIXES = {".csv", ".htm", ".html", ".json", ".md", ".rst", ".txt", ".xml"} TEXT_SUFFIXES = {".csv", ".htm", ".html", ".json", ".jsonl", ".md", ".rst", ".txt", ".xml"}
MAX_SOURCE_CHARS = 120_000 MAX_SOURCE_CHARS = 120_000
MAX_CANDIDATES = 20 MAX_CANDIDATES = 20
MAX_CLAIMS = 3 MAX_CLAIMS = 3
@ -124,20 +124,37 @@ def _load_context(path: Path) -> dict[str, Any]:
return {"database_search_query": query.strip(), "candidate_claims": normalized} return {"database_search_query": query.strip(), "candidate_claims": normalized}
def build_source_fragments(text: str) -> dict[str, str]: def build_source_fragment_index(text: str) -> tuple[dict[str, str], dict[str, dict[str, Any]]]:
"""Label non-empty source lines so the model selects text instead of copying it.""" """Return selectable lines plus exact line/character provenance."""
fragments: dict[str, str] = {} fragments: dict[str, str] = {}
for line in text.splitlines(): locations: dict[str, dict[str, Any]] = {}
if line.strip(): offset = 0
fragments[f"S{len(fragments) + 1:05d}"] = line for line_number, raw_line in enumerate(text.splitlines(keepends=True), start=1):
line = raw_line.rstrip("\r\n")
quote = line.strip()
if quote:
reference = f"S{len(fragments) + 1:05d}"
start = offset + line.find(quote)
fragments[reference] = quote
locations[reference] = {
"reference": reference,
"line": line_number,
"char_start": start,
"char_end": start + len(quote),
"quote_sha256": sha256_bytes(quote.encode("utf-8")),
}
offset += len(raw_line)
if not fragments: if not fragments:
raise PreparationError("extracted text has no selectable source lines") raise PreparationError("extracted text has no selectable source lines")
return fragments return fragments, locations
def build_prompt( def build_source_fragments(text: str) -> dict[str, str]:
fragments: dict[str, str], context: dict[str, Any], repair_error: str | None = None """Label non-empty source lines so the model selects text instead of copying it."""
) -> str: return build_source_fragment_index(text)[0]
def build_prompt(fragments: dict[str, str], context: dict[str, Any], repair_error: str | None = None) -> str:
candidates = json.dumps(context["candidate_claims"], indent=2, ensure_ascii=True) candidates = json.dumps(context["candidate_claims"], indent=2, ensure_ascii=True)
source = "\n".join(f"[{reference}] {line}" for reference, line in fragments.items()) source = "\n".join(f"[{reference}] {line}" for reference, line in fragments.items())
repair = "" repair = ""
@ -273,9 +290,7 @@ def parse_model_output(content: str) -> dict[str, Any]:
if not isinstance(claim, dict): if not isinstance(claim, dict):
raise PreparationError(f"model extraction claims[{index}] must be an object") raise PreparationError(f"model extraction claims[{index}] must be an object")
if set(claim) != MODEL_CLAIM_KEYS: if set(claim) != MODEL_CLAIM_KEYS:
raise PreparationError( raise PreparationError(f"model extraction claims[{index}] keys must equal {sorted(MODEL_CLAIM_KEYS)}")
f"model extraction claims[{index}] keys must equal {sorted(MODEL_CLAIM_KEYS)}"
)
confidence = claim.get("confidence") confidence = claim.get("confidence")
if confidence is not None and ( if confidence is not None and (
isinstance(confidence, bool) or not isinstance(confidence, (int, float)) or not 0 <= confidence <= 0.75 isinstance(confidence, bool) or not isinstance(confidence, (int, float)) or not 0 <= confidence <= 0.75
@ -304,27 +319,47 @@ def _resolve_reference(reference: Any, fragments: dict[str, str], label: str) ->
return fragments[reference] return fragments[reference]
def resolve_model_output(selection: dict[str, Any], fragments: dict[str, str]) -> dict[str, Any]: def resolve_model_output(
selection: dict[str, Any],
fragments: dict[str, str],
fragment_locations: dict[str, dict[str, Any]] | None = None,
) -> dict[str, Any]:
def selected(kind: str, reference: str, **identity: Any) -> dict[str, Any]:
result = {"kind": kind, "reference": reference, **identity}
if fragment_locations is not None:
result.update(fragment_locations[reference])
return result
claims: list[dict[str, Any]] = [] claims: list[dict[str, Any]] = []
selected_references: list[dict[str, str]] = [] selected_references: list[dict[str, Any]] = []
for claim_index, claim in enumerate(selection["claims"]): for claim_index, claim in enumerate(selection["claims"]):
quote_ref = claim["quote_ref"] quote_ref = claim["quote_ref"]
quote = _resolve_reference(quote_ref, fragments, f"claims[{claim_index}].quote_ref") quote = _resolve_reference(quote_ref, fragments, f"claims[{claim_index}].quote_ref")
selected_references.append({"kind": "claim", "reference": quote_ref}) selected_references.append(selected("claim", quote_ref, claim_key=claim["claim_key"], quote=quote))
evidence: list[dict[str, str]] = [] evidence: list[dict[str, str]] = []
for evidence_index, row in enumerate(claim["evidence"]): for evidence_index, row in enumerate(claim["evidence"]):
evidence_ref = row["quote_ref"] evidence_ref = row["quote_ref"]
evidence.append( evidence_quote = _resolve_reference(
{
"quote": _resolve_reference(
evidence_ref, evidence_ref,
fragments, fragments,
f"claims[{claim_index}].evidence[{evidence_index}].quote_ref", f"claims[{claim_index}].evidence[{evidence_index}].quote_ref",
), )
evidence.append(
{
"quote": evidence_quote,
"role": row["role"], "role": row["role"],
} }
) )
selected_references.append({"kind": "evidence", "reference": evidence_ref}) selected_references.append(
selected(
"evidence",
evidence_ref,
claim_key=claim["claim_key"],
evidence_index=evidence_index,
evidence_role=row["role"],
quote=evidence_quote,
)
)
claims.append( claims.append(
{ {
"claim_key": claim["claim_key"], "claim_key": claim["claim_key"],
@ -351,7 +386,14 @@ def resolve_model_output(selection: dict[str, Any], fragments: dict[str, str]) -
"reason": duplicate["reason"], "reason": duplicate["reason"],
} }
) )
selected_references.append({"kind": "duplicate", "reference": source_quote_ref}) selected_references.append(
selected(
"duplicate",
source_quote_ref,
claim_id=duplicate["claim_id"],
quote=duplicates[-1]["source_quote"],
)
)
return { return {
"schema": RESOLVED_OUTPUT_SCHEMA, "schema": RESOLVED_OUTPUT_SCHEMA,
@ -362,6 +404,37 @@ def resolve_model_output(selection: dict[str, Any], fragments: dict[str, str]) -
} }
def validate_bundle_source_locations(bundle: dict[str, Any], selected_references: list[dict[str, Any]]) -> None:
"""Ensure compiler quote offsets match the exact selected fragment occurrence."""
available = list(bundle.get("quote_bindings") or [])
for selected in selected_references:
if selected.get("kind") not in {"claim", "evidence"}:
continue
match_index = next(
(
index
for index, binding in enumerate(available)
if binding.get("kind") == selected.get("kind")
and binding.get("claim_key") == selected.get("claim_key")
and binding.get("quote") == selected.get("quote")
and (
selected.get("kind") != "evidence" or binding.get("evidence_role") == selected.get("evidence_role")
)
),
None,
)
if match_index is None:
raise PreparationError("compiler dropped a selected claim/evidence source binding")
binding = available.pop(match_index)
if binding.get("first_start") != selected.get("char_start") or binding.get("first_end") != selected.get(
"char_end"
):
raise PreparationError(
f"selected {selected['reference']} is an ambiguous repeated quote; "
"the current compiler cannot preserve that exact occurrence"
)
def build_manifest( def build_manifest(
*, *,
args: argparse.Namespace, args: argparse.Namespace,
@ -427,7 +500,7 @@ def prepare(args: argparse.Namespace) -> dict[str, Any]:
artifact_bytes = _read_nonempty(args.artifact, "artifact") artifact_bytes = _read_nonempty(args.artifact, "artifact")
text_bytes = extract_utf8_text(args.artifact, args.text) text_bytes = extract_utf8_text(args.artifact, args.text)
text = text_bytes.decode("utf-8", errors="strict") text = text_bytes.decode("utf-8", errors="strict")
fragments = build_source_fragments(text) fragments, fragment_locations = build_source_fragment_index(text)
context = _load_context(args.kb_context) context = _load_context(args.kb_context)
requested_output_dir = args.output_dir.expanduser() requested_output_dir = args.output_dir.expanduser()
@ -464,7 +537,7 @@ def prepare(args: argparse.Namespace) -> dict[str, Any]:
attempts.append({"attempt": attempt, "response_sha256": sha256_bytes(raw.encode("utf-8")), "usage": usage}) attempts.append({"attempt": attempt, "response_sha256": sha256_bytes(raw.encode("utf-8")), "usage": usage})
try: try:
selection = parse_model_output(raw) selection = parse_model_output(raw)
extraction = resolve_model_output(selection, fragments) extraction = resolve_model_output(selection, fragments, fragment_locations)
compiler._validate_dedupe( compiler._validate_dedupe(
{ {
"database_search_query": context["database_search_query"], "database_search_query": context["database_search_query"],
@ -494,6 +567,7 @@ def prepare(args: argparse.Namespace) -> dict[str, Any]:
with os.fdopen(fd, "wb") as handle: with os.fdopen(fd, "wb") as handle:
handle.write(_json_bytes(manifest)) handle.write(_json_bytes(manifest))
bundle = compiler.compile_source_packet(args.artifact, temp_text, temp_manifest) bundle = compiler.compile_source_packet(args.artifact, temp_text, temp_manifest)
validate_bundle_source_locations(bundle, extraction["selected_source_references"])
finally: finally:
if temp_text.exists(): if temp_text.exists():
temp_text.unlink() temp_text.unlink()
@ -518,6 +592,7 @@ def prepare(args: argparse.Namespace) -> dict[str, Any]:
raise PreparationError("prepared extraction has no manifest") raise PreparationError("prepared extraction has no manifest")
_write_private(manifest_path, _json_bytes(manifest)) _write_private(manifest_path, _json_bytes(manifest))
bundle = compiler.compile_source_packet(args.artifact, text_path, manifest_path) bundle = compiler.compile_source_packet(args.artifact, text_path, manifest_path)
validate_bundle_source_locations(bundle, extraction["selected_source_references"])
receipt = { receipt = {
"schema": RECEIPT_SCHEMA, "schema": RECEIPT_SCHEMA,
@ -545,10 +620,20 @@ def prepare(args: argparse.Namespace) -> dict[str, Any]:
"model": args.model, "model": args.model,
"attempts": attempts, "attempts": attempts,
"claim_count": len(extraction["claims"]), "claim_count": len(extraction["claims"]),
"evidence_count": sum(len(claim["evidence"]) for claim in extraction["claims"]),
"duplicate_judgment_count": len(extraction["duplicates"]),
"selectable_source_line_count": len(fragments), "selectable_source_line_count": len(fragments),
"selected_source_references": extraction["selected_source_references"], "selected_source_references": extraction["selected_source_references"],
"notes": extraction["extraction_notes"], "notes": extraction["extraction_notes"],
}, },
"replay": {
"artifact_sha256": artifact_sha256,
"extracted_text_sha256": text_sha256,
"kb_context_sha256": sha256_bytes(_json_bytes(context)),
"fragment_index_sha256": sha256_bytes(_json_bytes(fragment_locations)),
"extractor": {"name": f"openrouter:{args.model}", "version": EXTRACTOR_VERSION},
"exact_selected_locations_validated": bundle is not None,
},
"outputs": { "outputs": {
"output_dir": str(output_dir), "output_dir": str(output_dir),
"extracted_text": str(text_path), "extracted_text": str(text_path),

File diff suppressed because it is too large Load diff

View file

@ -57,6 +57,12 @@ def prepare_normalized_child(parent: dict[str, Any]) -> dict[str, Any]:
manifest = apply_payload.setdefault("normalization_manifest", {}) manifest = apply_payload.setdefault("normalization_manifest", {})
if not isinstance(manifest, dict): if not isinstance(manifest, dict):
raise bound.CheckpointError("normalized child normalization_manifest must be an object") raise bound.CheckpointError("normalized child normalization_manifest must be an object")
parent_payload = parent.get("payload")
ingestion_manifest = parent_payload.get("ingestion_manifest") if isinstance(parent_payload, dict) else None
if ingestion_manifest is not None:
if not isinstance(ingestion_manifest, dict):
raise bound.CheckpointError("parent payload.ingestion_manifest must be an object")
manifest["ingestion_manifest"] = json.loads(json.dumps(ingestion_manifest, sort_keys=True))
parent_packet_sha256 = canonical_sha256(parent) parent_packet_sha256 = canonical_sha256(parent)
manifest["parent_packet_sha256"] = parent_packet_sha256 manifest["parent_packet_sha256"] = parent_packet_sha256
child_payload_sha256 = canonical_sha256(payload) child_payload_sha256 = canonical_sha256(payload)

View file

@ -203,6 +203,15 @@ def _lines(completed: subprocess.CompletedProcess[str]) -> set[str]:
return {line for line in completed.stdout.splitlines() if line} return {line for line in completed.stdout.splitlines() if line}
def test_reviewer_principal_seed_timestamp_is_deterministic(migrated_postgres: str) -> None:
created_at = _psql(
migrated_postgres,
"select created_at::text from kb_stage.kb_review_principals where db_role = 'kb_review';",
).stdout.strip()
assert created_at == "1970-01-01 00:00:00+00"
def _catalog_snapshot(container: str) -> str: def _catalog_snapshot(container: str) -> str:
return _psql( return _psql(
container, container,

View file

@ -0,0 +1,346 @@
from __future__ import annotations
import asyncio
import json
from pathlib import Path
from aiohttp.test_utils import TestClient, TestServer
from observatory_read_adapter.app import create_app
from observatory_read_adapter.config import EXPECTED_CONNECTION_NAME, Settings
from observatory_read_adapter.db import CloudSqlClaimReader
CLAIM_ID = "d0000000-0000-0000-0000-000000000005"
API_KEY = "observatory-test-api-key-000000000000000000000000"
ROOT = Path(__file__).resolve().parents[1]
def settings(**overrides: object) -> Settings:
values = {
"project_id": "teleo-501523",
"instance_connection_name": EXPECTED_CONNECTION_NAME,
"database": "teleo_canonical",
"iam_database_user": "sa-observatory-read-adapter@teleo-501523.iam",
"authorization_role": "kb_observatory_read",
"api_key": API_KEY,
"service_revision": "test-revision",
}
values.update(overrides)
return Settings(**values)
class FakeReader:
def __init__(self) -> None:
self.closed = False
def check_ready(self) -> dict[str, object]:
return {
"ready": True,
"database": "teleo_canonical",
"authorization_role": "kb_observatory_read",
"transaction_read_only": True,
}
def read_claim(self, claim_id: str | None = None) -> dict[str, object] | None:
if claim_id not in (None, CLAIM_ID):
return None
return {
"schema": "livingip.observatory-canonical-claim.v1",
"read_only": True,
"provenance": {
"database": "teleo_canonical",
"database_principal": "sa-observatory-read-adapter@teleo-501523.iam",
"authorization_role": "kb_observatory_read",
"transaction_read_only": True,
"write_privileges_denied": True,
},
"canonical": {
"claim": {"id": CLAIM_ID, "status": "open", "text": "Canonical claim"},
"evidence": [{"source_id": "e0000000-0000-0000-0000-000000000006"}],
},
"proposal_ledger": {
"distinct_from_canonical": True,
"status_counts": {"pending_review": 2, "approved": 1, "applied": 1},
"related": [{"status": "approved", "applied_at": None}],
},
}
def close(self) -> None:
self.closed = True
class FakeCursor:
def __init__(self) -> None:
self.description: list[tuple[str]] = []
self.result: list[tuple[object, ...]] = []
self.executed: list[tuple[str, tuple[object, ...]]] = []
def execute(self, sql: str, parameters: tuple[object, ...] = ()) -> None:
normalized = " ".join(sql.split()).lower()
self.executed.append((normalized, parameters))
self.description = []
self.result = []
if "current_database() as database" in normalized:
self.description = [
("database",),
("database_principal",),
("transaction_read_only",),
("has_authorization_role",),
("has_required_reads",),
("required_writes_denied",),
]
self.result = [
(
"teleo_canonical",
"sa-observatory-read-adapter@teleo-501523.iam",
"on",
True,
True,
True,
)
]
elif "from public.claims c" in normalized:
self.description = [
("id",),
("type",),
("text",),
("status",),
("confidence",),
("tags",),
("superseded_by",),
("created_at",),
("updated_at",),
]
self.result = [
(
CLAIM_ID,
"strategy",
"Canonical claim",
"open",
0.8,
["test"],
None,
"2026-07-15T00:00:00+00:00",
"2026-07-15T00:00:00+00:00",
)
]
elif "from public.claim_evidence ce" in normalized:
self.description = [
("evidence_id",),
("role",),
("weight",),
("linked_at",),
("source_id",),
("source_type",),
("url",),
("storage_path",),
("excerpt",),
("content_hash",),
("captured_at",),
("source_created_at",),
]
self.result = [
(
"e0000000-0000-0000-0000-000000000006",
"grounds",
1.0,
"2026-07-15T00:00:00+00:00",
"f0000000-0000-0000-0000-000000000007",
"document",
"https://example.test/source",
None,
"Source excerpt",
"sha256:test",
"2026-07-15T00:00:00+00:00",
"2026-07-15T00:00:00+00:00",
)
]
elif "from public.claim_edges e" in normalized:
self.description = [
("edge_id",),
("direction",),
("edge_type",),
("weight",),
("connected_claim_id",),
("created_at",),
]
self.result = []
elif "group by status" in normalized:
self.description = [("status",), ("count",)]
self.result = [("approved", 1), ("applied", 1), ("pending_review", 2)]
elif "where p.payload::text like" in normalized:
self.description = [
("id",),
("proposal_type",),
("status",),
("source_ref",),
("reviewed_at",),
("applied_at",),
("updated_at",),
]
self.result = [
(
"a0000000-0000-0000-0000-000000000001",
"revise_claim",
"approved",
"source:test",
"2026-07-15T00:00:00+00:00",
None,
"2026-07-15T00:00:00+00:00",
)
]
elif "from kb_stage.kb_proposals p" in normalized:
self.description = [
("id",),
("proposal_type",),
("status",),
("source_ref",),
("reviewed_at",),
("applied_at",),
("updated_at",),
]
self.result = []
def fetchone(self) -> tuple[object, ...] | None:
return self.result.pop(0) if self.result else None
def fetchall(self) -> list[tuple[object, ...]]:
result, self.result = self.result, []
return result
class FakeConnection:
def __init__(self) -> None:
self.cursor_value = FakeCursor()
self.rolled_back = False
self.closed = False
def cursor(self) -> FakeCursor:
return self.cursor_value
def rollback(self) -> None:
self.rolled_back = True
def close(self) -> None:
self.closed = True
def test_settings_fail_closed_on_wrong_database_role_or_short_key() -> None:
for overrides in (
{"database": "teleo"},
{"authorization_role": "leoclean_kb_runtime"},
{"api_key": "too-short"},
{"instance_connection_name": "other:region:instance"},
):
candidate = settings(**overrides)
try:
candidate.validate()
except ValueError:
pass
else:
raise AssertionError(f"unsafe settings accepted: {overrides}")
def test_authenticated_claim_response_and_negative_http_paths() -> None:
async def exercise() -> None:
reader = FakeReader()
client = TestClient(TestServer(create_app(settings(), reader=reader)))
await client.start_server()
try:
anonymous = await client.get("/v1/claims/sample")
assert anonymous.status == 401
assert await anonymous.json() == {"error": "unauthorized"}
response = await client.get(
f"/v1/claims/{CLAIM_ID}",
headers={"X-Api-Key": API_KEY},
)
assert response.status == 200
assert response.headers["Cache-Control"] == "private, no-store, max-age=0"
payload = await response.json()
assert payload["schema"] == "livingip.observatory-canonical-claim.v1"
assert payload["provenance"]["database"] == "teleo_canonical"
assert payload["provenance"]["write_privileges_denied"] is True
assert payload["canonical"]["evidence"]
assert payload["proposal_ledger"]["distinct_from_canonical"] is True
assert payload["proposal_ledger"]["related"][0]["applied_at"] is None
assert API_KEY not in json.dumps(payload)
write_attempt = await client.post(
f"/v1/claims/{CLAIM_ID}",
headers={"X-Api-Key": API_KEY},
json={"status": "applied"},
)
assert write_attempt.status == 405
invalid = await client.get(
"/v1/claims/not-a-uuid",
headers={"X-Api-Key": API_KEY},
)
assert invalid.status == 400
finally:
await client.close()
assert reader.closed is True
asyncio.run(exercise())
def test_readiness_checks_database_contract_without_exposing_claims() -> None:
async def exercise() -> None:
client = TestClient(TestServer(create_app(settings(), reader=FakeReader())))
await client.start_server()
try:
response = await client.get("/readyz")
payload = await response.json()
assert response.status == 200
assert payload == {"status": "ready"}
finally:
await client.close()
asyncio.run(exercise())
def test_database_reader_returns_provenance_and_keeps_proposals_distinct() -> None:
connection = FakeConnection()
reader = CloudSqlClaimReader(settings(), connection_factory=lambda: connection)
payload = reader.read_claim(CLAIM_ID)
assert payload is not None
assert payload["provenance"]["database"] == "teleo_canonical"
assert payload["provenance"]["database_principal"] == settings().iam_database_user
assert payload["provenance"]["write_privileges_denied"] is True
assert payload["canonical"]["claim"]["id"] == CLAIM_ID
assert payload["canonical"]["evidence"][0]["content_hash"] == "sha256:test"
assert payload["proposal_ledger"]["distinct_from_canonical"] is True
assert payload["proposal_ledger"]["related"][0]["status"] == "approved"
assert payload["proposal_ledger"]["related"][0]["applied_at"] is None
assert connection.rolled_back is True
assert connection.closed is True
assert any("set transaction read only" in sql for sql, _parameters in connection.cursor_value.executed)
def test_database_role_contract_is_select_only_and_iam_scoped() -> None:
sql = (ROOT / "ops" / "observatory_read_role.sql").read_text(encoding="utf-8")
lowered = sql.lower()
assert "kb_observatory_read nologin" in lowered
assert "default_transaction_read_only = on" in lowered
assert "public.claims" in lowered
assert "public.sources" in lowered
assert "public.claim_evidence" in lowered
assert "public.claim_edges" in lowered
assert "kb_stage.kb_proposals" in lowered
assert "grant select on" in lowered
assert "grant insert" not in lowered
assert "grant update" not in lowered
assert "grant delete" not in lowered
assert "effective_table_writes_denied" in lowered
def test_container_runs_as_non_root_and_contains_no_password_contract() -> None:
dockerfile = (ROOT / "Dockerfile.observatory-read-adapter").read_text(encoding="utf-8")
assert "USER 10001:10001" in dockerfile
assert "PGPASSWORD" not in dockerfile
assert "DB_PASS" not in dockerfile

View file

@ -0,0 +1,182 @@
from __future__ import annotations
import shutil
import subprocess
import time
import uuid
from pathlib import Path
import pytest
ROOT = Path(__file__).resolve().parents[1]
ROLE_SQL = ROOT / "ops" / "observatory_read_role.sql"
POSTGRES_IMAGE = "postgres:16-alpine"
DATABASE = "teleo_canonical"
IAM_USER = "sa-observatory-read-adapter@teleo-501523.iam"
TEST_PASSWORD = "generated-test-only-password"
BOOTSTRAP_SQL = f"""
begin;
create schema kb_stage;
create role "{IAM_USER}" login inherit password '{TEST_PASSWORD}';
create table public.claims (id uuid primary key);
create table public.sources (id uuid primary key);
create table public.claim_evidence (id uuid primary key);
create table public.claim_edges (id uuid primary key);
create table public.private_notes (id uuid primary key);
create table kb_stage.kb_proposals (id uuid primary key);
insert into public.claims values ('11111111-1111-1111-1111-111111111111');
commit;
"""
def run(command: list[str], *, input_text: str | None = None, check: bool = True) -> subprocess.CompletedProcess[str]:
return subprocess.run(
command,
input=input_text,
text=True,
capture_output=True,
check=check,
timeout=30,
)
@pytest.mark.skipif(shutil.which("docker") is None, reason="Docker is required")
def test_observatory_role_is_read_only_and_exactly_allowlisted() -> None:
container = f"teleo-observatory-role-{uuid.uuid4().hex[:10]}"
run(
[
"docker",
"run",
"--detach",
"--rm",
"--name",
container,
"--env",
f"POSTGRES_PASSWORD={TEST_PASSWORD}",
"--env",
f"POSTGRES_DB={DATABASE}",
POSTGRES_IMAGE,
]
)
try:
deadline = time.monotonic() + 25
while time.monotonic() < deadline:
ready = run(
["docker", "exec", container, "pg_isready", "-U", "postgres", "-d", DATABASE],
check=False,
)
if ready.returncode == 0:
break
time.sleep(0.25)
else:
raise AssertionError("ephemeral Postgres did not become ready")
bootstrap: subprocess.CompletedProcess[str] | None = None
deadline = time.monotonic() + 15
while time.monotonic() < deadline:
bootstrap = run(
[
"docker",
"exec",
"-i",
container,
"psql",
"--set",
"ON_ERROR_STOP=1",
"-U",
"postgres",
"-d",
DATABASE,
],
input_text=BOOTSTRAP_SQL,
check=False,
)
if bootstrap.returncode == 0:
break
time.sleep(0.25)
else:
assert bootstrap is not None
raise AssertionError(f"ephemeral Postgres bootstrap failed: {bootstrap.stderr[-1000:]}")
migration = run(
[
"docker",
"exec",
"-e",
f"OBSERVATORY_DB_IAM_USER={IAM_USER}",
"-i",
container,
"psql",
"-U",
"postgres",
"-d",
DATABASE,
],
input_text=ROLE_SQL.read_text(encoding="utf-8"),
)
assert '"effective_table_writes_denied": true' in migration.stdout
readback = run(
[
"docker",
"exec",
"-e",
f"PGPASSWORD={TEST_PASSWORD}",
container,
"psql",
"-h",
"127.0.0.1",
"-U",
IAM_USER,
"-d",
DATABASE,
"-At",
"-c",
"show default_transaction_read_only; select count(*) from public.claims;",
]
)
assert readback.stdout.splitlines() == ["on", "1"]
write_attempt = run(
[
"docker",
"exec",
"-e",
f"PGPASSWORD={TEST_PASSWORD}",
container,
"psql",
"-h",
"127.0.0.1",
"-U",
IAM_USER,
"-d",
DATABASE,
"-c",
"insert into public.claims values ('22222222-2222-2222-2222-222222222222');",
],
check=False,
)
assert write_attempt.returncode != 0
outside_allowlist = run(
[
"docker",
"exec",
"-e",
f"PGPASSWORD={TEST_PASSWORD}",
container,
"psql",
"-h",
"127.0.0.1",
"-U",
IAM_USER,
"-d",
DATABASE,
"-c",
"select * from public.private_notes;",
],
check=False,
)
assert outside_allowlist.returncode != 0
finally:
run(["docker", "rm", "--force", container], check=False)

View file

@ -123,11 +123,17 @@ def test_prepare_builds_private_compiler_validated_manifest_without_db_write(
assert manifest["dedupe"]["duplicates"][0]["claim_id"] == CANDIDATE_ID assert manifest["dedupe"]["duplicates"][0]["claim_id"] == CANDIDATE_ID
assert manifest["claims"][0]["quote"] == CLAIM_QUOTE assert manifest["claims"][0]["quote"] == CLAIM_QUOTE
assert manifest["dedupe"]["duplicates"][0]["source_quote"] == DUPLICATE_QUOTE assert manifest["dedupe"]["duplicates"][0]["source_quote"] == DUPLICATE_QUOTE
assert receipt["extraction"]["selected_source_references"] == [ selected = receipt["extraction"]["selected_source_references"]
{"kind": "claim", "reference": "S00003"}, assert [(row["kind"], row["reference"]) for row in selected] == [
{"kind": "evidence", "reference": "S00003"}, ("claim", "S00003"),
{"kind": "duplicate", "reference": "S00002"}, ("evidence", "S00003"),
("duplicate", "S00002"),
] ]
assert all(row["char_end"] > row["char_start"] for row in selected)
assert all(len(row["quote_sha256"]) == 64 for row in selected)
assert receipt["replay"]["exact_selected_locations_validated"] is True
assert receipt["extraction"]["evidence_count"] == 1
assert receipt["extraction"]["duplicate_judgment_count"] == 1
assert stat.S_IMODE(output_dir.stat().st_mode) == 0o700 assert stat.S_IMODE(output_dir.stat().st_mode) == 0o700
for path in output_dir.iterdir(): for path in output_dir.iterdir():
assert stat.S_IMODE(path.stat().st_mode) == 0o600 assert stat.S_IMODE(path.stat().st_mode) == 0o600
@ -173,6 +179,63 @@ def test_source_fragment_ids_are_dense_across_blank_lines() -> None:
assert fragments == {"S00001": "first", "S00002": "second"} assert fragments == {"S00001": "first", "S00002": "second"}
def test_source_fragment_index_trims_indentation_but_preserves_exact_offsets() -> None:
text = " indented Unicode: \u201creviewed\u201d \nnext\n"
fragments, locations = preparer.build_source_fragment_index(text)
assert fragments["S00001"] == "indented Unicode: \u201creviewed\u201d"
selected = locations["S00001"]
assert selected["line"] == 1
assert text[selected["char_start"] : selected["char_end"]] == fragments["S00001"]
def test_bundle_location_validation_rejects_ambiguous_rebound() -> None:
text = "same line\nsame line\n"
fragments, locations = preparer.build_source_fragment_index(text)
extraction = preparer.resolve_model_output(
{
"claims": [
{
"claim_key": "second_occurrence",
"type": "structural",
"text": "The second occurrence was selected.",
"quote_ref": "S00002",
"confidence": 0.5,
"tags": [],
"evidence": [{"quote_ref": "S00002", "role": "grounds"}],
}
],
"duplicates": [],
"extraction_notes": "Select the second repeated line.",
},
fragments,
locations,
)
fake_bundle = {
"quote_bindings": [
{
"kind": "claim",
"claim_key": "second_occurrence",
"quote": "same line",
"first_start": 0,
"first_end": 9,
},
{
"kind": "evidence",
"claim_key": "second_occurrence",
"evidence_role": "grounds",
"quote": "same line",
"first_start": 0,
"first_end": 9,
},
]
}
with pytest.raises(preparer.PreparationError, match="ambiguous repeated quote"):
preparer.validate_bundle_source_locations(fake_bundle, extraction["selected_source_references"])
def test_prepare_returns_no_novel_claims_without_manifest_or_stageable_packet( def test_prepare_returns_no_novel_claims_without_manifest_or_stageable_packet(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None: ) -> None:
@ -195,6 +258,13 @@ def test_binary_artifact_requires_explicit_utf8_extraction(tmp_path: Path) -> No
preparer.extract_utf8_text(artifact, None) preparer.extract_utf8_text(artifact, None)
def test_repo_native_jsonl_transcript_is_accepted_as_strict_utf8(tmp_path: Path) -> None:
artifact = tmp_path / "telegram.jsonl"
artifact.write_text('{"chat_id":1,"message_id":2,"message":"hello"}\n', encoding="utf-8")
assert preparer.extract_utf8_text(artifact, None) == artifact.read_bytes()
def test_openrouter_key_cannot_be_redirected_to_an_unapproved_endpoint(tmp_path: Path) -> None: def test_openrouter_key_cannot_be_redirected_to_an_unapproved_endpoint(tmp_path: Path) -> None:
with pytest.raises(preparer.PreparationError, match=r"must equal https://openrouter\.ai"): with pytest.raises(preparer.PreparationError, match=r"must equal https://openrouter\.ai"):
preparer.call_openrouter( preparer.call_openrouter(

View file

@ -1,6 +1,8 @@
from __future__ import annotations from __future__ import annotations
import json import json
import re
import subprocess
from pathlib import Path from pathlib import Path
ROOT = Path(__file__).resolve().parents[1] ROOT = Path(__file__).resolve().parents[1]
@ -15,12 +17,12 @@ def _skill(name: str) -> str:
def test_manifest_indexes_valid_repo_native_skills() -> None: def test_manifest_indexes_valid_repo_native_skills() -> None:
manifest = json.loads(MANIFEST.read_text(encoding="utf-8")) manifest = json.loads(MANIFEST.read_text(encoding="utf-8"))
assert manifest["status"] == "repo_native_validated" assert manifest["status"] == "repo_native_path_validated"
assert manifest["repo_skill_root"] == ".agents/skills" assert manifest["repo_skill_root"] == ".agents/skills"
assert manifest["validation"] == "tests/test_repo_skill_pack.py" assert manifest["validation"] == "tests/test_repo_skill_pack.py"
assert "37/37" in manifest["claim_ceiling"] assert "37/37" in manifest["claim_ceiling"]
assert "auto-synchronized" in manifest["claim_ceiling"] assert "PRs #146 and #147 are merged" in manifest["claim_ceiling"]
assert "apply worker remained disabled/inactive" in manifest["claim_ceiling"] assert "PR #148 is open candidate evidence only" in manifest["claim_ceiling"]
for entry in manifest["skills"]: for entry in manifest["skills"]:
path = ROOT / entry["path"] path = ROOT / entry["path"]
@ -34,6 +36,139 @@ def test_manifest_indexes_valid_repo_native_skills() -> None:
assert (ROOT / relative).is_file(), relative assert (ROOT / relative).is_file(), relative
def test_manifest_covers_the_required_onboarding_surface() -> None:
manifest = json.loads(MANIFEST.read_text(encoding="utf-8"))
expected = {
"company_product_context",
"vps",
"gcp",
"hermes_runtime",
"database_provenance",
"ingestion",
"proposal_apply",
"reconstruction",
"identity",
"testing",
"security",
"secrets",
"incident_recovery",
}
assert set(manifest["required_coverage"]) == expected
skill_names = {entry["name"] for entry in manifest["skills"]}
repo_skill_names = {path.parent.name for path in (ROOT / ".agents" / "skills").glob("*/SKILL.md")}
assert skill_names == repo_skill_names
for owners in manifest["required_coverage"].values():
assert owners
assert set(owners) <= skill_names
def test_path_validator_and_clean_installer() -> None:
manifest = json.loads(MANIFEST.read_text(encoding="utf-8"))
validator = ROOT / manifest["validator"]
result = subprocess.run(
[str(validator), "--root", str(ROOT)],
check=False,
capture_output=True,
text=True,
)
assert result.returncode == 0, result.stdout + result.stderr
payload = json.loads(result.stdout)
assert payload["status"] == "pass"
assert payload["skill_count"] == len(manifest["skills"])
assert payload["coverage_area_count"] == len(manifest["required_coverage"])
assert payload["problems"] == []
def test_installer_copies_only_manifest_skills(tmp_path: Path) -> None:
manifest = json.loads(MANIFEST.read_text(encoding="utf-8"))
installer = ROOT / manifest["installer"]
target = tmp_path / "skills"
result = subprocess.run(
[str(installer), "--root", str(ROOT), "--target", str(target)],
check=False,
capture_output=True,
text=True,
)
assert result.returncode == 0, result.stdout + result.stderr
payload = json.loads(result.stdout)
assert payload["status"] == "pass"
assert payload["installed_skill_count"] == len(manifest["skills"])
assert {path.name for path in target.iterdir()} == {entry["name"] for entry in manifest["skills"]}
assert all((target / entry["name"] / "SKILL.md").is_file() for entry in manifest["skills"])
def test_installer_rejects_manifest_path_traversal(tmp_path: Path) -> None:
manifest = json.loads(MANIFEST.read_text(encoding="utf-8"))
manifest["skills"][0]["name"] = "../escape"
malicious_manifest = tmp_path / "manifest.json"
malicious_manifest.write_text(json.dumps(manifest), encoding="utf-8")
target = tmp_path / "target" / "skills"
result = subprocess.run(
[
str(ROOT / manifest["installer"]),
"--root",
str(ROOT),
"--manifest",
str(malicious_manifest),
"--target",
str(target),
],
check=False,
capture_output=True,
text=True,
)
assert result.returncode == 1
assert "invalid skill name" in result.stdout
assert not (tmp_path / "target" / "escape").exists()
assert not target.exists()
def test_isolated_install_canary_reaches_t2_without_claiming_agent_reasoning(tmp_path: Path) -> None:
manifest = json.loads(MANIFEST.read_text(encoding="utf-8"))
canary = ROOT / manifest["isolated_install_canary"]
output = tmp_path / "receipt.json"
result = subprocess.run(
[str(canary), "--root", str(ROOT), "--output", str(output)],
check=False,
capture_output=True,
text=True,
)
assert result.returncode == 0, result.stdout + result.stderr
payload = json.loads(output.read_text(encoding="utf-8"))
assert payload["status"] == "pass"
assert payload["required_tier"] == "T2_runtime"
assert payload["current_tier"] == "T2_runtime"
assert payload["proof_scope"] == "deterministic_isolated_install_and_route_validation"
assert payload["fresh_temporary_skill_root"] is True
assert payload["ambient_skill_root_used"] is False
assert payload["agent_reasoning_exercised"] is False
assert payload["installed_skill_count"] == len(manifest["skills"])
assert all(payload["semantic_checks"].values())
assert payload["canary_exit_status"] == 0
assert payload["cleanup_readback"]["temporary_skill_root_removed"] is True
assert payload["missing_to_reach_required_tier"] == []
def test_fresh_agent_receipt_rejects_stale_routes() -> None:
manifest = json.loads(MANIFEST.read_text(encoding="utf-8"))
receipt = json.loads((ROOT / manifest["fresh_agent_receipt"]).read_text(encoding="utf-8"))
assert receipt["status"] == "pass"
assert receipt["evidence_origin"]["fork_turns"] == "none"
assert receipt["evidence_origin"]["memory_used"] is False
assert all(receipt["stale_claims_rejected"].values())
assert receipt["validator"]["status"] == "pass"
assert receipt["validator"]["skill_count"] == 16
assert receipt["repository_readback"]["unchanged"] is True
assert receipt["cleanup_readback"]["temporary_skill_root_removed"] is True
assert receipt["external_runtime_contacted"] is False
assert receipt["production_mutation"] is False
assert receipt["contains_secrets"] is False
assert receipt["problems"] == []
def test_db_change_skill_tracks_guarded_v2_runtime_proof() -> None: def test_db_change_skill_tracks_guarded_v2_runtime_proof() -> None:
text = _skill("teleo-kb-db-change-workflow") text = _skill("teleo-kb-db-change-workflow")
squashed = " ".join(text.split()) squashed = " ".join(text.split())
@ -120,3 +255,31 @@ def test_vps_onboarding_and_m3taversal_skills_point_to_current_proof() -> None:
assert "Next proof-changing follow-up:" in outcomes assert "Next proof-changing follow-up:" in outcomes
assert "working-leo-current-proof-20260712.md" in outcomes assert "working-leo-current-proof-20260712.md" in outcomes
assert "Cory" not in outcomes assert "Cory" not in outcomes
def test_reconstruction_and_pr_state_are_routed_without_candidate_overclaim() -> None:
reconstruction = _skill("teleo-reconstruction-recovery")
onboarding = _skill("teleo-leo-onboarding")
gcp = _skill("teleo-gcp-parity-ops")
current_truth = (REPORT_DIR / "current-truth-index.md").read_text(encoding="utf-8")
assert "ops/run_local_genesis_ledger_rebuild.py" in reconstruction
assert "scripts/verify_leo_unseen_reasoning_chain.py" in reconstruction
assert "Semantic source-to-blank-database recompilation remains incomplete" in reconstruction
assert "PRs #146 and #147 are merged repository truth" in onboarding
assert "PR #148 remains an open GCP" in onboarding
assert "candidate evidence only" in gcp
assert "PRs #146 and #147 are merged into canonical `main`" in current_truth
assert "PR #148 remains open candidate evidence" in current_truth
def test_active_onboarding_prose_uses_exact_m3taversal_identity() -> None:
manifest = json.loads(MANIFEST.read_text(encoding="utf-8"))
active_files = [
*[ROOT / relative for relative in manifest["scan_files"]],
*[ROOT / entry["path"] for entry in manifest["skills"]],
]
for path in active_files:
text = path.read_text(encoding="utf-8")
prose = re.sub(r"`[^`]*\/[^`]+\.(?:md|json|svg)`", "", text)
assert re.search(r"\bcory(?:-style)?\b", prose, flags=re.IGNORECASE) is None, path

View file

@ -1,6 +1,7 @@
from __future__ import annotations from __future__ import annotations
import copy import copy
import hashlib
import json import json
import sys import sys
from pathlib import Path from pathlib import Path
@ -13,90 +14,420 @@ sys.path.insert(0, str(REPO_ROOT / "scripts"))
import run_leo_local_ingestion_proposal_canary as canary # noqa: E402 import run_leo_local_ingestion_proposal_canary as canary # noqa: E402
def _loaded() -> tuple[dict, dict, dict]: def _loaded(path: Path) -> tuple[dict, dict, dict, dict, dict]:
fixture, input_receipt = canary.load_fixture(canary.DEFAULT_FIXTURE) fixture, input_receipt = canary.load_fixture(path)
row_ids = canary.build_row_ids(input_receipt["artifact_sha256"]) row_ids = canary.build_row_ids(input_receipt, fixture)
return fixture, input_receipt, row_ids
def test_fixture_is_realistic_hash_bound_and_exactly_evidenced() -> None:
fixture, input_receipt, row_ids = _loaded()
assert fixture["extraction"]["evidence"]["body"] in fixture["source"]["body"]
assert len(input_receipt["artifact_sha256"]) == 64
assert len(input_receipt["source_content_sha256"]) == 64
assert input_receipt["artifact_sha256"] != input_receipt["source_content_sha256"]
assert len(set(row_ids.values())) == 4
def test_fixture_validation_fails_closed_when_evidence_is_not_in_source(tmp_path: Path) -> None:
fixture = json.loads(canary.DEFAULT_FIXTURE.read_text(encoding="utf-8"))
fixture["extraction"]["evidence"]["body"] = "invented evidence"
path = tmp_path / "bad-fixture.json"
path.write_text(json.dumps(fixture), encoding="utf-8")
with pytest.raises(canary.CanaryError, match="exact substring"):
canary.load_fixture(path)
def test_fixture_validation_fails_closed_when_claim_body_is_not_in_source(
tmp_path: Path,
) -> None:
fixture = json.loads(canary.DEFAULT_FIXTURE.read_text(encoding="utf-8"))
fixture["extraction"]["claim"]["body"] = "invented claim body"
path = tmp_path / "bad-claim-fixture.json"
path.write_text(json.dumps(fixture), encoding="utf-8")
with pytest.raises(canary.CanaryError, match="claim body must be an exact substring"):
canary.load_fixture(path)
def test_capture_sql_escapes_quotes_and_backslashes_from_fixture(
tmp_path: Path,
) -> None:
fixture = json.loads(canary.DEFAULT_FIXTURE.read_text(encoding="utf-8"))
fixture["source"]["title"] = "O'Brien\\review"
fixture["source"]["metadata"]["note"] = "quoted ' value \\ path"
path = tmp_path / "quoted-fixture.json"
path.write_text(json.dumps(fixture), encoding="utf-8")
loaded, input_receipt = canary.load_fixture(path)
row_ids = canary.build_row_ids(input_receipt["artifact_sha256"])
sql = canary.build_capture_sql(loaded, input_receipt, row_ids)
assert canary.ap.sql_literal(fixture["source"]["title"]) in sql
assert canary.ap.sql_literal(
json.dumps(fixture["source"]["metadata"], sort_keys=True)
) in sql
def test_parent_normalizes_to_hash_bound_pending_proposal_with_embedded_row_links() -> None:
fixture, input_receipt, row_ids = _loaded()
parent = canary.build_parent_packet(fixture, input_receipt, row_ids) parent = canary.build_parent_packet(fixture, input_receipt, row_ids)
child = canary.normalized_stage.prepare_normalized_child(parent) child = canary.normalized_stage.prepare_normalized_child(parent)
payload = child["payload"]["apply_payload"] return fixture, input_receipt, row_ids, parent, child
assert child["status"] == "pending_review"
assert child["proposal_type"] == "approve_claim"
assert len(payload["claims"]) == 1
assert len(payload["sources"]) == 2
assert len(payload["evidence"]) == 2
assert input_receipt["source_content_sha256"] in {row["hash"] for row in payload["sources"]}
excerpts = "\n".join(row["excerpt"] for row in payload["sources"])
assert input_receipt["artifact_sha256"] in excerpts
assert row_ids["source_capture_id"] in excerpts
assert row_ids["claim_extraction_id"] in excerpts
assert row_ids["evidence_extraction_id"] in excerpts
def test_capture_and_stage_sql_never_mutate_canonical_public_tables() -> None: def _copy_scenario(tmp_path: Path, scenario_path: Path) -> Path:
fixture, input_receipt, row_ids = _loaded() scenario = json.loads(scenario_path.read_text(encoding="utf-8"))
child = canary.normalized_stage.prepare_normalized_child( artifact_source = scenario_path.parent / scenario["artifact"]["path"]
canary.build_parent_packet(fixture, input_receipt, row_ids) artifact_target = tmp_path / artifact_source.name
artifact_target.write_bytes(artifact_source.read_bytes())
scenario_target = tmp_path / scenario_path.name
scenario_target.write_text(json.dumps(scenario), encoding="utf-8")
return scenario_target
def _canonical_snapshot() -> dict:
return {
"claims": [{"id": "1"}],
"sources": [{"id": "2"}],
"claim_evidence": [{"claim_id": "1"}],
"claim_edges": [{"from_claim": "1"}],
}
def _row_id_values(row_ids: dict) -> set[str]:
values: set[str] = set()
for value in row_ids.values():
if isinstance(value, dict):
values.update(_row_id_values(value))
else:
values.add(value)
return values
def _planned_uuid_values(child: dict) -> set[str]:
apply_payload = child["payload"]["apply_payload"]
values = {child["id"]}
for row in apply_payload["claims"] + apply_payload["sources"]:
values.add(row["id"])
for row in apply_payload["evidence"]:
values.update((row["claim_id"], row["source_id"]))
for row in apply_payload["edges"]:
values.update((row["from_claim"], row["to_claim"]))
return values
def _synthetic_readback(fixture: dict, input_receipt: dict, row_ids: dict, child: dict) -> dict:
claim_rows = []
evidence_rows = []
segment_by_id = {segment["id"]: segment for segment in fixture["segments"]}
for claim in fixture["extraction"]["claims"]:
claim_id = row_ids["claim_extraction_ids"][claim["claim_key"]]
claim_rows.append(
{
"id": claim_id,
"source_capture_id": row_ids["source_capture_id"],
"claim_key": claim["claim_key"],
"body": claim["body"],
"metadata": {
**claim["metadata"],
"claim_type": claim["type"],
"confidence": claim["confidence"],
"text": claim["text"],
"extractor": input_receipt["extractor"],
"replay_identity_sha256": input_receipt["replay_identity_sha256"],
},
}
) )
for index, evidence in enumerate(claim["evidence"]):
segment = segment_by_id[evidence["segment_id"]]
evidence_rows.append(
{
"id": row_ids["evidence_extraction_ids"][f"{claim['claim_key']}:{index}"],
"claim_extraction_id": claim_id,
"source_capture_id": row_ids["source_capture_id"],
"source_segment_id": segment["id"],
"source_locator": segment["locator"],
"source_json_pointer": segment["json_pointer"],
"role": evidence["role"],
"source_content_sha256": segment["content_sha256"],
"body": evidence["excerpt"],
"body_sha256": canary.sha256_bytes(evidence["excerpt"].encode()),
"metadata": {
**evidence.get("metadata", {}),
"source_text_sha256": segment["text_sha256"],
},
}
)
duplicate_rows = []
for index, duplicate in enumerate(fixture["extraction"]["duplicates"]):
duplicate_rows.append(
{
"id": row_ids["duplicate_assessment_ids"][str(index)],
"source_capture_id": row_ids["source_capture_id"],
"source_segment_id": duplicate["segment_id"],
"source_locator": duplicate["source_locator"],
"duplicate_of_claim_key": duplicate["duplicate_of_claim_key"],
"excerpt": duplicate["excerpt"],
"excerpt_sha256": canary.sha256_bytes(duplicate["excerpt"].encode()),
"reason": duplicate["reason"],
"metadata": {
"json_pointer": duplicate["source_json_pointer"],
"source_content_sha256": duplicate["source_content_sha256"],
"source_text_sha256": duplicate["source_text_sha256"],
},
}
)
conflict_rows = []
for index, conflict in enumerate(fixture["extraction"]["conflicts"]):
conflict_rows.append(
{
"id": row_ids["conflict_assessment_ids"][str(index)],
"source_capture_id": row_ids["source_capture_id"],
"from_claim_key": conflict["from_claim_key"],
"to_claim_key": conflict["to_claim_key"],
"relationship": conflict["relationship"],
"metadata": {
key: value
for key, value in conflict.items()
if key not in {"from_claim_key", "to_claim_key", "relationship"}
},
}
)
proposal = {
**child,
"reviewed_by_handle": None,
"reviewed_at": None,
"applied_by_handle": None,
"applied_at": None,
}
counts = input_receipt["counts"]
return {
"source_captures": [
{
"id": row_ids["source_capture_id"],
"source_identity": fixture["source"]["identity"],
"source_type": fixture["source"]["source_type"],
"title": fixture["source"]["title"],
"locator": fixture["source"]["locator"],
"artifact_path": input_receipt["artifact_path"],
"artifact_sha256": input_receipt["artifact_sha256"],
"content_sha256": input_receipt["source_content_sha256"],
"replay_identity_sha256": input_receipt["replay_identity_sha256"],
"metadata": {**fixture["source"]["metadata"], "extractor": input_receipt["extractor"]},
}
],
"claim_extractions": claim_rows,
"evidence_extractions": evidence_rows,
"duplicate_assessments": duplicate_rows,
"conflict_assessments": conflict_rows,
"staged_proposal": proposal,
"counts": {
"source_captures": 1,
"claim_extractions": counts["claim_candidates"],
"evidence_extractions": counts["evidence_candidates"],
"duplicate_assessments": counts["duplicate_judgments"],
"conflict_assessments": counts["conflict_relationships"],
"staged_proposals": 1,
},
}
@pytest.mark.parametrize("scenario_path", canary.DEFAULT_FIXTURES)
def test_source_artifact_is_separate_hash_bound_and_replayable(scenario_path: Path) -> None:
fixture, input_receipt, row_ids, parent, child = _loaded(scenario_path)
artifact_path = Path(input_receipt["artifact_path"])
assert input_receipt["artifact_sha256"] == hashlib.sha256(artifact_path.read_bytes()).hexdigest()
assert input_receipt["artifact_sha256"] != input_receipt["scenario_sha256"]
assert len(input_receipt["replay_identity_sha256"]) == 64
assert row_ids == canary.build_row_ids(input_receipt, fixture)
ingestion = child["payload"]["apply_payload"]["normalization_manifest"]["ingestion_manifest"]
assert ingestion["artifact_sha256"] == input_receipt["artifact_sha256"]
assert ingestion["extractor"] == input_receipt["extractor"]
assert ingestion["replay_identity_sha256"] == input_receipt["replay_identity_sha256"]
assert ingestion["row_ids"] == row_ids
assert parent["status"] == child["status"] == "pending_review"
def test_document_and_telegram_candidate_accounting_is_exact() -> None:
_doc, doc_input, _doc_ids, _doc_parent, doc_child = _loaded(canary.DEFAULT_DOCUMENT_FIXTURE)
telegram, telegram_input, _telegram_ids, _telegram_parent, telegram_child = _loaded(canary.DEFAULT_TELEGRAM_FIXTURE)
assert doc_input["counts"] == {
"source_segments": 3,
"claim_candidates": 1,
"evidence_candidates": 1,
"duplicate_judgments": 0,
"conflict_relationships": 0,
}
assert telegram_input["counts"] == {
"source_segments": 3,
"claim_candidates": 2,
"evidence_candidates": 3,
"duplicate_judgments": 1,
"conflict_relationships": 1,
}
doc_payload = doc_child["payload"]["apply_payload"]
telegram_payload = telegram_child["payload"]["apply_payload"]
assert {key: len(doc_payload[key]) for key in ("claims", "sources", "evidence", "edges")} == {
"claims": 1,
"sources": 2,
"evidence": 2,
"edges": 0,
}
assert {key: len(telegram_payload[key]) for key in ("claims", "sources", "evidence", "edges")} == {
"claims": 2,
"sources": 5,
"evidence": 5,
"edges": 1,
}
assessment = telegram_payload["normalization_manifest"]["dedupe_conflict_assessment"]
assert assessment["duplicates"] == telegram["extraction"]["duplicates"]
assert assessment["conflicts"] == telegram["extraction"]["conflicts"]
assert telegram_payload["edges"][0]["edge_type"] == "contradicts"
def test_telegram_duplicate_text_keeps_distinct_exact_message_provenance() -> None:
fixture, _input, _row_ids, _parent, _child = _loaded(canary.DEFAULT_TELEGRAM_FIXTURE)
first, _second, repeated = fixture["segments"]
assert first["body"] == repeated["body"]
assert first["text_sha256"] == repeated["text_sha256"]
assert first["content_sha256"] != repeated["content_sha256"]
assert first["locator"] == "telegram://chat/900001/message/7301"
assert repeated["locator"] == "telegram://chat/900001/message/7303"
duplicate = fixture["extraction"]["duplicates"][0]
assert duplicate["source_locator"] == repeated["locator"]
assert duplicate["source_json_pointer"] == "jsonl://line/3/message"
def test_fixture_validation_fails_closed_on_invented_evidence(tmp_path: Path) -> None:
scenario_path = _copy_scenario(tmp_path, canary.DEFAULT_TELEGRAM_FIXTURE)
scenario = json.loads(scenario_path.read_text(encoding="utf-8"))
scenario["extraction"]["claims"][0]["evidence"][0]["excerpt"] = "invented evidence"
scenario_path.write_text(json.dumps(scenario), encoding="utf-8")
with pytest.raises(canary.CanaryError, match="not an exact segment substring"):
canary.load_fixture(scenario_path)
def test_fixture_artifact_path_cannot_escape_scenario_directory(tmp_path: Path) -> None:
scenario = json.loads(canary.DEFAULT_DOCUMENT_FIXTURE.read_text(encoding="utf-8"))
scenario["artifact"]["path"] = "../outside.txt"
path = tmp_path / "scenario.json"
path.write_text(json.dumps(scenario), encoding="utf-8")
with pytest.raises(canary.CanaryError, match="remain inside"):
canary.load_fixture(path)
def test_capture_sql_escapes_quotes_and_backslashes(tmp_path: Path) -> None:
scenario_path = _copy_scenario(tmp_path, canary.DEFAULT_DOCUMENT_FIXTURE)
scenario = json.loads(scenario_path.read_text(encoding="utf-8"))
scenario["source"]["title"] = "O'Brien\\review"
scenario["source"]["metadata"]["note"] = "quoted ' value \\ path"
scenario_path.write_text(json.dumps(scenario), encoding="utf-8")
fixture, input_receipt, row_ids, _parent, _child = _loaded(scenario_path)
sql = canary.build_capture_sql(fixture, input_receipt, row_ids)
assert canary.ap.sql_literal(scenario["source"]["title"]) in sql
expected_metadata = {
**scenario["source"]["metadata"],
"extractor": input_receipt["extractor"],
}
assert canary.ap.sql_literal(json.dumps(expected_metadata, sort_keys=True)) in sql
def test_replay_identity_and_ids_change_with_extractor_version(tmp_path: Path) -> None:
scenario_path = _copy_scenario(tmp_path, canary.DEFAULT_DOCUMENT_FIXTURE)
fixture, original, original_ids, _parent, original_child = _loaded(scenario_path)
scenario = json.loads(scenario_path.read_text(encoding="utf-8"))
scenario["extractor"]["version"] = "3"
scenario_path.write_text(json.dumps(scenario), encoding="utf-8")
changed_fixture, changed, changed_ids, _changed_parent, changed_child = _loaded(scenario_path)
assert original["artifact_sha256"] == changed["artifact_sha256"]
assert original["replay_identity_sha256"] != changed["replay_identity_sha256"]
assert original_ids["source_capture_id"] != changed_ids["source_capture_id"]
assert original_ids["claim_extraction_ids"] != changed_ids["claim_extraction_ids"]
assert original_ids["parent_proposal_id"] != changed_ids["parent_proposal_id"]
assert original_child["payload_sha256"] != changed_child["payload_sha256"]
assert fixture["segments"] == changed_fixture["segments"]
@pytest.mark.parametrize(
("source_field", "replacement"),
(
("identity", "document:mutated-exact-source-identity"),
("locator", "fixture://working-leo/mutated-exact-source-locator"),
),
)
def test_replay_identity_and_every_row_id_bind_exact_source_identity(
tmp_path: Path, source_field: str, replacement: str
) -> None:
scenario_path = _copy_scenario(tmp_path, canary.DEFAULT_DOCUMENT_FIXTURE)
fixture, before, before_ids, _parent, before_child = _loaded(scenario_path)
scenario = json.loads(scenario_path.read_text(encoding="utf-8"))
scenario["source"][source_field] = replacement
scenario_path.write_text(json.dumps(scenario), encoding="utf-8")
changed_fixture, after, after_ids, _changed_parent, after_child = _loaded(scenario_path)
assert before["artifact_sha256"] == after["artifact_sha256"]
assert before["extraction_spec_sha256"] == after["extraction_spec_sha256"]
assert before["replay_identity_sha256"] != after["replay_identity_sha256"]
assert _row_id_values(before_ids).isdisjoint(_row_id_values(after_ids))
assert _planned_uuid_values(before_child).isdisjoint(_planned_uuid_values(after_child))
assert fixture["source"][source_field] != changed_fixture["source"][source_field]
@pytest.mark.parametrize(
("segment_field", "replacement"),
(
("id", "message-900001-mutated"),
("locator", "telegram://chat/900001/message/999999"),
("json_pointer", "jsonl://line/999/message"),
("content_sha256", "a" * 64),
("text_sha256", "b" * 64),
),
)
def test_replay_identity_and_every_row_id_bind_complete_normalized_segment(
segment_field: str, replacement: str
) -> None:
fixture, before, before_ids, _parent, before_child = _loaded(canary.DEFAULT_TELEGRAM_FIXTURE)
changed_fixture = copy.deepcopy(fixture)
after = copy.deepcopy(before)
segment = changed_fixture["segments"][-1]
original_id = segment["id"]
segment[segment_field] = replacement
for claim in changed_fixture["extraction"]["claims"]:
for evidence in claim["evidence"]:
if evidence["segment_id"] == original_id:
evidence["segment_id"] = segment["id"]
evidence["source_locator"] = segment["locator"]
evidence["source_json_pointer"] = segment["json_pointer"]
evidence["source_content_sha256"] = segment["content_sha256"]
evidence["source_text_sha256"] = segment["text_sha256"]
for duplicate in changed_fixture["extraction"]["duplicates"]:
if duplicate["segment_id"] == original_id:
duplicate["segment_id"] = segment["id"]
duplicate["source_locator"] = segment["locator"]
duplicate["source_json_pointer"] = segment["json_pointer"]
duplicate["source_content_sha256"] = segment["content_sha256"]
duplicate["source_text_sha256"] = segment["text_sha256"]
after["source_content_sha256"] = canary.canonical_sha256(
canary.normalized_segment_manifest(changed_fixture["segments"])
)
after["replay_identity_components"] = canary.replay_identity_payload(
artifact_sha256=after["artifact_sha256"],
artifact_format=after["artifact_format"],
source_identity=after["source_identity"],
source_locator=after["source_locator"],
normalized_segments_sha256=after["source_content_sha256"],
extractor=after["extractor"],
extraction_spec_sha256=after["extraction_spec_sha256"],
)
after["replay_identity_sha256"] = canary.canonical_sha256(after["replay_identity_components"])
after_ids = canary.build_row_ids(after, changed_fixture)
after_parent = canary.build_parent_packet(changed_fixture, after, after_ids)
after_child = canary.normalized_stage.prepare_normalized_child(after_parent)
assert before["artifact_sha256"] == after["artifact_sha256"]
assert before["scenario_sha256"] == after["scenario_sha256"]
assert before["extraction_spec_sha256"] == after["extraction_spec_sha256"]
assert before["replay_identity_sha256"] != after["replay_identity_sha256"]
assert _row_id_values(before_ids).isdisjoint(_row_id_values(after_ids))
assert _planned_uuid_values(before_child).isdisjoint(_planned_uuid_values(after_child))
def test_source_byte_tamper_changes_hashes_and_replay_ids(tmp_path: Path) -> None:
scenario_path = _copy_scenario(tmp_path, canary.DEFAULT_DOCUMENT_FIXTURE)
fixture, before, before_ids, _parent, _child = _loaded(scenario_path)
artifact_path = Path(before["artifact_path"])
artifact_path.write_text(
artifact_path.read_text(encoding="utf-8") + "\n\nA new source paragraph.", encoding="utf-8"
)
changed_fixture, after, after_ids, _changed_parent, _changed_child = _loaded(scenario_path)
assert before["artifact_sha256"] != after["artifact_sha256"]
assert before["source_content_sha256"] != after["source_content_sha256"]
assert before["replay_identity_sha256"] != after["replay_identity_sha256"]
assert before_ids["source_capture_id"] != after_ids["source_capture_id"]
assert before_ids["claim_extraction_ids"] != after_ids["claim_extraction_ids"]
assert len(changed_fixture["segments"]) == len(fixture["segments"]) + 1
def test_replay_properties_hold_across_many_version_labels(tmp_path: Path) -> None:
scenario_path = _copy_scenario(tmp_path, canary.DEFAULT_DOCUMENT_FIXTURE)
base = json.loads(scenario_path.read_text(encoding="utf-8"))
observed: set[str] = set()
for index in range(40):
scenario = copy.deepcopy(base)
scenario["extractor"]["version"] = f"property-{index}"
scenario_path.write_text(json.dumps(scenario), encoding="utf-8")
fixture_a, receipt_a = canary.load_fixture(scenario_path)
fixture_b, receipt_b = canary.load_fixture(scenario_path)
ids_a = canary.build_row_ids(receipt_a, fixture_a)
ids_b = canary.build_row_ids(receipt_b, fixture_b)
assert receipt_a == receipt_b
assert ids_a == ids_b
observed.add(receipt_a["replay_identity_sha256"])
assert len(observed) == 40
@pytest.mark.parametrize("scenario_path", canary.DEFAULT_FIXTURES)
def test_capture_and_stage_sql_do_not_mutate_canonical_tables(scenario_path: Path) -> None:
fixture, input_receipt, row_ids, parent, child = _loaded(scenario_path)
sql = "\n".join( sql = "\n".join(
( (
canary.build_schema_sql(),
canary.build_capture_sql(fixture, input_receipt, row_ids), canary.build_capture_sql(fixture, input_receipt, row_ids),
canary.normalized_stage.build_stage_sql(child), canary.normalized_stage.build_stage_sql(child),
) )
@ -106,59 +437,346 @@ def test_capture_and_stage_sql_never_mutate_canonical_public_tables() -> None:
assert "update public." not in sql assert "update public." not in sql
assert "delete from public." not in sql assert "delete from public." not in sql
assert "insert into kb_canary.source_captures" in sql assert "insert into kb_canary.source_captures" in sql
assert "insert into kb_canary.claim_extractions" in sql
assert "insert into kb_canary.evidence_extractions" in sql
assert "insert into kb_stage.kb_proposals" in sql assert "insert into kb_stage.kb_proposals" in sql
assert parent["payload"]["ingestion_manifest"]["row_ids"] == row_ids
def test_check_evaluation_requires_every_row_link_and_staging_boundary() -> None: def test_canonical_snapshot_covers_all_nonempty_canonical_tables() -> None:
fixture, input_receipt, row_ids = _loaded() schema_sql = canary.build_schema_sql().lower()
child = canary.normalized_stage.prepare_normalized_child( snapshot_sql = canary.build_canonical_snapshot_sql().lower()
canary.build_parent_packet(fixture, input_receipt, row_ids)
for table in ("claims", "sources", "claim_evidence", "claim_edges"):
assert f"insert into public.{table}" in schema_sql
assert f"from public.{table}" in snapshot_sql
assert "order by" in snapshot_sql
assert "begin transaction read only" in snapshot_sql
assert canary.canonical_snapshot_complete({}) is False
assert (
canary.canonical_snapshot_complete(
{
"claims": [{"id": "1"}],
"sources": [{"id": "2"}],
"claim_evidence": [{"claim_id": "1"}],
"claim_edges": [{"from_claim": "1"}],
}
) )
apply_payload = child["payload"]["apply_payload"] is True
readback = { )
"source_capture": {
"id": row_ids["source_capture_id"],
"artifact_sha256": input_receipt["artifact_sha256"], def test_evaluation_fails_when_exact_evidence_locator_is_changed() -> None:
"content_sha256": input_receipt["source_content_sha256"], fixture, input_receipt, row_ids, _parent, child = _loaded(canary.DEFAULT_TELEGRAM_FIXTURE)
"source_identity": input_receipt["source_identity"], readback = _synthetic_readback(fixture, input_receipt, row_ids, child)
}, canonical = {
"claim_extraction": { "claims": [{"id": "1"}],
"id": row_ids["claim_extraction_id"], "sources": [{"id": "2"}],
"source_capture_id": row_ids["source_capture_id"], "claim_evidence": [{"claim_id": "1"}],
"body": fixture["extraction"]["claim"]["body"], "claim_edges": [{"from_claim": "1"}],
"metadata": {"text": fixture["extraction"]["claim"]["text"]},
},
"evidence_extraction": {
"id": row_ids["evidence_extraction_id"],
"claim_extraction_id": row_ids["claim_extraction_id"],
"source_capture_id": row_ids["source_capture_id"],
"body": fixture["extraction"]["evidence"]["body"],
"metadata": fixture["extraction"]["evidence"]["metadata"],
},
"staged_proposal": {
"id": child["id"],
"status": "pending_review",
"source_ref": child["source_ref"],
"payload": {"apply_payload": apply_payload},
"reviewed_by_handle": None,
"reviewed_at": None,
"applied_by_handle": None,
"applied_at": None,
},
"counts": {
"source_captures": 1,
"claim_extractions": 1,
"evidence_extractions": 1,
"staged_proposals": 1,
},
} }
checks = canary.evaluate_checks(fixture, input_receipt, row_ids, readback) checks = canary.evaluate_checks(fixture, input_receipt, row_ids, readback, canonical, copy.deepcopy(canonical))
assert all(checks.values()) assert all(checks.values())
broken = copy.deepcopy(readback) broken = copy.deepcopy(readback)
broken["evidence_extraction"]["claim_extraction_id"] = canary.stable_uuid("0" * 64, "wrong") broken["evidence_extractions"][0]["source_locator"] = "telegram://chat/900001/message/9999"
assert ( assert (
canary.evaluate_checks(fixture, input_receipt, row_ids, broken)["evidence_links_to_claim_and_source"] is False canary.evaluate_checks(fixture, input_receipt, row_ids, broken, canonical, copy.deepcopy(canonical))[
"all_evidence_has_exact_source_location"
]
is False
) )
def test_evaluation_recomputes_instead_of_trusting_supplied_row_ids() -> None:
fixture, input_receipt, row_ids, _parent, _child = _loaded(canary.DEFAULT_TELEGRAM_FIXTURE)
tampered_ids = copy.deepcopy(row_ids)
tampered_ids["source_capture_id"] = "00000000-0000-4000-8000-000000009990"
tampered_parent = canary.build_parent_packet(fixture, input_receipt, tampered_ids)
tampered_child = canary.normalized_stage.prepare_normalized_child(tampered_parent)
readback = _synthetic_readback(fixture, input_receipt, tampered_ids, tampered_child)
checks = canary.evaluate_checks(
fixture, input_receipt, tampered_ids, readback, _canonical_snapshot(), _canonical_snapshot()
)
assert checks["deterministic_row_ids_recomputed_exact"] is False
assert checks["source_capture_identity_exact"] is False
assert checks["planned_proposal_identities_and_linkages_exact"] is False
def test_independent_graph_oracle_rejects_consistent_generator_edge_corruption(
monkeypatch: pytest.MonkeyPatch,
) -> None:
fixture, input_receipt = canary.load_fixture(canary.DEFAULT_TELEGRAM_FIXTURE)
row_ids = canary.build_row_ids(input_receipt, fixture)
parent = canary.build_parent_packet(fixture, input_receipt, row_ids)
original_prepare = canary.normalized_stage.prepare_normalized_child
def corrupted_prepare(parent_packet: dict) -> dict:
child = copy.deepcopy(original_prepare(parent_packet))
child["payload"]["apply_payload"]["edges"][0]["to_claim"] = "00000000-0000-4000-8000-000000009996"
return child
monkeypatch.setattr(canary.normalized_stage, "prepare_normalized_child", corrupted_prepare)
corrupted_child = corrupted_prepare(parent)
readback = _synthetic_readback(fixture, input_receipt, row_ids, corrupted_child)
checks = canary.evaluate_checks(
fixture, input_receipt, row_ids, readback, _canonical_snapshot(), _canonical_snapshot()
)
assert checks["planned_graph_matches_independent_source_oracle"] is False
assert checks["planned_proposal_identities_and_linkages_exact"] is False
assert checks["conflicts_and_relationships_persisted"] is False
@pytest.mark.parametrize(
("mutation", "failed_check"),
(
("source_id", "source_capture_identity_exact"),
("source_identity", "source_capture_identity_exact"),
("source_locator", "source_capture_identity_exact"),
("claim_id", "claim_extraction_identities_and_fks_exact"),
("claim_source_fk", "claim_extraction_identities_and_fks_exact"),
("evidence_id", "evidence_extraction_identities_and_fks_exact"),
("evidence_claim_fk", "evidence_extraction_identities_and_fks_exact"),
("evidence_source_fk", "evidence_extraction_identities_and_fks_exact"),
("evidence_segment_id", "evidence_extraction_identities_and_fks_exact"),
("evidence_json_pointer", "evidence_extraction_identities_and_fks_exact"),
("evidence_role", "evidence_extraction_identities_and_fks_exact"),
("evidence_body_sha256", "evidence_extraction_identities_and_fks_exact"),
("proposal_id", "planned_proposal_identities_and_linkages_exact"),
("planned_claim_id", "planned_proposal_identities_and_linkages_exact"),
("planned_source_id", "planned_proposal_identities_and_linkages_exact"),
("planned_evidence_claim_fk", "planned_proposal_identities_and_linkages_exact"),
("planned_evidence_source_fk", "planned_proposal_identities_and_linkages_exact"),
("planned_evidence_role", "planned_proposal_identities_and_linkages_exact"),
("manifest_row_id", "planned_proposal_identities_and_linkages_exact"),
),
)
def test_evaluation_rejects_wrong_deterministic_ids_and_every_fk_linkage(mutation: str, failed_check: str) -> None:
fixture, input_receipt, row_ids, _parent, child = _loaded(canary.DEFAULT_TELEGRAM_FIXTURE)
readback = _synthetic_readback(fixture, input_receipt, row_ids, child)
broken = copy.deepcopy(readback)
wrong_uuid = "00000000-0000-4000-8000-000000009999"
apply_payload = broken["staged_proposal"]["payload"]["apply_payload"]
if mutation == "source_id":
broken["source_captures"][0]["id"] = wrong_uuid
elif mutation == "source_identity":
broken["source_captures"][0]["source_identity"] = "fixture:wrong-source-identity"
elif mutation == "source_locator":
broken["source_captures"][0]["locator"] = "fixture://working-leo/wrong-source-locator"
elif mutation == "claim_id":
broken["claim_extractions"][0]["id"] = broken["claim_extractions"][1]["id"]
elif mutation == "claim_source_fk":
broken["claim_extractions"][0]["source_capture_id"] = wrong_uuid
elif mutation == "evidence_id":
broken["evidence_extractions"][0]["id"] = broken["evidence_extractions"][1]["id"]
elif mutation == "evidence_claim_fk":
broken["evidence_extractions"][0]["claim_extraction_id"] = broken["claim_extractions"][1]["id"]
elif mutation == "evidence_source_fk":
broken["evidence_extractions"][0]["source_capture_id"] = wrong_uuid
elif mutation == "evidence_segment_id":
broken["evidence_extractions"][0]["source_segment_id"] = "message-900001-9999"
elif mutation == "evidence_json_pointer":
broken["evidence_extractions"][0]["source_json_pointer"] = "jsonl://line/999/message"
elif mutation == "evidence_role":
broken["evidence_extractions"][0]["role"] = "supports"
elif mutation == "evidence_body_sha256":
broken["evidence_extractions"][0]["body_sha256"] = "d" * 64
elif mutation == "proposal_id":
broken["staged_proposal"]["id"] = wrong_uuid
elif mutation == "planned_claim_id":
apply_payload["claims"][0]["id"] = apply_payload["claims"][1]["id"]
elif mutation == "planned_source_id":
apply_payload["sources"][0]["id"] = apply_payload["sources"][1]["id"]
elif mutation == "planned_evidence_claim_fk":
apply_payload["evidence"][0]["claim_id"] = apply_payload["claims"][1]["id"]
elif mutation == "planned_evidence_source_fk":
apply_payload["evidence"][0]["source_id"] = apply_payload["sources"][1]["id"]
elif mutation == "planned_evidence_role":
apply_payload["evidence"][0]["role"] = "supports"
elif mutation == "manifest_row_id":
apply_payload["normalization_manifest"]["ingestion_manifest"]["row_ids"]["source_capture_id"] = wrong_uuid
checks = canary.evaluate_checks(
fixture, input_receipt, row_ids, broken, _canonical_snapshot(), _canonical_snapshot()
)
assert checks[failed_check] is False
assert not all(checks.values())
@pytest.mark.parametrize(
("surface", "field", "replacement", "failed_check"),
(
("duplicate", "id", "00000000-0000-4000-8000-000000009991", "duplicate_judgments_persisted"),
(
"duplicate",
"source_capture_id",
"00000000-0000-4000-8000-000000009992",
"duplicate_judgments_persisted",
),
("duplicate", "source_segment_id", "message-900001-9999", "duplicate_judgments_persisted"),
(
"duplicate",
"source_locator",
"telegram://chat/900001/message/9999",
"duplicate_judgments_persisted",
),
("duplicate", "duplicate_of_claim_key", "approval_alone_makes_canonical", "duplicate_judgments_persisted"),
("duplicate", "excerpt", "fabricated duplicate excerpt", "duplicate_judgments_persisted"),
("duplicate", "excerpt_sha256", "c" * 64, "duplicate_judgments_persisted"),
("duplicate", "reason", "fabricated duplicate reason", "duplicate_judgments_persisted"),
("duplicate", "metadata", {"json_pointer": "fabricated"}, "duplicate_judgments_persisted"),
("conflict", "id", "00000000-0000-4000-8000-000000009993", "conflicts_and_relationships_persisted"),
(
"conflict",
"source_capture_id",
"00000000-0000-4000-8000-000000009994",
"conflicts_and_relationships_persisted",
),
("conflict", "from_claim_key", "approval_alone_makes_canonical", "conflicts_and_relationships_persisted"),
(
"conflict",
"to_claim_key",
"pending_review_does_not_change_canonical",
"conflicts_and_relationships_persisted",
),
("conflict", "relationship", "supports", "conflicts_and_relationships_persisted"),
("conflict", "metadata", {"reason": "fabricated"}, "conflicts_and_relationships_persisted"),
("edge", "from_claim", "00000000-0000-4000-8000-000000009995", "conflicts_and_relationships_persisted"),
("edge", "to_claim", "00000000-0000-4000-8000-000000009996", "conflicts_and_relationships_persisted"),
("edge", "edge_type", "supports", "conflicts_and_relationships_persisted"),
("edge", "weight", 0.5, "conflicts_and_relationships_persisted"),
("edge", "created_by", "00000000-0000-4000-8000-000000009997", "conflicts_and_relationships_persisted"),
),
)
def test_evaluation_rejects_mutated_duplicate_conflict_and_edge_identities(
surface: str, field: str, replacement: object, failed_check: str
) -> None:
fixture, input_receipt, row_ids, _parent, child = _loaded(canary.DEFAULT_TELEGRAM_FIXTURE)
broken = _synthetic_readback(fixture, input_receipt, row_ids, child)
if surface == "duplicate":
broken["duplicate_assessments"][0][field] = replacement
elif surface == "conflict":
broken["conflict_assessments"][0][field] = replacement
else:
broken["staged_proposal"]["payload"]["apply_payload"]["edges"][0][field] = replacement
checks = canary.evaluate_checks(
fixture, input_receipt, row_ids, broken, _canonical_snapshot(), _canonical_snapshot()
)
assert checks[failed_check] is False
assert not all(checks.values())
@pytest.mark.parametrize(
("field", "replacement"),
(
("reviewed_by_handle", "reviewer"),
("reviewed_by_agent_id", "00000000-0000-4000-8000-000000009981"),
("reviewed_at", "2026-07-15T00:00:00+00:00"),
("review_note", "fabricated review note"),
("applied_by_handle", "applier"),
("applied_by_agent_id", "00000000-0000-4000-8000-000000009982"),
("applied_at", "2026-07-15T00:00:01+00:00"),
),
)
def test_evaluation_rejects_every_review_and_apply_metadata_mutation(field: str, replacement: str) -> None:
fixture, input_receipt, row_ids, _parent, child = _loaded(canary.DEFAULT_TELEGRAM_FIXTURE)
broken = _synthetic_readback(fixture, input_receipt, row_ids, child)
broken["staged_proposal"][field] = replacement
checks = canary.evaluate_checks(
fixture, input_receipt, row_ids, broken, _canonical_snapshot(), _canonical_snapshot()
)
assert checks["no_review_or_apply_metadata"] is False
assert checks["planned_proposal_identities_and_linkages_exact"] is False
def _suite_child(artifact_format: str, *, passed: bool = True, canonical_unchanged: bool = True) -> dict:
before = "a" * 64
after = before if canonical_unchanged else "b" * 64
return {
"status": "pass" if passed else "fail",
"input": {"artifact_format": artifact_format},
"canonical": {
"fingerprint_before": before,
"fingerprint_after": after,
"unchanged": canonical_unchanged,
},
"checks": {"canonical_fingerprint_unchanged": canonical_unchanged},
}
def test_single_fixture_suite_is_truthfully_partial(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
monkeypatch.setattr(canary, "run_canary", lambda _path: _suite_child("plain_text"))
suite = canary.run_suite([tmp_path / "document.scenario.json"])
assert suite["status"] == "partial"
assert suite["full_suite_passed"] is False
assert suite["coverage_status"] == "partial"
assert suite["missing_required_coverage"] == ["social_conversation"]
def test_malformed_fixture_fails_closed_with_receipt_and_without_runtime(tmp_path: Path) -> None:
malformed = tmp_path / "malformed.scenario.json"
malformed.write_text("{not-json", encoding="utf-8")
receipt = canary.run_canary(malformed)
suite = canary.run_suite([malformed])
assert receipt["status"] == "fail"
assert receipt["error"]["type"] == "CanaryError"
assert receipt["cleanup"]["runtime_not_started"] is True
assert receipt["cleanup"]["container_absent"] is True
assert suite["status"] == "fail"
assert suite["receipts"][0]["error"]["type"] == "CanaryError"
@pytest.mark.parametrize(
("children", "expected_status"),
(
(
[_suite_child("plain_text"), _suite_child("telegram_jsonl")],
"pass",
),
(
[_suite_child("plain_text", canonical_unchanged=False), _suite_child("telegram_jsonl")],
"fail",
),
(
[_suite_child("plain_text"), _suite_child("telegram_jsonl", passed=False)],
"fail",
),
),
)
def test_full_suite_status_requires_both_shapes_all_children_and_canonical_invariance(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path, children: list[dict], expected_status: str
) -> None:
queue = iter(children)
monkeypatch.setattr(canary, "run_canary", lambda _path: next(queue))
suite = canary.run_suite([tmp_path / "document.scenario.json", tmp_path / "telegram.scenario.json"])
assert suite["status"] == expected_status
assert suite["full_suite_passed"] is (expected_status == "pass")
def test_suite_recomputes_canonical_invariance_instead_of_trusting_stale_boolean(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
children = [_suite_child("plain_text"), _suite_child("telegram_jsonl")]
children[0]["canonical"]["fingerprint_after"] = "c" * 64
queue = iter(children)
monkeypatch.setattr(canary, "run_canary", lambda _path: next(queue))
suite = canary.run_suite([tmp_path / "document.scenario.json", tmp_path / "telegram.scenario.json"])
assert suite["status"] == "fail"
assert suite["canonical_fingerprint_invariant_all"] is False
assert suite["full_suite_passed"] is False
assert "canonical_fingerprint_invariance" in suite["missing_required_coverage"]

View file

@ -23,6 +23,18 @@ REVIEWER_ID = "11111111-1111-4111-8111-111111111111"
SERVICE_ID = "44444444-4444-4444-4444-444444444444" SERVICE_ID = "44444444-4444-4444-4444-444444444444"
PROPOSAL_ID = "99999999-9999-4999-8999-999999999999" PROPOSAL_ID = "99999999-9999-4999-8999-999999999999"
EDGE_ID = "eeeeeeee-eeee-4eee-8eee-eeeeeeeeeeee" EDGE_ID = "eeeeeeee-eeee-4eee-8eee-eeeeeeeeeeee"
OTHER_AGENT_ID = "22222222-2222-4222-8222-222222222222"
REVISE_PROPOSAL_ID = "88888888-8888-4888-8888-888888888888"
PRIOR_STRATEGY_ID = "33333333-3333-4333-8333-333333333333"
PRIOR_ACTIVE_NODE_ID = "55555555-5555-4555-8555-555555555555"
PRIOR_RETIRED_NODE_ID = "66666666-6666-4666-8666-666666666666"
OTHER_STRATEGY_ID = "77777777-7777-4777-8777-777777777777"
OTHER_ACTIVE_NODE_ID = "12121212-1212-4212-8212-121212121212"
NULL_AGENT_NODE_ID = "13131313-1313-4313-8313-131313131313"
PRIOR_NODE_ANCHOR_ID = "19191919-1919-4919-8919-191919191919"
RECEIPT_STRATEGY_ID = "14141414-1414-4414-8414-141414141414"
RECEIPT_NODE_A_ID = "15151515-1515-4515-8515-151515151515"
RECEIPT_NODE_B_ID = "16161616-1616-4616-8616-161616161616"
PRIVATE_MARKER = "PRIVATE-REPLAY-MATERIAL-MUST-NOT-LEAK" PRIVATE_MARKER = "PRIVATE-REPLAY-MATERIAL-MUST-NOT-LEAK"
@ -155,6 +167,58 @@ def proposal_rows() -> tuple[dict, dict, dict]:
return approved, applied, approval return approved, applied, approval
def revise_strategy_proposal_rows() -> tuple[dict, dict, dict]:
approved, _, approval = proposal_rows()
payload = {
"apply_payload": {
"agent_id": REVIEWER_ID,
"strategy": {
"diagnosis": "Deterministic reconstruction needs exact mutation deltas.",
"guiding_policy": "Replay every guarded transition and fail closed on drift.",
"proximate_objectives": ["exact parity", "zero orphan containers"],
},
"strategy_nodes": [
{
"node_type": "policy",
"title": "Replay exactly",
"body": "Bind the guarded transition to its canonical receipt.",
"rank": 1,
},
{
"node_type": "policy",
"title": "Replay exactly",
"body": "Bind the guarded transition to its canonical receipt.",
"rank": 1,
},
],
},
"private_title": PRIVATE_MARKER,
}
approved = {
**approved,
"id": REVISE_PROPOSAL_ID,
"proposal_type": "revise_strategy",
"payload": payload,
"created_at": "2026-07-14T00:58:00+00:00",
"updated_at": "2026-07-14T01:00:00+00:00",
}
applied = {
**approved,
"status": "applied",
"applied_by_handle": "kb-apply",
"applied_by_agent_id": SERVICE_ID,
"applied_at": "2026-07-14T01:01:00+00:00",
"updated_at": "2026-07-14T01:01:00.000001+00:00",
}
approval = {
**approval,
"proposal_id": REVISE_PROPOSAL_ID,
"proposal_type": "revise_strategy",
"payload": payload,
}
return approved, applied, approval
def applied_envelope(applied: dict) -> dict: def applied_envelope(applied: dict) -> dict:
fields = ( fields = (
"id", "id",
@ -202,6 +266,73 @@ def replay_material() -> dict:
} }
def revise_strategy_replay_material(*, apply_sql_source: str = "exact_executed_sql", version: int = 2) -> dict:
approved, applied, approval = revise_strategy_proposal_rows()
proposal = applied_envelope(applied)
transaction_timestamp = "2026-07-14T01:00:30+00:00"
canonical_rows = {
"strategies": [
{
"id": RECEIPT_STRATEGY_ID,
"agent_id": REVIEWER_ID,
**proposal["payload"]["apply_payload"]["strategy"],
"version": version,
"active": True,
"created_at": transaction_timestamp,
}
],
"strategy_nodes": [
{
"id": node_id,
"agent_id": REVIEWER_ID,
**node,
"status": "active",
"horizon": None,
"measure": None,
"source_ref": None,
"metadata": {},
"created_at": transaction_timestamp,
"updated_at": transaction_timestamp,
}
for node_id, node in zip(
(RECEIPT_NODE_A_ID, RECEIPT_NODE_B_ID),
proposal["payload"]["apply_payload"]["strategy_nodes"],
strict=True,
)
],
}
apply_sql = rebuild.apply_engine.build_apply_sql(proposal, applied["applied_by_handle"])
receipt = rebuild.replay_receipt.build_replay_receipt(
proposal,
canonical_rows,
apply_sql_sha256=rebuild.replay_receipt.sha256_text(apply_sql),
apply_sql_source=apply_sql_source,
exported_at_utc="2026-07-14T01:02:00+00:00",
)
return {
"artifact": rebuild.MATERIAL_ARTIFACT,
"contract_version": rebuild.MATERIAL_CONTRACT_VERSION,
"sequence": 1,
"approved_proposal": approved,
"approval_snapshot": approval,
"applied_proposal": applied,
"replay_receipt": receipt,
}
def rehash_revise_strategy_receipt(material: dict) -> None:
prior_receipt = material["replay_receipt"]
proposal = applied_envelope(material["applied_proposal"])
apply_sql = rebuild.apply_engine.build_apply_sql(proposal, material["applied_proposal"]["applied_by_handle"])
material["replay_receipt"] = rebuild.replay_receipt.build_replay_receipt(
proposal,
prior_receipt["canonical_rows"],
apply_sql_sha256=rebuild.replay_receipt.sha256_text(apply_sql),
apply_sql_source=prior_receipt["apply_engine"]["source"],
exported_at_utc=prior_receipt["exported_at_utc"],
)
def write_bundle(tmp_path: Path, *, material: dict | None = None) -> argparse.Namespace: def write_bundle(tmp_path: Path, *, material: dict | None = None) -> argparse.Namespace:
dump = tmp_path / "genesis.dump" dump = tmp_path / "genesis.dump"
dump.write_bytes(b"PGDMPfixture") dump.write_bytes(b"PGDMPfixture")
@ -238,7 +369,7 @@ def write_bundle(tmp_path: Path, *, material: dict | None = None) -> argparse.Na
ledger=ledger_path, ledger=ledger_path,
ledger_sha256=sha256_file(ledger_path), ledger_sha256=sha256_file(ledger_path),
database="teleo", database="teleo",
image=rebuild.canonical_rebuild.DEFAULT_IMAGE, image=rebuild.DEFAULT_REBUILD_IMAGE,
container_prefix="teleo-genesis-ledger-test", container_prefix="teleo-genesis-ledger-test",
tmpfs_mb=256, tmpfs_mb=256,
startup_timeout=30.0, startup_timeout=30.0,
@ -302,18 +433,192 @@ def test_dump_hash_mismatch_fails_before_container_start(tmp_path: Path, monkeyp
assert not any(len(command) > 1 and command[1] == "run" for command in commands) assert not any(len(command) > 1 and command[1] == "run" for command in commands)
def test_revise_strategy_receipt_fails_closed_before_receipt_rows_are_used(tmp_path: Path) -> None: def test_revise_strategy_material_validates_exact_engine_and_canonicalizes_row_order(
material = replay_material() tmp_path: Path,
material["replay_receipt"]["proposal"]["proposal_type"] = "revise_strategy" ) -> None:
material["applied_proposal"]["proposal_type"] = "revise_strategy" material = revise_strategy_replay_material()
material["approved_proposal"]["proposal_type"] = "revise_strategy" material["replay_receipt"]["canonical_rows"]["strategy_nodes"].reverse()
material["approval_snapshot"]["proposal_type"] = "revise_strategy"
args = write_bundle(tmp_path, material=material) args = write_bundle(tmp_path, material=material)
with pytest.raises(rebuild.ReconstructionError, match="prior strategy") as exc_info: entry = rebuild.load_bundle(args).entries[0]
assert entry.applied_proposal["proposal_type"] == "revise_strategy"
assert entry.receipt["apply_engine"]["source"] == "exact_executed_sql"
assert entry.receipt["apply_engine"]["apply_sql_sha256"] == entry.current_apply_sql_sha256
assert entry.receipt["canonical_rows"]["strategy_nodes"] == sorted(
entry.receipt["canonical_rows"]["strategy_nodes"], key=rebuild._canonical_json
)
def test_revise_strategy_material_rejects_reconstructed_apply_engine(tmp_path: Path) -> None:
args = write_bundle(
tmp_path,
material=revise_strategy_replay_material(apply_sql_source="reconstructed_current_engine"),
)
with pytest.raises(rebuild.ReconstructionError) as exc_info:
rebuild.load_bundle(args) rebuild.load_bundle(args)
assert exc_info.value.code == "unsupported_mutating_receipt" assert exc_info.value.code == "mutating_apply_engine_mismatch"
def test_revise_strategy_rejects_fully_rehashed_transaction_before_approval(
tmp_path: Path,
) -> None:
material = revise_strategy_replay_material()
original_receipt = material["replay_receipt"]
forged_timestamp = "2026-07-13T01:00:30+00:00"
canonical_rows = copy.deepcopy(original_receipt["canonical_rows"])
canonical_rows["strategies"][0]["created_at"] = forged_timestamp
for row in canonical_rows["strategy_nodes"]:
row["created_at"] = forged_timestamp
row["updated_at"] = forged_timestamp
material["replay_receipt"]["canonical_rows"] = canonical_rows
rehash_revise_strategy_receipt(material)
args = write_bundle(tmp_path, material=material)
ledger = json.loads(args.ledger.read_text(encoding="utf-8"))
material_path = args.ledger.parent / ledger["entries"][0]["material"]
assert material["replay_receipt"]["hashes"] != original_receipt["hashes"]
assert ledger["entries"][0]["sha256"] == sha256_file(material_path)
assert (
ledger["entries"][0]["replay_material_sha256"] == material["replay_receipt"]["hashes"]["replay_material_sha256"]
)
assert args.ledger_sha256 == sha256_file(args.ledger)
with pytest.raises(rebuild.ReconstructionError) as exc_info:
rebuild.load_bundle(args)
assert exc_info.value.code == "invalid_mutating_receipt"
assert "before proposal approval" in exc_info.value.safe_message
@pytest.mark.parametrize(
("reviewed_at", "transaction_timestamp", "applied_at"),
(
(
"2026-07-14T03:00:00+02:00",
"2026-07-14T01:00:00+00:00",
"2026-07-14T01:01:00+00:00",
),
(
"2026-07-14T01:00:00+00:00",
"2026-07-14T01:01:00+00:00",
"2026-07-14T03:01:00+02:00",
),
),
)
def test_revise_strategy_accepts_timezone_aware_closed_timestamp_boundaries(
tmp_path: Path,
reviewed_at: str,
transaction_timestamp: str,
applied_at: str,
) -> None:
material = revise_strategy_replay_material()
for proposal_row in (material["approved_proposal"], material["applied_proposal"]):
proposal_row["reviewed_at"] = reviewed_at
material["approval_snapshot"]["reviewed_at"] = reviewed_at
material["applied_proposal"]["applied_at"] = applied_at
rows = material["replay_receipt"]["canonical_rows"]
rows["strategies"][0]["created_at"] = transaction_timestamp
for row in rows["strategy_nodes"]:
row["created_at"] = transaction_timestamp
row["updated_at"] = transaction_timestamp
rehash_revise_strategy_receipt(material)
entry = rebuild.load_bundle(write_bundle(tmp_path, material=material)).entries[0]
assert entry.receipt["canonical_rows"]["strategies"][0]["created_at"] == transaction_timestamp
def test_revise_strategy_rejects_reviewed_at_without_timezone(tmp_path: Path) -> None:
material = revise_strategy_replay_material()
reviewed_at = "2026-07-14T01:00:00"
for proposal_row in (material["approved_proposal"], material["applied_proposal"]):
proposal_row["reviewed_at"] = reviewed_at
material["approval_snapshot"]["reviewed_at"] = reviewed_at
rehash_revise_strategy_receipt(material)
with pytest.raises(rebuild.ReconstructionError) as exc_info:
rebuild.load_bundle(write_bundle(tmp_path, material=material))
assert exc_info.value.code == "invalid_mutating_receipt"
assert "reviewed_at must include a timezone" in exc_info.value.safe_message
@pytest.mark.parametrize(
"invalid_case",
("extra_strategy_field", "duplicate_node_id", "node_timestamp_drift", "transaction_after_apply"),
)
def test_revise_strategy_receipt_rejects_impossible_poststate(tmp_path: Path, invalid_case: str) -> None:
material = revise_strategy_replay_material()
rows = material["replay_receipt"]["canonical_rows"]
if invalid_case == "extra_strategy_field":
rows["strategies"][0]["unexpected"] = "ignored by jsonb_populate_record"
elif invalid_case == "duplicate_node_id":
rows["strategy_nodes"][1]["id"] = rows["strategy_nodes"][0]["id"]
elif invalid_case == "node_timestamp_drift":
rows["strategy_nodes"][0]["updated_at"] = "2026-07-14T01:00:31+00:00"
else:
rows["strategies"][0]["created_at"] = "2026-07-14T01:01:01+00:00"
for row in rows["strategy_nodes"]:
row["created_at"] = "2026-07-14T01:01:01+00:00"
row["updated_at"] = "2026-07-14T01:01:01+00:00"
args = write_bundle(tmp_path, material=material)
with pytest.raises(rebuild.ReconstructionError) as exc_info:
rebuild.load_bundle(args)
assert exc_info.value.code == "invalid_mutating_receipt"
def test_revise_strategy_transition_builders_preserve_historical_prestate(tmp_path: Path) -> None:
entry = rebuild.load_bundle(write_bundle(tmp_path, material=revise_strategy_replay_material())).entries[0]
prestate = {
"strategy_ids": [PRIOR_STRATEGY_ID],
"active_strategy_ids": [PRIOR_STRATEGY_ID],
"max_strategy_version": 1,
"strategy_node_ids": [PRIOR_ACTIVE_NODE_ID, PRIOR_RETIRED_NODE_ID],
"non_retired_strategy_node_ids": [PRIOR_ACTIVE_NODE_ID],
}
generated_rows = copy.deepcopy(entry.receipt["canonical_rows"])
generated_rows["strategies"][0]["id"] = "17171717-1717-4717-8717-171717171717"
generated_rows["strategies"][0]["created_at"] = "2026-07-15T01:00:30+00:00"
for index, row in enumerate(generated_rows["strategy_nodes"], start=1):
row["id"] = f"18181818-1818-4818-8818-18181818181{index}"
row["created_at"] = "2026-07-15T01:00:30+00:00"
row["updated_at"] = "2026-07-15T01:00:30+00:00"
sql = rebuild.build_revise_strategy_normalization_sql(entry, prestate, generated_rows)
assert PRIOR_ACTIVE_NODE_ID in sql
assert PRIOR_RETIRED_NODE_ID not in sql
assert PRIOR_STRATEGY_ID in sql
assert RECEIPT_STRATEGY_ID in sql
assert "updated_at = E'2026-07-14T01:00:30+00:00'::timestamptz" in sql
def test_revise_strategy_transition_builders_allow_empty_prestate(tmp_path: Path) -> None:
entry = rebuild.load_bundle(write_bundle(tmp_path, material=revise_strategy_replay_material(version=1))).entries[0]
prestate = {
"strategy_ids": [],
"active_strategy_ids": [],
"max_strategy_version": 0,
"strategy_node_ids": [],
"non_retired_strategy_node_ids": [],
}
generated_rows = copy.deepcopy(entry.receipt["canonical_rows"])
generated_rows["strategies"][0]["id"] = "17171717-1717-4717-8717-171717171717"
generated_rows["strategies"][0]["created_at"] = "2026-07-15T01:00:30+00:00"
for index, row in enumerate(generated_rows["strategy_nodes"], start=1):
row["id"] = f"18181818-1818-4818-8818-18181818181{index}"
row["created_at"] = "2026-07-15T01:00:30+00:00"
row["updated_at"] = "2026-07-15T01:00:30+00:00"
sql = rebuild.build_revise_strategy_normalization_sql(entry, prestate, generated_rows)
assert "prior active strategy transition mismatch" not in sql
assert "prior node normalization mismatch" not in sql
assert RECEIPT_STRATEGY_ID in sql
def test_legacy_freeform_receipt_fails_closed(tmp_path: Path) -> None: def test_legacy_freeform_receipt_fails_closed(tmp_path: Path) -> None:
@ -331,6 +636,17 @@ def test_legacy_freeform_receipt_fails_closed(tmp_path: Path) -> None:
assert PRIVATE_MARKER not in exc_info.value.safe_message assert PRIVATE_MARKER not in exc_info.value.safe_message
def test_unknown_proposal_operation_fails_closed_before_replay(tmp_path: Path) -> None:
material = replay_material()
material["replay_receipt"]["proposal"]["proposal_type"] = "delete_claim"
args = write_bundle(tmp_path, material=material)
with pytest.raises(rebuild.ReconstructionError) as exc_info:
rebuild.load_bundle(args)
assert exc_info.value.code == "unsupported_proposal_type"
def test_proposal_metadata_outside_apply_transition_is_rejected(tmp_path: Path) -> None: def test_proposal_metadata_outside_apply_transition_is_rejected(tmp_path: Path) -> None:
material = replay_material() material = replay_material()
material["applied_proposal"]["rationale"] = "silently changed after review" material["applied_proposal"]["rationale"] = "silently changed after review"
@ -354,6 +670,28 @@ def test_output_cannot_overwrite_a_fixed_guard_or_engine_file(tmp_path: Path) ->
assert exc_info.value.code == "unsafe_output_path" assert exc_info.value.code == "unsafe_output_path"
def test_cli_defaults_to_pinned_image_and_rejects_moving_tag(tmp_path: Path) -> None:
bundle = write_bundle(tmp_path)
argv = [
"--genesis-dump",
str(bundle.genesis_dump),
"--genesis-manifest",
str(bundle.genesis_manifest),
"--ledger",
str(bundle.ledger),
"--ledger-sha256",
bundle.ledger_sha256,
"--output",
str(tmp_path / "receipt.json"),
]
assert rebuild.parse_args(argv).image == rebuild.DEFAULT_REBUILD_IMAGE
with pytest.raises(SystemExit) as exc_info:
rebuild.parse_args([*argv, "--image", "postgres:16-alpine"])
assert exc_info.value.code == 2
def test_unknown_private_field_name_is_not_echoed_in_public_error(tmp_path: Path) -> None: def test_unknown_private_field_name_is_not_echoed_in_public_error(tmp_path: Path) -> None:
material = replay_material() material = replay_material()
material[PRIVATE_MARKER] = "private value" material[PRIVATE_MARKER] = "private value"
@ -484,17 +822,18 @@ create table public.reasoning_tools (
created_at timestamptz not null default now() created_at timestamptz not null default now()
); );
create table public.strategies ( create table public.strategies (
id uuid primary key, id uuid primary key default gen_random_uuid(),
agent_id uuid not null references public.agents(id), agent_id uuid not null references public.agents(id),
diagnosis text, diagnosis text,
guiding_policy text, guiding_policy text,
proximate_objectives jsonb, proximate_objectives jsonb,
version integer not null default 1, version integer not null default 1,
active boolean not null default true, active boolean not null default true,
created_at timestamptz not null default now() created_at timestamptz not null default now(),
unique (agent_id, version)
); );
create table public.strategy_nodes ( create table public.strategy_nodes (
id uuid primary key, id uuid primary key default gen_random_uuid(),
agent_id uuid references public.agents(id), agent_id uuid references public.agents(id),
node_type text, node_type text,
title text, title text,
@ -508,6 +847,20 @@ create table public.strategy_nodes (
created_at timestamptz not null default now(), created_at timestamptz not null default now(),
updated_at timestamptz not null default now() updated_at timestamptz not null default now()
); );
create table public.strategy_node_anchors (
id uuid primary key default gen_random_uuid(),
from_node_id uuid not null references public.strategy_nodes(id) on delete cascade,
anchor_role text not null,
to_node_id uuid references public.strategy_nodes(id) on delete cascade,
to_shared_root_id uuid,
belief_id uuid,
claim_id uuid,
source_id uuid,
weight numeric,
note text,
created_at timestamptz not null default now(),
check (num_nonnulls(to_node_id, to_shared_root_id, belief_id, claim_id, source_id) = 1)
);
create table kb_stage.kb_proposals ( create table kb_stage.kb_proposals (
id uuid primary key, id uuid primary key,
proposal_type text not null, proposal_type text not null,
@ -530,10 +883,34 @@ create table kb_stage.kb_proposals (
); );
insert into public.agents (id, handle, kind) values insert into public.agents (id, handle, kind) values
('{REVIEWER_ID}', 'm3ta', 'human'); ('{REVIEWER_ID}', 'm3ta', 'human'),
('{OTHER_AGENT_ID}', 'other-agent', 'system');
insert into public.claims (id, type, text, status, confidence, tags, created_by) values insert into public.claims (id, type, text, status, confidence, tags, created_by) values
('{CLAIM_A}', 'structural', 'Fixture claim A', 'open', 0.8, array['fixture'], '{REVIEWER_ID}'), ('{CLAIM_A}', 'structural', 'Fixture claim A', 'open', 0.8, array['fixture'], '{REVIEWER_ID}'),
('{CLAIM_B}', 'structural', 'Fixture claim B', 'open', 0.8, array['fixture'], '{REVIEWER_ID}'); ('{CLAIM_B}', 'structural', 'Fixture claim B', 'open', 0.8, array['fixture'], '{REVIEWER_ID}');
insert into public.strategies
(id, agent_id, diagnosis, guiding_policy, proximate_objectives, version, active, created_at)
values
('{PRIOR_STRATEGY_ID}', '{REVIEWER_ID}', 'Prior diagnosis', 'Prior policy', '["prior"]', 1, true,
'2026-07-01T00:00:00+00:00'),
('{OTHER_STRATEGY_ID}', '{OTHER_AGENT_ID}', 'Other diagnosis', 'Other policy', '["other"]', 1, true,
'2026-07-01T00:00:00+00:00');
insert into public.strategy_nodes
(id, agent_id, node_type, title, body, rank, status, created_at, updated_at)
values
('{PRIOR_ACTIVE_NODE_ID}', '{REVIEWER_ID}', 'policy', 'Prior active', 'Retire exactly once', 1,
'active', '2026-07-01T00:00:00+00:00', '2026-07-01T00:00:00+00:00'),
('{PRIOR_RETIRED_NODE_ID}', '{REVIEWER_ID}', 'policy', 'Prior retired', 'Leave timestamp unchanged', 2,
'retired', '2026-06-01T00:00:00+00:00', '2026-06-02T00:00:00+00:00'),
('{OTHER_ACTIVE_NODE_ID}', '{OTHER_AGENT_ID}', 'policy', 'Other active', 'Leave other agent unchanged', 1,
'active', '2026-07-01T00:00:00+00:00', '2026-07-01T00:00:00+00:00'),
('{NULL_AGENT_NODE_ID}', null, 'policy', 'Shared active', 'Leave shared node unchanged', 1,
'active', '2026-07-01T00:00:00+00:00', '2026-07-01T00:00:00+00:00');
insert into public.strategy_node_anchors
(id, from_node_id, anchor_role, to_node_id, weight, note, created_at)
values
('{PRIOR_NODE_ANCHOR_ID}', '{PRIOR_ACTIVE_NODE_ID}', 'serves', '{PRIOR_RETIRED_NODE_ID}', 1.0,
'Existing anchor must survive strategy revision replay', '2026-07-01T00:00:00+00:00');
""" """
@ -631,13 +1008,17 @@ def source_postgres() -> Iterator[tuple[str, str]]:
"run", "run",
"--rm", "--rm",
"--detach", "--detach",
"--network",
"none",
"--name", "--name",
container, container,
"--label",
f"{rebuild.canonical_rebuild.CANARY_LABEL}={container}",
"--env", "--env",
f"POSTGRES_DB={database}", f"POSTGRES_DB={database}",
"--env", "--env",
"POSTGRES_HOST_AUTH_METHOD=trust", "POSTGRES_HOST_AUTH_METHOD=trust",
rebuild.canonical_rebuild.DEFAULT_IMAGE, rebuild.DEFAULT_REBUILD_IMAGE,
], ],
text=True, text=True,
capture_output=True, capture_output=True,
@ -692,14 +1073,24 @@ def source_postgres() -> Iterator[tuple[str, str]]:
capture_output=True, capture_output=True,
check=False, check=False,
) )
inspected = subprocess.run(
["docker", "container", "inspect", container],
text=True,
capture_output=True,
check=False,
)
if inspected.returncode == 0:
pytest.fail(f"fixture PostgreSQL container was not removed: {container}")
def test_live_genesis_plus_ledger_command_rebuilds_exactly_and_proves_cleanup( def capture_source_snapshot(
source_container: str,
database: str,
tmp_path: Path, tmp_path: Path,
source_postgres: tuple[str, str], *,
) -> None: stem: str,
source_container, database = source_postgres ) -> tuple[Path, Path]:
genesis_dump = tmp_path / "genesis.dump" dump = tmp_path / f"{stem}.dump"
dump_result = subprocess.run( dump_result = subprocess.run(
[ [
"docker", "docker",
@ -711,7 +1102,7 @@ def test_live_genesis_plus_ledger_command_rebuilds_exactly_and_proves_cleanup(
"-d", "-d",
database, database,
"--format=custom", "--format=custom",
"--file=/tmp/genesis.dump", f"--file=/tmp/{stem}.dump",
], ],
text=True, text=True,
capture_output=True, capture_output=True,
@ -719,15 +1110,130 @@ def test_live_genesis_plus_ledger_command_rebuilds_exactly_and_proves_cleanup(
) )
assert dump_result.returncode == 0, dump_result.stderr assert dump_result.returncode == 0, dump_result.stderr
copy_result = subprocess.run( copy_result = subprocess.run(
["docker", "cp", f"{source_container}:/tmp/genesis.dump", str(genesis_dump)], ["docker", "cp", f"{source_container}:/tmp/{stem}.dump", str(dump)],
text=True, text=True,
capture_output=True, capture_output=True,
check=False, check=False,
) )
assert copy_result.returncode == 0, copy_result.stderr assert copy_result.returncode == 0, copy_result.stderr
manifest = tmp_path / f"{stem}-manifest.jsonl"
capture_manifest(source_container, database, manifest)
return dump, manifest
genesis_manifest = tmp_path / "genesis-manifest.jsonl"
capture_manifest(source_container, database, genesis_manifest) def seed_proposal_and_approval(
source_container: str,
database: str,
approved: dict,
approval: dict,
) -> dict:
sql = f"""begin;
set local standard_conforming_strings = on;
insert into kb_stage.kb_proposals
select seeded.* from jsonb_populate_record(
null::kb_stage.kb_proposals, {rebuild._jsonb_literal(approved)}
) seeded;
insert into kb_stage.kb_proposal_approvals
select seeded.* from jsonb_populate_record(
null::kb_stage.kb_proposal_approvals, {rebuild._jsonb_literal(approval)}
) seeded;
commit;
"""
docker_psql(source_container, database, sql)
raw = docker_psql(
source_container,
database,
rebuild.build_proposal_readback_sql(approved["id"]),
).stdout.strip()
return json.loads(raw)
def run_genesis_ledger_command(
*,
tmp_path: Path,
database: str,
genesis_dump: Path,
genesis_manifest: Path,
material_path: Path,
final_manifest: Path,
artifact_stem: str,
container_prefix: str,
run_number: int,
) -> tuple[subprocess.CompletedProcess[str], dict, Path]:
ledger = {
"artifact": rebuild.LEDGER_ARTIFACT,
"contract_version": rebuild.LEDGER_CONTRACT_VERSION,
"engine": rebuild._engine_hashes(),
"genesis": {
"dump_sha256": sha256_file(genesis_dump),
"parity_manifest_sha256": sha256_file(genesis_manifest),
},
"entries": [
{
"sequence": 1,
"material": material_path.name,
"sha256": sha256_file(material_path),
"replay_material_sha256": json.loads(material_path.read_text(encoding="utf-8"))["replay_receipt"][
"hashes"
]["replay_material_sha256"],
}
],
"final_parity": {
"manifest": final_manifest.name,
"sha256": sha256_file(final_manifest),
},
}
ledger_path = write_json(tmp_path / f"{artifact_stem}-ledger-run-{run_number}.json", ledger)
output = tmp_path / f"{artifact_stem}-reconstruction-receipt-run-{run_number}.json"
command = [
sys.executable,
str(Path(rebuild.__file__).resolve()),
"--genesis-dump",
str(genesis_dump),
"--genesis-manifest",
str(genesis_manifest),
"--ledger",
str(ledger_path),
"--ledger-sha256",
sha256_file(ledger_path),
"--database",
database,
"--container-prefix",
container_prefix,
"--tmpfs-mb",
"256",
"--max-target-query-ms",
"5000",
"--max-target-source-ratio",
"100",
"--output",
str(output),
]
completed = subprocess.run(
command,
cwd=rebuild.REPO_ROOT,
text=True,
capture_output=True,
check=False,
timeout=180,
)
assert completed.returncode == 0, completed.stderr + completed.stdout
assert output.is_file(), "rebuild command succeeded without writing its receipt"
receipt = json.loads(output.read_text(encoding="utf-8"))
return completed, receipt, output
def test_live_genesis_plus_ledger_command_rebuilds_exactly_and_proves_cleanup(
tmp_path: Path,
source_postgres: tuple[str, str],
) -> None:
source_container, database = source_postgres
genesis_dump, genesis_manifest = capture_source_snapshot(
source_container,
database,
tmp_path,
stem="insert-only-genesis",
)
material = replay_material() material = replay_material()
material_path = write_json(tmp_path / "entry-0001.json", material) material_path = write_json(tmp_path / "entry-0001.json", material)
@ -753,64 +1259,18 @@ def test_live_genesis_plus_ledger_command_rebuilds_exactly_and_proves_cleanup(
final_manifest = tmp_path / "final-manifest.jsonl" final_manifest = tmp_path / "final-manifest.jsonl"
capture_manifest(source_container, database, final_manifest) capture_manifest(source_container, database, final_manifest)
ledger = { completed, receipt, output = run_genesis_ledger_command(
"artifact": rebuild.LEDGER_ARTIFACT, tmp_path=tmp_path,
"contract_version": rebuild.LEDGER_CONTRACT_VERSION, database=database,
"engine": rebuild._engine_hashes(), genesis_dump=genesis_dump,
"genesis": { genesis_manifest=genesis_manifest,
"dump_sha256": sha256_file(genesis_dump), material_path=material_path,
"parity_manifest_sha256": sha256_file(genesis_manifest), final_manifest=final_manifest,
}, artifact_stem="insert-only",
"entries": [ container_prefix="teleo-genesis-ledger-live-test",
{ run_number=1,
"sequence": 1,
"material": material_path.name,
"sha256": sha256_file(material_path),
"replay_material_sha256": entry.replay_material_sha256,
}
],
"final_parity": {
"manifest": final_manifest.name,
"sha256": sha256_file(final_manifest),
},
}
ledger_path = write_json(tmp_path / "ledger.json", ledger)
output = tmp_path / "reconstruction-receipt.json"
command = [
sys.executable,
str(Path(rebuild.__file__).resolve()),
"--genesis-dump",
str(genesis_dump),
"--genesis-manifest",
str(genesis_manifest),
"--ledger",
str(ledger_path),
"--ledger-sha256",
sha256_file(ledger_path),
"--database",
database,
"--container-prefix",
"teleo-genesis-ledger-live-test",
"--tmpfs-mb",
"256",
"--max-target-query-ms",
"5000",
"--max-target-source-ratio",
"100",
"--output",
str(output),
]
completed = subprocess.run(
command,
cwd=rebuild.REPO_ROOT,
text=True,
capture_output=True,
check=False,
timeout=180,
) )
assert completed.returncode == 0, completed.stderr + completed.stdout
receipt = json.loads(output.read_text(encoding="utf-8"))
assert receipt["status"] == "pass" assert receipt["status"] == "pass"
assert receipt["genesis_parity"]["status"] == "pass" assert receipt["genesis_parity"]["status"] == "pass"
assert receipt["ledger"]["entries"][0]["status"] == "pass" assert receipt["ledger"]["entries"][0]["status"] == "pass"
@ -831,3 +1291,131 @@ def test_live_genesis_plus_ledger_command_rebuilds_exactly_and_proves_cleanup(
check=False, check=False,
) )
assert inspect.returncode != 0 assert inspect.returncode != 0
def test_live_revise_strategy_rebuild_is_exact_repeatable_and_leaves_no_container(
tmp_path: Path,
source_postgres: tuple[str, str],
) -> None:
source_container, database = source_postgres
genesis_dump, genesis_manifest = capture_source_snapshot(
source_container,
database,
tmp_path,
stem="revise-strategy-genesis",
)
approved, _, approval = revise_strategy_proposal_rows()
pre_apply = seed_proposal_and_approval(
source_container,
database,
approved,
approval,
)
apply_sql = rebuild.apply_engine.build_apply_sql(pre_apply["proposal"], "kb-apply")
docker_psql(source_container, database, apply_sql, role="kb_apply")
post_apply = json.loads(
docker_psql(
source_container,
database,
rebuild.build_proposal_readback_sql(REVISE_PROPOSAL_ID),
).stdout.strip()
)
applied_proposal = post_apply["proposal"]
proposal_envelope = applied_envelope(applied_proposal)
source_receipt_path, receipt = rebuild.apply_engine.capture_replay_receipt(
argparse.Namespace(
container=source_container,
role="kb_apply",
host="127.0.0.1",
db=database,
receipt_out=str(tmp_path / "source-revise-strategy-private-receipt.json"),
receipt_dir=None,
),
proposal_envelope,
"",
apply_sql=apply_sql,
)
assert source_receipt_path.is_file()
assert stat.S_IMODE(source_receipt_path.stat().st_mode) == 0o600
material = {
"artifact": rebuild.MATERIAL_ARTIFACT,
"contract_version": rebuild.MATERIAL_CONTRACT_VERSION,
"sequence": 1,
"approved_proposal": pre_apply["proposal"],
"approval_snapshot": pre_apply["approval_snapshot"],
"applied_proposal": applied_proposal,
"replay_receipt": receipt,
}
material_path = write_json(tmp_path / "revise-strategy-entry-0001.json", material)
final_manifest = tmp_path / "revise-strategy-final-manifest.jsonl"
capture_manifest(source_container, database, final_manifest)
runs = [
run_genesis_ledger_command(
tmp_path=tmp_path,
database=database,
genesis_dump=genesis_dump,
genesis_manifest=genesis_manifest,
material_path=material_path,
final_manifest=final_manifest,
artifact_stem="revise-strategy",
container_prefix="teleo-genesis-ledger-revise",
run_number=run_number,
)
for run_number in (1, 2)
]
for completed, reconstruction, output in runs:
assert reconstruction["status"] == "pass"
assert reconstruction["genesis_parity"]["status"] == "pass"
assert reconstruction["final_parity"]["status"] == "pass"
entry_summary = reconstruction["ledger"]["entries"][0]
assert entry_summary["proposal_type"] == "revise_strategy"
assert entry_summary["proposal_seed_exact"] is True
assert entry_summary["canonical_seed_exact"] is False
assert entry_summary["seed_exact"] is False
assert entry_summary["guarded_apply_executed"] is True
assert entry_summary["mutating_prestate_captured"] is True
assert entry_summary["mutating_delta_validated"] is True
assert entry_summary["mutating_poststate_normalized"] is True
assert entry_summary["proposal_exact"] is True
assert entry_summary["canonical_rows_exact"] is True
assert reconstruction["cleanup"]["container_absent"] is True
assert reconstruction["safety"]["network_access_available_to_container"] is False
assert reconstruction["safety"]["private_replay_material_emitted"] is False
assert PRIVATE_MARKER not in completed.stdout
assert PRIVATE_MARKER not in output.read_text(encoding="utf-8")
assert stat.S_IMODE(output.stat().st_mode) == 0o600
container_name = reconstruction["container"]["name"]
inspect = subprocess.run(
["docker", "container", "inspect", container_name],
text=True,
capture_output=True,
check=False,
)
assert inspect.returncode != 0
labeled = subprocess.run(
[
"docker",
"ps",
"-aq",
"--filter",
f"label={rebuild.canonical_rebuild.CANARY_LABEL}={container_name}",
],
text=True,
capture_output=True,
check=False,
)
assert labeled.returncode == 0
assert labeled.stdout.strip() == ""
first = runs[0][1]
second = runs[1][1]
assert first["inputs"] == second["inputs"]
assert first["ledger"]["entries"][0]["material_sha256"] == second["ledger"]["entries"][0]["material_sha256"]
assert (
first["ledger"]["entries"][0]["replay_material_sha256"]
== second["ledger"]["entries"][0]["replay_material_sha256"]
)

View file

@ -116,6 +116,37 @@ def test_invalid_source_hash_refuses_to_prepare_a_child() -> None:
stage.prepare_normalized_child(parent) stage.prepare_normalized_child(parent)
def test_prepare_normalized_child_preserves_replayable_ingestion_manifest() -> None:
parent = _parent()
parent["payload"]["ingestion_manifest"] = {
"artifact_sha256": "a" * 64,
"extractor": {"name": "deterministic_fixture_replay", "version": "2"},
"replay_identity_sha256": "b" * 64,
"segments": [
{
"id": "message-1",
"locator": "telegram://chat/1/message/1",
"content_sha256": "c" * 64,
}
],
}
child = stage.prepare_normalized_child(parent)
assert (
child["payload"]["apply_payload"]["normalization_manifest"]["ingestion_manifest"]
== parent["payload"]["ingestion_manifest"]
)
def test_prepare_normalized_child_rejects_non_object_ingestion_manifest() -> None:
parent = _parent()
parent["payload"]["ingestion_manifest"] = "not-an-object"
with pytest.raises(stage.bound.CheckpointError, match="ingestion_manifest must be an object"):
stage.prepare_normalized_child(parent)
def test_stage_sql_validates_exact_pending_row_before_commit_and_never_writes_public() -> None: def test_stage_sql_validates_exact_pending_row_before_commit_and_never_writes_public() -> None:
child = stage.prepare_normalized_child(_parent()) child = stage.prepare_normalized_child(_parent())
sql = stage.build_stage_sql(child) sql = stage.build_stage_sql(child)