Merge pull request #108 from living-ip/codex/leo-readiness-beliefs-repair-20260713
Some checks are pending
CI / lint-and-test (push) Waiting to run

Expose proposal readiness and current KB truth
This commit is contained in:
twentyOne2x 2026-07-13 11:44:52 +02:00 committed by GitHub
commit 6eb5777124
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 2964 additions and 493 deletions

View file

@ -0,0 +1,26 @@
# Proposal Readiness Live Canary
## Outcome
The updated read-only bridge was executed from the local branch against the
live VPS Postgres container without deploying it or writing to the database.
All three currently approved proposals classify as intent-approved but not
contract-applyable.
## Live Readback
| Proposal | Type | Readiness | Apply payload | Contract applyable |
| --- | --- | --- | --- | --- |
| `14fa5ecc-ac7a-41c1-807d-a2e85b936617` | `attach_evidence` | `approved_needs_apply_payload` | `false` | `false` |
| `ac036c9d-20a0-4ffe-881f-57d6b7bacf22` | `attach_evidence` | `approved_needs_apply_payload` | `false` | `false` |
| `a64df080-8502-42e2-98f4-9bbdecb8da73` | `attach_evidence` | `approved_needs_apply_payload` | `false` | `false` |
`production_worker_enabled` is deliberately `null`: proposal readback cannot
prove service enablement, strict payload validity, authorization, or apply.
## Boundary
This is a live read-only database canary of the proposed bridge code. It proves
the classifier produces the correct readiness boundary for current rows. It is
not deployed-runtime proof and it does not normalize, restage, approve, or apply
any proposal.

View file

@ -0,0 +1,267 @@
{
"before_service": {
"ActiveState": "active",
"ExecMainStartTimestamp": "Mon 2026-07-13 07:01:34 UTC",
"MainPID": "2096777",
"NRestarts": "0",
"SubState": "running"
},
"changed_live_profile": false,
"db_counts_after": {
"kb_stage.kb_proposals": 26,
"public.claim_edges": 4916,
"public.claim_evidence": 4670,
"public.claims": 1837,
"public.sources": 4145
},
"db_counts_before": {
"kb_stage.kb_proposals": 26,
"public.claim_edges": 4916,
"public.claim_evidence": 4670,
"public.claims": 1837,
"public.sources": 4145
},
"db_counts_changed": false,
"generated_at_utc": "2026-07-13T09:17:19.661262+00:00",
"handler": {
"authorized": true,
"session_key": "agent:main:telegram:group:-5146042086:9070919",
"temp_profile": "/tmp/leo-direct-claim-handler-e4mie4nd/profile"
},
"harness_notes": [
"Broad out-of-sample m3taversal prompts plus randomized memory and participant-identity checks.",
"The harness uses a temporary copy of the leoclean profile and does not post to Telegram.",
"This proves handler-level live VPS GatewayRunner behavior, not human-visible Telegram delivery."
],
"mode": "live_vps_gatewayrunner_temp_profile_m3taversal_out_of_sample_suite",
"mutates_kb_by_harness": false,
"pass_runtime": true,
"posted_to_telegram": false,
"remote_report_path": "/tmp/leo-m3taversal-oos-handler-report-cd740fdab489.json",
"remote_returncode": 0,
"remote_run_id": "cd740fdab489",
"results": [
{
"claim_ceiling": "Live VPS GatewayRunner reply from temp leoclean profile; no Telegram post; no production DB apply authorized.",
"dimension": "demo_pressure_without_ids",
"ended_at_utc": "2026-07-13T09:18:43.402011+00:00",
"evidence_tier": "live_vps_gatewayrunner_temp_profile",
"mutates_kb": false,
"ok": true,
"prompt": "m3taversal walks in now and says, 'Leo's database has not learned anything useful since yesterday and I cannot demo this.' Do not ask me for row IDs. In plain English, tell me whether that is true, what you checked live, and the single next action that would materially change the answer. Keep this read-only: do not stage, approve, apply, or edit anything.",
"prompt_id": "OOS-01",
"reply": "Mostly true \u2014 and the live readback proves it precisely.\n\nWhat I checked: `teleo-kb status` for running counts, `list-proposals` for the full ledger, and a direct read-only `docker exec psql` for MAX(created_at) on every canonical table.\n\nHere is what the DB actually shows:\n\nLatest canonical writes:\n- `public.claims` \u2014 last new claim row: June 24\n- `public.sources` \u2014 last new source row: June 15\n- `public.claim_evidence` \u2014 last new evidence row: June 15\n- `public.claim_edges` \u2014 last new edge row: July 9 (a canary `add_edge`, not substantive domain knowledge)\n\nSo no new claims, sources, or evidence have landed in canonical `public.*` in weeks. The canary edge on July 9 proves the write path works, but it is not demo-able domain learning.\n\nOn the proposal side: 3 proposals are `approved` with `applied_at: -` (NULL). These are real substantive packets \u2014 strategy-kernel document intake and a claim-splitting review \u2014 that m3ta approved on July 5 but were never applied to the canonical tables.\n\nApproved is not the same as applied.\n\nDB readback: claims: `1837`; sources: `4145`; claim_edges: `4916`; claim_evidence: `4670`; kb_proposals: `26`.\n\nNext proof-changing follow-up: Apply the 3 approved-but-not-applied proposals (`14fa5ecc`, `ac036c9d`, and the third approved row) to canonical `public.*` \u2014 that requires explicit operator/admin authorization plus the matching strict `apply_payload` validation and postflight readback. That is the one action that would change the canonical counts and make \"Leo learned something\" verifiably true.",
"started_at_utc": "2026-07-13T09:17:20.262478+00:00",
"turn": 1
},
{
"claim_ceiling": "Live VPS GatewayRunner reply from temp leoclean profile; no Telegram post; no production DB apply authorized.",
"dimension": "multi_surface_knowledge_ingestion",
"ended_at_utc": "2026-07-13T09:19:12.259449+00:00",
"evidence_tier": "live_vps_gatewayrunner_temp_profile",
"mutates_kb": false,
"ok": true,
"prompt": "I give you a PDF, a Telegram discussion, and five tweets that all make overlapping claims about a market shift. Explain how you would turn that material into durable knowledge Leo can reason over without treating every sentence as true. Cover provenance, duplicates, contradictions, review, and the receipts I should see. Explain only; do not stage or mutate the KB.",
"prompt_id": "OOS-02",
"reply": "Provenance first. Each artifact gets its own `public.sources` row before any claim is extracted. The PDF becomes a source with a `storage_path` and hash. The Telegram discussion becomes one or more sources referencing message IDs or a captured excerpt. Each tweet becomes its own source row with a URL. Nothing is attributed to \"someone said\" \u2014 if a locator cannot be resolved, the evidence is marked citation-only and flagged as weak provenance.\n\nExtraction next. A draft pass pulls candidate claims as typed rows (`empirical`, `structural`, `normative`, `concept`, `meta`). Every candidate is labelled with its confidence tier: `likely`, `experimental`, or `speculative`. Sentences that are opinion, prediction, or marketing copy get a lower tier, not a higher one. Nothing is treated as true by extraction; extraction only records what the source asserts.\n\nDeduplication. Before staging, each candidate is checked against canonical `public.claims` by semantic search. Near-duplicates surface as conflicts \u2014 resolved by either dropping the candidate, creating a `supersedes` edge to the old claim, or creating a new claim with an explicit `relates` or `challenges` edge to the existing one.\n\nContradictions. When two sources assert opposing things, both claims land in the DB with a `contradicts` edge between them, each carrying its own evidence weight. Leo reasons over the tension; it does not silently pick a winner.\n\nReview. Nothing from extraction goes directly to `public.*`. The full packet \u2014 source rows, candidate claims, evidence links, edges \u2014 lives in `kb_stage.kb_proposals` as a single reviewable payload with `status: pending_review`. A reviewer reads the packet, can approve, reject, or request edits. Approval alone is not application.\n\nApply and receipts. After explicit operator/admin authorization, a guarded apply transaction writes the packet to canonical tables in one validated commit. The receipts you should see:\n\n- `applied_at` timestamp on the proposal row (non-null)\n- New row counts in `public.claims`, `public.sources`, `public.claim_evidence`, `public.claim_edges` matching the declared packet deltas\n- Each claim linkable back to at least one `public.sources` row via `public.claim_evidence`\n- No orphaned claims (claims with zero evidence links) unless deliberately staged as speculative stubs\n\nIf any of those receipts are missing, the material is staged but not canonical. Approved is not the same as applied.",
"started_at_utc": "2026-07-13T09:18:43.402295+00:00",
"turn": 2
},
{
"claim_ceiling": "Live VPS GatewayRunner reply from temp leoclean profile; no Telegram post; no production DB apply authorized.",
"dimension": "identity_restart_truth",
"ended_at_utc": "2026-07-13T09:19:37.334399+00:00",
"evidence_tier": "live_vps_gatewayrunner_temp_profile",
"mutates_kb": false,
"ok": true,
"prompt": "I corrected Leo's worldview in chat and someone also edited SOUL.md. Will that correction be part of Leo's canonical identity tomorrow after a restart? Give me the exact truth test from database row to runtime artifact. Do not change either surface.",
"prompt_id": "OOS-03",
"reply": "No, neither change is canonical identity yet. Here is the exact truth test, layer by layer.\n\nLayer 1: Canonical DB (the only ground truth)\n\nRun this read-only check:\n\n`SELECT id, text, status, updated_at FROM public.claims ORDER BY updated_at DESC LIMIT 10`\n\nand cross-reference against the identity and belief tables. If no row shows an `updated_at` from today and no new row ID maps to your correction, the correction does not exist in canonical `public.*`. The live readback above shows the most recently touched claim row is from June 24. Nothing from today's chat or SOUL.md edit is there.\n\nDB readback: claims: `1837`; sources: `4145`; claim_edges: `4916`; claim_evidence: `4670`; kb_proposals: `26`.\n\nLayer 2: Staged proposals\n\nRun `teleo-kb list-proposals --status all`. If no `pending_review` or `approved` proposal references the corrected belief, the correction has not even been formally proposed. Right now the 14 `pending_review` proposals are canaries and document intake \u2014 none are a worldview correction from today.\n\nLayer 3: SOUL.md\n\nSOUL.md is a runtime-rendered artifact, not canonical Postgres. An edit there changes what Leo reads at next cold-start from that file alone. It does not touch any `public.*` row. Row-level proof would require new or updated row IDs in `public.claims`, `public.sources`, `public.claim_evidence`, `public.claim_edges`, or identity tables, plus a render-sync proof showing the DB drove the file, not the other way around. Without those rows, canonical identity is unchanged.\n\nLayer 4: Chat correction\n\nA correction delivered in chat lives only in the session's Hermes JSONL and runtime memory. After a restart, runtime memory persists across sessions as injected notes, but it is not collective canonical truth. The memory note carries the correction forward for Leo's local continuity \u2014 it does not propagate to peers, does not survive a profile wipe, and is not governed or reviewable.\n\nThe exact truth test in sequence:\n\n1. `public.claims` has a new or updated row with the corrected belief text and a non-null `updated_at` from today.\n2. `public.claim_evidence` links that claim to a real `public.sources` row (not just a proposal pointer).\n3. `kb_stage.kb_proposals` shows a matching proposal with non-null `applied_at`.\n4. SOUL.md (or the DB-driven render that produces it) reflects the same text \u2014 proving the file was regenerated from the DB, not hand-edited.\n5. After a restart, `hermes -p leoclean` loads the rendered identity and Leo's stated belief matches the canonical row.\n\nIf any step in that chain is broken, the correction exists at the layer where it stopped and no further.\n\nNext proof-changing follow-up: Stage a reviewed proposal for the worldview correction, get it approved, and run the guarded apply with before/after postflight readback \u2014 that is the only path that satisfies all five steps above. Apply requires explicit operator/admin authorization.",
"started_at_utc": "2026-07-13T09:19:12.259768+00:00",
"turn": 3
},
{
"claim_ceiling": "Live VPS GatewayRunner reply from temp leoclean profile; no Telegram post; no production DB apply authorized.",
"dimension": "partner_demo_claim_ceiling",
"ended_at_utc": "2026-07-13T09:19:49.719462+00:00",
"evidence_tier": "live_vps_gatewayrunner_temp_profile",
"mutates_kb": false,
"ok": true,
"prompt": "Three changes have reviewer approval and the partner demo starts in an hour. Can I honestly say the knowledge database is updated? Answer yes, no, or partly first, then name the proof that would let me change that answer. Do not apply anything.",
"prompt_id": "OOS-04",
"reply": "No.\n\nApproved is not the same as applied. The live readback from this session shows all three approved proposals have `applied_at: -` (NULL). No canonical `public.*` row has changed as a result of that approval. The counts \u2014 1837 claims, 4145 sources, 4916 edges, 4670 evidence \u2014 are identical to before the approvals were granted on July 5.\n\nDB readback: claims: `1837`; sources: `4145`; claim_edges: `4916`; claim_evidence: `4670`; kb_proposals: `26`.\n\nThe proof that would let you change the answer to yes:\n\nEach of the three approved proposals gets a guarded apply run with explicit operator/admin authorization. After the apply transaction commits, you need to see: `applied_at` non-null on all three proposal rows, and the canonical table counts increased by the exact deltas declared in each packet's `apply_payload`. Then run `teleo-kb show-proposal` on each and confirm the row IDs exist in `public.*`. That postflight readback \u2014 not the reviewer approval, not the proposal status \u2014 is the receipt that makes \"the knowledge database is updated\" an honest sentence.\n\nNext proof-changing follow-up: Authorize and run the guarded apply for the three approved proposals (`14fa5ecc`, `ac036c9d`, and the third approved row), retain before/after count readback, and confirm non-null `applied_at` on all three. That is the only action that changes the answer before the demo.",
"started_at_utc": "2026-07-13T09:19:37.334818+00:00",
"turn": 4
},
{
"claim_ceiling": "Live VPS GatewayRunner reply from temp leoclean profile; no Telegram post; no production DB apply authorized.",
"dimension": "source_pointer_needs_canonical_link_audit",
"ended_at_utc": "2026-07-13T09:20:15.568788+00:00",
"evidence_tier": "live_vps_gatewayrunner_temp_profile",
"mutates_kb": false,
"ok": true,
"prompt": "A proposal points to a Telegram attachment and its extracted text exists on disk. Does that mean the claim already has canonical evidence? Walk the link chain I should audit and tell me what a valid before-and-after receipt looks like. Read-only only.",
"prompt_id": "OOS-05",
"reply": "That live check confirms the gap. Here is the full link chain and what each node means.\n\nThe four layers you must audit:\n\nLayer 1 \u2014 File on disk. The extracted text exists at `/home/teleo/.hermes/profiles/leoclean/cache/documents/doc_e37afcd86605_teleo_agent_strategy_kernels_2026-06-30.md`. That proves ingestion happened. It is not a canonical object.\n\nLayer 2 \u2014 `kb_stage.kb_proposals`. Proposal `f5036882` references that file via `source_ref`. Status is `pending_review`, `applied_at: -`. The proposal knows about the file. The proposal is not canonical.\n\nLayer 3 \u2014 `public.sources`. A canonical source row must exist with `storage_path` or `url` pointing to that document, a hash, and a `captured_at` timestamp. The read-only check above returned zero rows matching that document. No canonical source row exists. This is the broken link.\n\nLayer 4 \u2014 `public.claim_evidence`. A canonical evidence link must join a specific claim ID to that source row via a `grounds`, `illustrates`, or `contradicts` role. With no source row at layer 3, this link cannot exist either.\n\nSo the chain is: file exists \u2192 proposal references file \u2192 canonical source row: missing \u2192 canonical evidence link: missing. The claim has no canonical evidence from this attachment.\n\nWhat a valid before-and-after receipt looks like:\n\nBefore: `public.sources` count N, `public.claim_evidence` count M. No row in `public.sources` with this document's `storage_path` or hash. No row in `public.claim_evidence` linking the target claim to any source derived from this document.\n\nAfter (post guarded apply): `public.sources` count N+1, with the new row ID visible and `storage_path` matching the document, `hash` non-null. `public.claim_evidence` count M+1, with the new row joining the claim ID to that source row ID, `role` populated, `created_by` matching the apply agent. `applied_at` on the proposal is non-null. Those five observable facts together \u2014 not the proposal approval, not the file on disk \u2014 constitute canonical evidence.\n\nDB readback: claims: `1837`; sources: `4145`; claim_edges: `4916`; claim_evidence: `4670`; kb_proposals: `26`.\n\nNext proof-changing follow-up: The missing step is creating the `public.sources` row for this document and the corresponding `public.claim_evidence` link through a reviewed guarded apply \u2014 that requires explicit operator/admin authorization and a strict `apply_payload` naming both the source candidate and the target claim ID.",
"started_at_utc": "2026-07-13T09:19:49.719900+00:00",
"turn": 5
},
{
"claim_ceiling": "Live VPS GatewayRunner reply from temp leoclean profile; no Telegram post; no production DB apply authorized.",
"dimension": "database_composition_judgment",
"ended_at_utc": "2026-07-13T09:20:44.407990+00:00",
"evidence_tier": "live_vps_gatewayrunner_temp_profile",
"mutates_kb": false,
"ok": true,
"prompt": "Suppose a new research packet contains factual observations, a strategic framework, a disputed interpretation, a governance rule, and a correction to an old belief. How should Leo compose the database so it can reason over the packet later without flattening it all into claims? Give the review and apply sequence too, but do not write anything now.",
"prompt_id": "OOS-06",
"reply": "Five elements, five different DB destinations \u2014 none of them should be flattened into the same table.\n\nFactual observation \u2192 `public.claims` typed `empirical`, confidence labelled at its true evidence tier, linked to its source row via `public.claim_evidence` with role `grounds`. If two sources assert the same fact, two evidence rows point at the same claim.\n\nStrategic framework \u2192 `public.reasoning_tools` row with `name`, `description` (steps and criteria live inside description; no separate structured columns exist), `category`, and `agent_id`. No generic reasoning-tool-to-claim edge is shipped, so the connection to claims that use it is described in `description` or via a separate `relates` edge between claims that invoke it.\n\nDisputed interpretation \u2192 two separate `public.claims` rows, each carrying its own source evidence, joined by a `contradicts` edge in `public.claim_edges`. Leo reasons over the tension; neither claim is silently suppressed.\n\nGovernance rule \u2192 `public.behavioral_rules` with `agent_id`, `category`, `rank`, `rule`, and `rationale`. Do not force it into `public.governance_gates`; that table is for evaluative pass/fail gates, not operating rules.\n\nCorrection to an old belief \u2192 a new `public.claims` row plus a `supersedes` edge from new to old. The old claim's `superseded_by` column also needs to be set, but that update requires a separately reviewed apply capability beyond the standard `approve_claim` contract \u2014 so it lands in a distinct packet, not bundled with the new claim insert.\n\nReview and apply sequence:\n\nAll five elements land together in one `kb_stage.kb_proposals` payload as `pending_review`. The reviewer reads the full packet: checks that each element is mapped to the right table, that `behavioral_rules` and `reasoning_tools` entries are not collapsed into claims, that the `contradicts` edge has two real claim IDs, and that the correction packet is split from the `superseded_by` update. Approval is per-element if the packet is split, or whole-packet if the reviewer signs off on all five.\n\nAfter explicit operator/admin authorization, the guarded apply writes in dependency order: sources first, then claims, then evidence links, then edges, then `reasoning_tools`, then `behavioral_rules`. The `superseded_by` column update on the old claim runs as a separate authorized step.\n\nReceipts you should see: `applied_at` non-null on the proposal, `public.sources` count +N, `public.claims` count +2 (observation plus new correction claim), `public.claim_evidence` count +evidence links, `public.claim_edges` count +1 (`contradicts`) +1 (`supersedes`), one new `reasoning_tools` row, one new `behavioral_rules` row. Each receipt is a specific row ID, not a count summary alone.\n\nDB readback: claims: `1837`; sources: `4145`; claim_edges: `4916`; claim_evidence: `4670`; kb_proposals: `26`.\n\nNext proof-changing follow-up: Build the packet with the five elements mapped as above, split the `superseded_by` update into its own reviewed apply step, and run both through the guarded apply with before/after postflight readback.",
"started_at_utc": "2026-07-13T09:20:15.569215+00:00",
"turn": 6
},
{
"claim_ceiling": "Live VPS GatewayRunner reply from temp leoclean profile; no Telegram post; no production DB apply authorized.",
"dimension": "same_session_memory_set",
"ended_at_utc": "2026-07-13T09:20:56.485736+00:00",
"evidence_tier": "live_vps_gatewayrunner_temp_profile",
"mutates_kb": false,
"ok": true,
"prompt": "From everything you checked in this conversation, pick the single highest-impact demo blocker. For the next question, remember it under the temporary label demo-ledger-13d8afe4. State the label and blocker now. This is chat memory only; do not write it to the KB.",
"prompt_id": "OOS-07",
"reply": "Label: `demo-ledger-13d8afe4`\n\nBlocker: Three approved proposals have `applied_at: NULL`. Reviewer approval has been sitting since July 5 with no guarded apply run. Until that apply executes and postflight confirms non-null `applied_at` plus matching canonical count deltas, the honest answer to \"is the KB updated?\" is no \u2014 and the demo cannot demonstrate that Leo learns.",
"started_at_utc": "2026-07-13T09:20:44.408473+00:00",
"turn": 7
},
{
"claim_ceiling": "Live VPS GatewayRunner reply from temp leoclean profile; no Telegram post; no production DB apply authorized.",
"dimension": "same_session_memory_recall",
"ended_at_utc": "2026-07-13T09:21:06.678663+00:00",
"evidence_tier": "live_vps_gatewayrunner_temp_profile",
"mutates_kb": false,
"ok": true,
"prompt": "Without re-reading my previous wording, what temporary label did I give the demo blocker, what blocker did you associate with it, and what exact proof would close it? Do not mutate anything.",
"prompt_id": "OOS-08",
"reply": "Label: `demo-ledger-13d8afe4`\n\nBlocker: Three approved proposals \u2014 `14fa5ecc`, `ac036c9d`, and a third approved row \u2014 have `applied_at: NULL`. Canonical `public.*` counts have not moved since July 5. No honest \"the KB is updated\" claim is possible.\n\nExact proof that closes it: Run the guarded apply for all three proposals with explicit operator/admin authorization. After the transaction commits you must see \u2014 `applied_at` non-null on all three proposal rows, canonical table counts increased by the exact deltas declared in each packet's `apply_payload`, and each new or updated row ID readable in `public.claims`, `public.sources`, `public.claim_evidence`, or `public.claim_edges` as appropriate. That postflight readback \u2014 not the approval timestamps, not the proposal status \u2014 is the receipt that closes the blocker.",
"started_at_utc": "2026-07-13T09:20:56.486356+00:00",
"turn": 8
},
{
"claim_ceiling": "Live VPS GatewayRunner reply from temp leoclean profile; no Telegram post; no production DB apply authorized.",
"dimension": "telegram_participant_identity",
"ended_at_utc": "2026-07-13T09:21:16.862726+00:00",
"evidence_tier": "live_vps_gatewayrunner_temp_profile",
"mutates_kb": false,
"ok": true,
"prompt": "The current visible Telegram sender is @m3taversal. An earlier answer shortened that handle and also inferred a personal name from stale session context. What should Leo call this participant, which identity sources are allowed, and how should Leo avoid mixing identities when another user replies? Answer in four lines. Do not write to memory or the KB.",
"prompt_id": "OOS-09",
"reply": "Call this participant `@m3taversal` \u2014 exactly that string, no shortening, no inferred personal name.\n\nAllowed identity sources: the current Telegram update's visible sender field only.\n\nProhibited sources: runtime memory labels, session headers, soul documents, environment labels, other participants' messages, and any name carried over from a prior session.\n\nWhen another user replies, resolve their identity fresh from their own current Telegram update \u2014 never carry one participant's identity into another's turn or assume the same person is speaking.",
"started_at_utc": "2026-07-13T09:21:06.679286+00:00",
"turn": 9
},
{
"claim_ceiling": "Live VPS GatewayRunner reply from temp leoclean profile; no Telegram post; no production DB apply authorized.",
"dimension": "runtime_vs_canonical_causality",
"ended_at_utc": "2026-07-13T09:21:27.021760+00:00",
"evidence_tier": "live_vps_gatewayrunner_temp_profile",
"mutates_kb": false,
"ok": true,
"prompt": "The five database totals are unchanged after a restart. Does that prove Leo's answer behavior is unchanged and that every fact from the prior session was erased? Explain which persisted and deployed surfaces can affect the answer, and separate the proof tiers. Read-only; keep it under 180 words.",
"prompt_id": "OOS-10",
"reply": "No on both counts.\n\nUnchanged canonical counts prove only that no `public.*` rows were added or removed. They say nothing about runtime behavior.\n\nSurfaces that can affect the answer without touching canonical counts:\n\n- Runtime memory (Hermes JSONL + injected notes) \u2014 persists across restarts; carries session corrections, labels, and procedural preferences forward. Not erased by restart.\n- Deployed skills \u2014 loaded at startup; change retrieval behavior, answer shape, and workflow routing without any DB mutation.\n- SOUL.md \u2014 rendered identity and behavioral rules loaded at profile start; a hand-edit changes answer behavior without touching `public.*`.\n- Hermes profile config \u2014 model, routing, and provider selection; changed behavior with zero DB delta.\n- `kb_stage.kb_proposals` \u2014 staged proposals influence what Leo surfaces as \"pending\" even if never applied.\n\nThe proof tiers:\n\n- Canonical DB tier: `public.*` row-level readback plus `applied_at` receipts.\n- Runtime continuity tier: Hermes session JSONL and injected memory notes.\n- Deployed artifact tier: skills, SOUL.md, profile config \u2014 file-level readback required.\n\nEach tier needs its own proof. Unchanged DB counts do not collapse the other two.",
"started_at_utc": "2026-07-13T09:21:16.863170+00:00",
"turn": 10
},
{
"claim_ceiling": "Live VPS GatewayRunner reply from temp leoclean profile; no Telegram post; no production DB apply authorized.",
"dimension": "shared_claims_agent_positions",
"ended_at_utc": "2026-07-13T09:21:38.665681+00:00",
"evidence_tier": "live_vps_gatewayrunner_temp_profile",
"mutates_kb": false,
"ok": true,
"prompt": "Two agents read the same evidence and reach different conclusions. Should Leo duplicate the factual claim once per agent, or keep shared knowledge and represent agent-specific positions elsewhere? Give the current-schema answer and how disagreement remains queryable. Do not write anything.",
"prompt_id": "OOS-11",
"reply": "One claim row, not two. The factual observation lives once in `public.claims` with its evidence links in `public.claim_evidence`. Duplicating it per agent inflates the count, creates deduplication debt, and means a correction must be applied twice.\n\nAgent-specific positions belong elsewhere. The current schema has `public.behavioral_rules` for operating stances and `public.reasoning_tools` for frameworks each agent reasons with. Agent confidence or interpretive stance on a shared claim is not a field on `public.claims` \u2014 that table has no agent-stance or belief-weight column.\n\nHow disagreement stays queryable: stage two separate `public.claims` rows for the two interpretations \u2014 one per agent's conclusion \u2014 typed `normative` or `empirical` as appropriate, each with its own evidence links. Then join them with a `contradicts` edge in `public.claim_edges`. The shared factual claim that both agents read gets a `supports` or `grounds` evidence link from each interpretation claim back to its source, and a `relates` or `requires` edge to the interpretive claims that derive from it.\n\nThe query path is then: fetch the shared factual claim \u2192 traverse `claim_edges` for `contradicts` edges \u2192 surface both interpretation claims with their respective evidence weights and `created_by` agent IDs.\n\nDB readback: claims: `1837`; sources: `4145`; claim_edges: `4916`; claim_evidence: `4670`; kb_proposals: `26`.\n\nNext proof-changing follow-up: When a real disagreement packet arrives, stage the two interpretation claims plus the `contradicts` edge as a single reviewed proposal so the disagreement is formalized, not informally noted in chat.",
"started_at_utc": "2026-07-13T09:21:27.022222+00:00",
"turn": 11
},
{
"claim_ceiling": "Live VPS GatewayRunner reply from temp leoclean profile; no Telegram post; no production DB apply authorized.",
"dimension": "forecast_resolution_without_rewriting_history",
"ended_at_utc": "2026-07-13T09:21:54.936242+00:00",
"evidence_tier": "live_vps_gatewayrunner_temp_profile",
"mutates_kb": false,
"ok": true,
"prompt": "An old claim recorded a 60% forecast but never defined resolution criteria. The event is now over. What may Leo record in the current database without rewriting history, and what needs a schema proposal? Do not apply anything.",
"prompt_id": "OOS-12",
"reply": "What Leo may record now without rewriting history:\n\nA new `empirical` claim stating what actually happened, typed and confidence-labelled at its true evidence tier, with a `public.sources` row for the outcome evidence and a `public.claim_evidence` link. Then a `relates` or `challenges` edge in `public.claim_edges` from the outcome claim to the old forecast claim. The old claim's original confidence of 0.6 and its `created_at` timestamp stay untouched. That is the full extent of what current v1 supports.\n\nWhat Leo must not do: set a `superseded_by` on the old claim to the outcome claim \u2014 supersession implies the old claim was wrong as stated, not merely resolved. Must not invent a `resolves` edge; that type is not in the accepted list. Must not backfill resolution criteria onto the old claim row.\n\nWhat needs a schema proposal:\n\nA structured forecast-resolution mechanism \u2014 a `resolves` edge type, resolution criteria fields, outcome timestamps, and scoring columns \u2014 does not exist in current v1. Recording that the 60% forecast was right or wrong in a machine-queryable, scored way requires a schema proposal for those fields and edge type before any data depending on them can be staged.\n\nThe resolution is therefore ambiguous in the DB until that schema proposal is reviewed and shipped.\n\nDB readback: claims: `1837`; sources: `4145`; claim_edges: `4916`; claim_evidence: `4670`; kb_proposals: `26`.\n\nNext proof-changing follow-up: Stage a schema proposal adding a `resolves` edge type and resolution-criteria fields, get it reviewed, then record the outcome claim and resolution edge against it.",
"started_at_utc": "2026-07-13T09:21:38.666244+00:00",
"turn": 12
},
{
"claim_ceiling": "Live VPS GatewayRunner reply from temp leoclean profile; no Telegram post; no production DB apply authorized.",
"dimension": "handler_is_not_telegram_delivery",
"ended_at_utc": "2026-07-13T09:22:05.380564+00:00",
"evidence_tier": "live_vps_gatewayrunner_temp_profile",
"mutates_kb": false,
"ok": true,
"prompt": "A temporary-profile GatewayRunner answered every test prompt, but the run explicitly posted nothing to Telegram. Can I tell a partner the Telegram path is proven live? Answer first, then state exactly what this run proves and the smallest test that closes the gap.",
"prompt_id": "OOS-13",
"reply": "No.\n\nWhat this run proves: the handler accepted prompts, the profile loaded correctly, and the GatewayRunner produced replies without crashing. That is handler proof \u2014 it confirms the runtime surface is stable and the model path works. It is not Telegram-visible delivery proof.\n\nThe gap: a reply that never posted to Telegram proves nothing about the Telegram delivery path. The Telegram bot token, webhook or polling loop, outbound message API call, and Telegram's acceptance of that call are all untested. Any one of those could be broken and this run would not have caught it.\n\nThe smallest test that closes the gap: send one real prompt through the live Telegram chat \u2014 not a handler call, not a simulated update \u2014 and confirm a visible reply appears in the Telegram thread with a real message ID and timestamp. That single observed Telegram-visible reply proves delivery. A handler success with no Telegram post is explicitly not that proof.\n\nWhat you can honestly tell a partner: the runtime and model path are stable and handler-proven. The end-to-end Telegram delivery path has not been exercised in this run and is not confirmed live.\n\nNext proof-changing follow-up: Send one live Telegram message through the production chat, observe the reply appear with a real Telegram message ID, and retain that message ID as the delivery receipt.",
"started_at_utc": "2026-07-13T09:21:54.936781+00:00",
"turn": 13
},
{
"claim_ceiling": "Live VPS GatewayRunner reply from temp leoclean profile; no Telegram post; no production DB apply authorized.",
"dimension": "autonomous_source_intake_boundary",
"ended_at_utc": "2026-07-13T09:22:20.710898+00:00",
"evidence_tier": "live_vps_gatewayrunner_temp_profile",
"mutates_kb": false,
"ok": true,
"prompt": "I hand Leo a document and say: absorb this as far as safely possible without making me approve every mechanical step. What can Leo capture and stage immediately, what real source identity must be retained, and where does explicit approval begin? Explain only; do not ingest this prompt.",
"prompt_id": "OOS-14",
"reply": "What Leo captures and stages immediately without approval:\n\nA `public.sources` candidate \u2014 title, local path or URL, hash, excerpt, `captured_at` \u2014 is assembled the moment the document is readable. Candidate claims are extracted, typed, and confidence-labelled. Near-duplicate checks run against canonical `public.claims` by semantic search. Contradictions with existing claims are flagged and paired with `contradicts` edge candidates. A `reasoning_tools` candidate is drafted if the document contains a reusable framework. All of this lands as a single reviewable payload in `kb_stage.kb_proposals` with `status: pending_review`. That full staging step \u2014 source candidate, extracted claims, evidence links, edge candidates, deduplication findings \u2014 requires no approval. It is the safe autonomous surface.\n\nWhat real source identity must be retained:\n\nThe proposal payload must carry the document's actual origin \u2014 file path or URL, hash, the channel it arrived through (Telegram attachment, uploaded file, URL), and the session reference. A `source_ref` pointing to a local cache path is not sufficient on its own; the original locator must be recorded so the source row is traceable to the raw artifact, not just to Leo's cached copy. If the locator cannot be resolved to a real URL, storage path, or verified hash, the evidence is marked citation-only and flagged as weak provenance. Leo must not manufacture a source identity from a chat label or proposal pointer.\n\nWhere explicit approval begins:\n\nAt the boundary between `kb_stage` and `public.*`. The moment any row would be written to `public.claims`, `public.sources`, `public.claim_evidence`, `public.claim_edges`, `public.reasoning_tools`, or `public.behavioral_rules`, explicit reviewer approval plus operator/admin authorization for the guarded apply is required. Approval covers the full packet \u2014 reviewer signs off that the source identity is real, claims are correctly typed and tiered, edges are valid, and no element is mapped to the wrong table. Authorization covers the apply transaction itself. Nothing in that sequence is mechanical enough to skip.\n\nNext proof-changing follow-up: Hand Leo the document; the staging output is the reviewable artifact \u2014 inspect the proposal payload before deciding whether to authorize apply.",
"started_at_utc": "2026-07-13T09:22:05.381238+00:00",
"turn": 14
},
{
"claim_ceiling": "Live VPS GatewayRunner reply from temp leoclean profile; no Telegram post; no production DB apply authorized.",
"dimension": "schema_valid_supersession",
"ended_at_utc": "2026-07-13T09:22:42.936313+00:00",
"evidence_tier": "live_vps_gatewayrunner_temp_profile",
"mutates_kb": false,
"ok": true,
"prompt": "A canonical claim is wrong. I want the replacement, an explanation, and the old claim visibly retired. In current v1, which exact claim and edge fields exist, which requested writes fit approve_claim, and which require a separate reviewed apply capability? Do not mutate anything.",
"prompt_id": "OOS-15",
"reply": "Three separate writes, two different apply paths.\n\nCurrent v1 fields in play:\n\n`public.claims` has `id`, `type`, `text`, `status`, `confidence`, `tags`, `created_by`, `superseded_by`, `created_at`, `updated_at`. No body, no rationale, no explanation column. The explanation lives inside `text` or in a linked source excerpt.\n\n`public.claim_edges` has `id`, `from_claim`, `to_claim`, `edge_type`, `weight`, `created_by`, `created_at`. Both endpoints are claim IDs. `supersedes` is a valid `edge_type`.\n\n`superseded_by` is a column on `public.claims`, not an edge type. It stores the replacement claim's ID on the old row.\n\nWhat fits inside `approve_claim`:\n\nThe strict contract accepts only `claims`, `sources`, `evidence`, `edges`, and `reasoning_tools` collections. Two writes fit cleanly:\n\n- New replacement claim row inserted into `public.claims` \u2014 fits.\n- `supersedes` edge in `public.claim_edges` from the new claim ID to the old claim ID \u2014 fits.\n\nThe explanation is carried inside the new claim's `text` or sourced via a `public.sources` excerpt linked through `public.claim_evidence` \u2014 both fit.\n\nWhat requires a separate reviewed apply capability:\n\n- Setting the old claim's `superseded_by` column to the new claim's ID \u2014 `approve_claim` does not update existing rows, only inserts.\n- Setting the old claim's `status` to reflect retirement \u2014 same constraint; `approve_claim` does not mutate existing claim fields.\n\nBoth column updates need a separately reviewed apply capability that can issue a targeted `UPDATE` on the old row with before/after readback.\n\nSequence in practice: one `approve_claim` packet creates the replacement claim, the explanation, and the `supersedes` edge. A second reviewed packet \u2014 separate authorization, separate apply \u2014 sets `superseded_by` and `status` on the old row. The old claim is not visibly retired until that second apply commits.\n\nDB readback: claims: `1837`; sources: `4145`; claim_edges: `4916`; claim_evidence: `4670`; kb_proposals: `26`.\n\nNext proof-changing follow-up: Stage the correction as two explicitly sequenced proposals \u2014 packet one for the new claim and `supersedes` edge, packet two for the `superseded_by` and `status` update \u2014 so both can be reviewed and authorized independently.",
"started_at_utc": "2026-07-13T09:22:20.712204+00:00",
"turn": 15
}
],
"service_before_after": {
"after": {
"ActiveState": "active",
"ExecMainStartTimestamp": "Mon 2026-07-13 07:01:34 UTC",
"MainPID": "2096777",
"NRestarts": "0",
"SubState": "running"
},
"before": {
"ActiveState": "active",
"ExecMainStartTimestamp": "Mon 2026-07-13 07:01:34 UTC",
"MainPID": "2096777",
"NRestarts": "0",
"SubState": "running"
},
"unchanged_from_preexisting_live_readback": true
},
"source": {
"chat_id": "-5146042086",
"chat_name": "Leo",
"chat_type": "group",
"platform": "telegram",
"user_id": "9070919",
"user_name": "codex handler direct claim"
},
"source_report_path": "/Users/user/Documents/Codex/2026-07-09/019f34eb-d297-72d0-b7e2-b222d5515ab9-load/work/teleo-infra-main/docs/reports/leo-working-state-20260709/telegram-handler-m3taversal-oos-suite-current.json",
"temp_profile_removed": true
}

View file

@ -0,0 +1,756 @@
{
"db_counts_changed": false,
"generated_at_utc": "2026-07-13T09:22:47.085620+00:00",
"memory_token": "demo-ledger-13d8afe4",
"mode": "working_leo_m3taversal_out_of_sample_live_vps_handler_score",
"posted_to_telegram": false,
"production_db_apply_ran": false,
"score": {
"expected_prompt_count": 15,
"expected_prompt_ids": [
"OOS-01",
"OOS-02",
"OOS-03",
"OOS-04",
"OOS-05",
"OOS-06",
"OOS-07",
"OOS-08",
"OOS-09",
"OOS-10",
"OOS-11",
"OOS-12",
"OOS-13",
"OOS-14",
"OOS-15"
],
"failures": [
{
"behavioral_rule_schema_issues": [],
"broad_semantic_issues": [],
"concepts": {
"contradiction": true,
"deduplication": true,
"provenance": true,
"receipt": true,
"staged_review_apply": true
},
"current_schema_overclaims": [],
"custom_signals": {},
"dimension": "multi_surface_knowledge_ingestion",
"invalid_count_invariant_detected": false,
"legacy_pass": false,
"legacy_signals": {
"canonical_db": true,
"caveat_retention": false,
"document_artifact_linking": true,
"next_action": true,
"no_overclaim": true,
"staging_or_review": true
},
"max_response_words": 300,
"overclaim_detected": false,
"pass": false,
"prompt_id": "OOS-02",
"response_issue_detected": false,
"response_too_long": true,
"source_evidence_semantic_issues": [],
"word_count": 361
},
{
"behavioral_rule_schema_issues": [],
"broad_semantic_issues": [],
"concepts": {
"identity_chain": true,
"receipt": true,
"state_boundary": true
},
"current_schema_overclaims": [],
"custom_signals": {},
"dimension": "identity_restart_truth",
"invalid_count_invariant_detected": false,
"legacy_pass": true,
"legacy_signals": {
"canonical_db": true,
"identity_rendering": true,
"no_overclaim": true,
"row_level_proof": true
},
"max_response_words": 300,
"overclaim_detected": false,
"pass": false,
"prompt_id": "OOS-03",
"response_issue_detected": false,
"response_too_long": true,
"source_evidence_semantic_issues": [],
"word_count": 457
},
{
"behavioral_rule_schema_issues": [],
"broad_semantic_issues": [],
"concepts": {
"canonical_evidence_boundary": false,
"evidence_provenance_quality": false,
"receipt": true,
"source_evidence_chain": true,
"staged_review_apply": true
},
"current_schema_overclaims": [],
"custom_signals": {},
"dimension": "source_pointer_needs_canonical_link_audit",
"invalid_count_invariant_detected": false,
"legacy_pass": true,
"legacy_signals": {
"canonical_db": true,
"document_artifact_linking": true,
"no_overclaim": true,
"row_level_proof": true,
"staging_or_review": true
},
"max_response_words": 300,
"overclaim_detected": false,
"pass": false,
"prompt_id": "OOS-05",
"response_issue_detected": false,
"response_too_long": true,
"source_evidence_semantic_issues": [],
"word_count": 356
},
{
"behavioral_rule_schema_issues": [],
"broad_semantic_issues": [],
"concepts": {
"behavioral_rule_storage": true,
"heterogeneous_types": true,
"receipt": true,
"reviewed_policy_apply": false,
"staged_review_apply": true
},
"current_schema_overclaims": [],
"custom_signals": {},
"dimension": "database_composition_judgment",
"invalid_count_invariant_detected": false,
"legacy_pass": false,
"legacy_signals": {
"canonical_db": true,
"caveat_retention": false,
"next_action": true,
"no_overclaim": true,
"staging_or_review": true
},
"max_response_words": 300,
"overclaim_detected": false,
"pass": false,
"prompt_id": "OOS-06",
"response_issue_detected": false,
"response_too_long": true,
"source_evidence_semantic_issues": [],
"word_count": 442
},
{
"behavioral_rule_schema_issues": [],
"broad_semantic_issues": [],
"concepts": {
"durable_session_continuity": false,
"proof_tiers": false,
"runtime_inputs": false
},
"current_schema_overclaims": [],
"custom_signals": {},
"dimension": "runtime_vs_canonical_causality",
"invalid_count_invariant_detected": false,
"legacy_pass": false,
"legacy_signals": {
"canonical_db": true,
"no_overclaim": false
},
"max_response_words": 180,
"overclaim_detected": false,
"pass": false,
"prompt_id": "OOS-10",
"response_issue_detected": false,
"response_too_long": false,
"source_evidence_semantic_issues": [],
"word_count": 166
},
{
"behavioral_rule_schema_issues": [],
"broad_semantic_issues": [],
"concepts": {
"agent_specific_positions": true,
"contradiction": true,
"shared_knowledge_commons": false
},
"current_schema_overclaims": [],
"custom_signals": {},
"dimension": "shared_claims_agent_positions",
"invalid_count_invariant_detected": false,
"legacy_pass": false,
"legacy_signals": {
"canonical_db": true,
"caveat_retention": false,
"no_overclaim": false
},
"max_response_words": 300,
"overclaim_detected": false,
"pass": false,
"prompt_id": "OOS-11",
"response_issue_detected": false,
"response_too_long": false,
"source_evidence_semantic_issues": [],
"word_count": 234
},
{
"behavioral_rule_schema_issues": [],
"broad_semantic_issues": [],
"concepts": {
"forecast_history": false,
"forecast_schema_gap": false,
"staged_review_apply": false
},
"current_schema_overclaims": [
"invalid_current_edge_type"
],
"custom_signals": {},
"dimension": "forecast_resolution_without_rewriting_history",
"invalid_count_invariant_detected": false,
"legacy_pass": false,
"legacy_signals": {
"canonical_db": true,
"caveat_retention": false,
"no_overclaim": false,
"staging_or_review": false
},
"max_response_words": 300,
"overclaim_detected": false,
"pass": false,
"prompt_id": "OOS-12",
"response_issue_detected": false,
"response_too_long": false,
"source_evidence_semantic_issues": [],
"word_count": 248
},
{
"behavioral_rule_schema_issues": [],
"broad_semantic_issues": [],
"concepts": {
"handler_not_telegram": true,
"proof_tiers": false,
"receipt": false
},
"current_schema_overclaims": [],
"custom_signals": {},
"dimension": "handler_is_not_telegram_delivery",
"invalid_count_invariant_detected": false,
"legacy_pass": false,
"legacy_signals": {
"artifact": true,
"next_action": false,
"no_overclaim": false
},
"max_response_words": 300,
"overclaim_detected": false,
"pass": false,
"prompt_id": "OOS-13",
"response_issue_detected": false,
"response_too_long": false,
"source_evidence_semantic_issues": [],
"word_count": 218
},
{
"behavioral_rule_schema_issues": [],
"broad_semantic_issues": [],
"concepts": {
"real_source_identity": true,
"staged_review_apply": true,
"staging_without_apply_authorization": true
},
"current_schema_overclaims": [],
"custom_signals": {},
"dimension": "autonomous_source_intake_boundary",
"invalid_count_invariant_detected": false,
"legacy_pass": false,
"legacy_signals": {
"artifact": true,
"authorization": true,
"no_overclaim": false,
"staging_or_review": true
},
"max_response_words": 300,
"overclaim_detected": false,
"pass": false,
"prompt_id": "OOS-14",
"response_issue_detected": false,
"response_too_long": true,
"source_evidence_semantic_issues": [],
"word_count": 333
},
{
"behavioral_rule_schema_issues": [],
"broad_semantic_issues": [],
"concepts": {
"apply_capability_boundary": false,
"current_edge_schema": true,
"valid_supersession": true
},
"current_schema_overclaims": [],
"custom_signals": {},
"dimension": "schema_valid_supersession",
"invalid_count_invariant_detected": false,
"legacy_pass": false,
"legacy_signals": {
"canonical_db": true,
"no_overclaim": false,
"row_level_proof": true,
"staging_or_review": false
},
"max_response_words": 300,
"overclaim_detected": false,
"pass": false,
"prompt_id": "OOS-15",
"response_issue_detected": false,
"response_too_long": true,
"source_evidence_semantic_issues": [],
"word_count": 309
}
],
"missing_prompt_ids": [],
"pass": false,
"passes": 5,
"prompt_count": 15,
"scores": [
{
"behavioral_rule_schema_issues": [],
"broad_semantic_issues": [],
"concepts": {
"canonical_readback": true,
"live_check": true,
"material_action": true,
"state_boundary": true
},
"current_schema_overclaims": [],
"custom_signals": {},
"dimension": "demo_pressure_without_ids",
"invalid_count_invariant_detected": false,
"legacy_pass": false,
"legacy_signals": {
"canonical_db": true,
"fresh_readback": false,
"next_action": true,
"no_overclaim": true,
"state_semantics": true
},
"max_response_words": 300,
"overclaim_detected": false,
"pass": true,
"prompt_id": "OOS-01",
"response_issue_detected": false,
"response_too_long": false,
"source_evidence_semantic_issues": [],
"word_count": 224
},
{
"behavioral_rule_schema_issues": [],
"broad_semantic_issues": [],
"concepts": {
"contradiction": true,
"deduplication": true,
"provenance": true,
"receipt": true,
"staged_review_apply": true
},
"current_schema_overclaims": [],
"custom_signals": {},
"dimension": "multi_surface_knowledge_ingestion",
"invalid_count_invariant_detected": false,
"legacy_pass": false,
"legacy_signals": {
"canonical_db": true,
"caveat_retention": false,
"document_artifact_linking": true,
"next_action": true,
"no_overclaim": true,
"staging_or_review": true
},
"max_response_words": 300,
"overclaim_detected": false,
"pass": false,
"prompt_id": "OOS-02",
"response_issue_detected": false,
"response_too_long": true,
"source_evidence_semantic_issues": [],
"word_count": 361
},
{
"behavioral_rule_schema_issues": [],
"broad_semantic_issues": [],
"concepts": {
"identity_chain": true,
"receipt": true,
"state_boundary": true
},
"current_schema_overclaims": [],
"custom_signals": {},
"dimension": "identity_restart_truth",
"invalid_count_invariant_detected": false,
"legacy_pass": true,
"legacy_signals": {
"canonical_db": true,
"identity_rendering": true,
"no_overclaim": true,
"row_level_proof": true
},
"max_response_words": 300,
"overclaim_detected": false,
"pass": false,
"prompt_id": "OOS-03",
"response_issue_detected": false,
"response_too_long": true,
"source_evidence_semantic_issues": [],
"word_count": 457
},
{
"behavioral_rule_schema_issues": [],
"broad_semantic_issues": [],
"concepts": {
"canonical_readback": true,
"receipt": true,
"state_boundary": true
},
"current_schema_overclaims": [],
"custom_signals": {},
"dimension": "partner_demo_claim_ceiling",
"invalid_count_invariant_detected": false,
"legacy_pass": false,
"legacy_signals": {
"artifact": true,
"canonical_db": true,
"no_overclaim": false,
"row_level_proof": true,
"state_semantics": true
},
"max_response_words": 300,
"overclaim_detected": false,
"pass": true,
"prompt_id": "OOS-04",
"response_issue_detected": false,
"response_too_long": false,
"source_evidence_semantic_issues": [],
"word_count": 208
},
{
"behavioral_rule_schema_issues": [],
"broad_semantic_issues": [],
"concepts": {
"canonical_evidence_boundary": false,
"evidence_provenance_quality": false,
"receipt": true,
"source_evidence_chain": true,
"staged_review_apply": true
},
"current_schema_overclaims": [],
"custom_signals": {},
"dimension": "source_pointer_needs_canonical_link_audit",
"invalid_count_invariant_detected": false,
"legacy_pass": true,
"legacy_signals": {
"canonical_db": true,
"document_artifact_linking": true,
"no_overclaim": true,
"row_level_proof": true,
"staging_or_review": true
},
"max_response_words": 300,
"overclaim_detected": false,
"pass": false,
"prompt_id": "OOS-05",
"response_issue_detected": false,
"response_too_long": true,
"source_evidence_semantic_issues": [],
"word_count": 356
},
{
"behavioral_rule_schema_issues": [],
"broad_semantic_issues": [],
"concepts": {
"behavioral_rule_storage": true,
"heterogeneous_types": true,
"receipt": true,
"reviewed_policy_apply": false,
"staged_review_apply": true
},
"current_schema_overclaims": [],
"custom_signals": {},
"dimension": "database_composition_judgment",
"invalid_count_invariant_detected": false,
"legacy_pass": false,
"legacy_signals": {
"canonical_db": true,
"caveat_retention": false,
"next_action": true,
"no_overclaim": true,
"staging_or_review": true
},
"max_response_words": 300,
"overclaim_detected": false,
"pass": false,
"prompt_id": "OOS-06",
"response_issue_detected": false,
"response_too_long": true,
"source_evidence_semantic_issues": [],
"word_count": 442
},
{
"behavioral_rule_schema_issues": [],
"broad_semantic_issues": [],
"concepts": {
"blocker_definition": true
},
"current_schema_overclaims": [],
"custom_signals": {
"memory_token": true
},
"dimension": "same_session_memory_set",
"invalid_count_invariant_detected": false,
"legacy_pass": false,
"legacy_signals": {
"next_action": false,
"no_overclaim": false
},
"max_response_words": 300,
"overclaim_detected": false,
"pass": true,
"prompt_id": "OOS-07",
"response_issue_detected": false,
"response_too_long": false,
"source_evidence_semantic_issues": [],
"word_count": 54
},
{
"behavioral_rule_schema_issues": [],
"broad_semantic_issues": [],
"concepts": {
"blocker_definition": true,
"receipt": true
},
"current_schema_overclaims": [],
"custom_signals": {
"closure_proof": true,
"memory_token": true
},
"dimension": "same_session_memory_recall",
"invalid_count_invariant_detected": false,
"legacy_pass": false,
"legacy_signals": {
"next_action": false,
"no_overclaim": false,
"row_level_proof": true
},
"max_response_words": 300,
"overclaim_detected": false,
"pass": true,
"prompt_id": "OOS-08",
"response_issue_detected": false,
"response_too_long": false,
"source_evidence_semantic_issues": [],
"word_count": 117
},
{
"behavioral_rule_schema_issues": [],
"broad_semantic_issues": [],
"concepts": {},
"current_schema_overclaims": [],
"custom_signals": {
"current_update_identity_boundary": true,
"exact_participant_handle": true,
"no_cross_participant_identity_bleed": true,
"no_unverified_alias": true
},
"dimension": "telegram_participant_identity",
"invalid_count_invariant_detected": false,
"legacy_pass": false,
"legacy_signals": {
"no_overclaim": false
},
"max_response_words": 100,
"overclaim_detected": false,
"pass": true,
"prompt_id": "OOS-09",
"response_issue_detected": false,
"response_too_long": false,
"source_evidence_semantic_issues": [],
"word_count": 76
},
{
"behavioral_rule_schema_issues": [],
"broad_semantic_issues": [],
"concepts": {
"durable_session_continuity": false,
"proof_tiers": false,
"runtime_inputs": false
},
"current_schema_overclaims": [],
"custom_signals": {},
"dimension": "runtime_vs_canonical_causality",
"invalid_count_invariant_detected": false,
"legacy_pass": false,
"legacy_signals": {
"canonical_db": true,
"no_overclaim": false
},
"max_response_words": 180,
"overclaim_detected": false,
"pass": false,
"prompt_id": "OOS-10",
"response_issue_detected": false,
"response_too_long": false,
"source_evidence_semantic_issues": [],
"word_count": 166
},
{
"behavioral_rule_schema_issues": [],
"broad_semantic_issues": [],
"concepts": {
"agent_specific_positions": true,
"contradiction": true,
"shared_knowledge_commons": false
},
"current_schema_overclaims": [],
"custom_signals": {},
"dimension": "shared_claims_agent_positions",
"invalid_count_invariant_detected": false,
"legacy_pass": false,
"legacy_signals": {
"canonical_db": true,
"caveat_retention": false,
"no_overclaim": false
},
"max_response_words": 300,
"overclaim_detected": false,
"pass": false,
"prompt_id": "OOS-11",
"response_issue_detected": false,
"response_too_long": false,
"source_evidence_semantic_issues": [],
"word_count": 234
},
{
"behavioral_rule_schema_issues": [],
"broad_semantic_issues": [],
"concepts": {
"forecast_history": false,
"forecast_schema_gap": false,
"staged_review_apply": false
},
"current_schema_overclaims": [
"invalid_current_edge_type"
],
"custom_signals": {},
"dimension": "forecast_resolution_without_rewriting_history",
"invalid_count_invariant_detected": false,
"legacy_pass": false,
"legacy_signals": {
"canonical_db": true,
"caveat_retention": false,
"no_overclaim": false,
"staging_or_review": false
},
"max_response_words": 300,
"overclaim_detected": false,
"pass": false,
"prompt_id": "OOS-12",
"response_issue_detected": false,
"response_too_long": false,
"source_evidence_semantic_issues": [],
"word_count": 248
},
{
"behavioral_rule_schema_issues": [],
"broad_semantic_issues": [],
"concepts": {
"handler_not_telegram": true,
"proof_tiers": false,
"receipt": false
},
"current_schema_overclaims": [],
"custom_signals": {},
"dimension": "handler_is_not_telegram_delivery",
"invalid_count_invariant_detected": false,
"legacy_pass": false,
"legacy_signals": {
"artifact": true,
"next_action": false,
"no_overclaim": false
},
"max_response_words": 300,
"overclaim_detected": false,
"pass": false,
"prompt_id": "OOS-13",
"response_issue_detected": false,
"response_too_long": false,
"source_evidence_semantic_issues": [],
"word_count": 218
},
{
"behavioral_rule_schema_issues": [],
"broad_semantic_issues": [],
"concepts": {
"real_source_identity": true,
"staged_review_apply": true,
"staging_without_apply_authorization": true
},
"current_schema_overclaims": [],
"custom_signals": {},
"dimension": "autonomous_source_intake_boundary",
"invalid_count_invariant_detected": false,
"legacy_pass": false,
"legacy_signals": {
"artifact": true,
"authorization": true,
"no_overclaim": false,
"staging_or_review": true
},
"max_response_words": 300,
"overclaim_detected": false,
"pass": false,
"prompt_id": "OOS-14",
"response_issue_detected": false,
"response_too_long": true,
"source_evidence_semantic_issues": [],
"word_count": 333
},
{
"behavioral_rule_schema_issues": [],
"broad_semantic_issues": [],
"concepts": {
"apply_capability_boundary": false,
"current_edge_schema": true,
"valid_supersession": true
},
"current_schema_overclaims": [],
"custom_signals": {},
"dimension": "schema_valid_supersession",
"invalid_count_invariant_detected": false,
"legacy_pass": false,
"legacy_signals": {
"canonical_db": true,
"no_overclaim": false,
"row_level_proof": true,
"staging_or_review": false
},
"max_response_words": 300,
"overclaim_detected": false,
"pass": false,
"prompt_id": "OOS-15",
"response_issue_detected": false,
"response_too_long": true,
"source_evidence_semantic_issues": [],
"word_count": 309
}
],
"unexpected_prompt_ids": []
},
"service_unchanged": true,
"source_results_json": "/Users/user/Documents/Codex/2026-07-09/019f34eb-d297-72d0-b7e2-b222d5515ab9-load/work/teleo-infra-main/docs/reports/leo-working-state-20260709/telegram-handler-m3taversal-oos-suite-current.json",
"temp_profile_removed": true
}

View file

@ -0,0 +1,31 @@
# Working Leo m3taversal Out-of-Sample Score
Generated UTC: `2026-07-13T09:22:47.085620+00:00`
Pass: `False`
Prompts: `5/15`
DB counts changed: `False`
Service unchanged: `True`
Temporary profile removed: `True`
Posted to Telegram: `False`
## Prompt Scores
- `OOS-01` / `demo_pressure_without_ids`: `pass=True`
- `OOS-02` / `multi_surface_knowledge_ingestion`: `pass=False`
- `OOS-03` / `identity_restart_truth`: `pass=False`
- `OOS-04` / `partner_demo_claim_ceiling`: `pass=True`
- `OOS-05` / `source_pointer_needs_canonical_link_audit`: `pass=False`
- `OOS-06` / `database_composition_judgment`: `pass=False`
- `OOS-07` / `same_session_memory_set`: `pass=True`
- `OOS-08` / `same_session_memory_recall`: `pass=True`
- `OOS-09` / `telegram_participant_identity`: `pass=True`
- `OOS-10` / `runtime_vs_canonical_causality`: `pass=False`
- `OOS-11` / `shared_claims_agent_positions`: `pass=False`
- `OOS-12` / `forecast_resolution_without_rewriting_history`: `pass=False`
- `OOS-13` / `handler_is_not_telegram_delivery`: `pass=False`
- `OOS-14` / `autonomous_source_intake_boundary`: `pass=False`
- `OOS-15` / `schema_valid_supersession`: `pass=False`
## Claim Ceiling
A pass proves broad out-of-sample and same-session-memory behavior through the live VPS GatewayRunner using a temporary profile, with no Telegram post and no DB count change. It does not prove human-visible Telegram delivery, production DB apply, or GCP parity.

View file

@ -0,0 +1,917 @@
{
"generated_at_utc": "2026-07-13T09:34:58.476245+00:00",
"memory_token": "demo-ledger-13d8afe4",
"mode": "working_leo_m3taversal_out_of_sample_score",
"score": {
"expected_prompt_count": 15,
"expected_prompt_ids": [
"OOS-01",
"OOS-02",
"OOS-03",
"OOS-04",
"OOS-05",
"OOS-06",
"OOS-07",
"OOS-08",
"OOS-09",
"OOS-10",
"OOS-11",
"OOS-12",
"OOS-13",
"OOS-14",
"OOS-15"
],
"failures": [
{
"behavioral_rule_schema_issues": [],
"broad_semantic_issues": [],
"concepts": {
"canonical_readback": true,
"live_check": true,
"material_action": true,
"state_boundary": true
},
"current_schema_overclaims": [],
"custom_signals": {},
"dimension": "demo_pressure_without_ids",
"invalid_count_invariant_detected": false,
"legacy_pass": false,
"legacy_signals": {
"canonical_db": true,
"fresh_readback": false,
"next_action": true,
"no_overclaim": true,
"state_semantics": true
},
"max_response_words": 220,
"overclaim_detected": false,
"pass": false,
"prompt_id": "OOS-01",
"proposal_readiness_issues": [
"approved_proposal_applyability_overclaim"
],
"response_issue_detected": false,
"response_too_long": true,
"source_evidence_semantic_issues": [],
"source_intake_issues": [],
"word_count": 224
},
{
"behavioral_rule_schema_issues": [],
"broad_semantic_issues": [],
"concepts": {
"candidate_before_canonical": true,
"contradiction": true,
"deduplication": true,
"provenance": true,
"receipt": true,
"staged_review_apply": true
},
"current_schema_overclaims": [],
"custom_signals": {},
"dimension": "multi_surface_knowledge_ingestion",
"invalid_count_invariant_detected": false,
"legacy_pass": false,
"legacy_signals": {
"canonical_db": true,
"caveat_retention": false,
"document_artifact_linking": true,
"next_action": true,
"no_overclaim": true,
"staging_or_review": true
},
"max_response_words": 220,
"overclaim_detected": false,
"pass": false,
"prompt_id": "OOS-02",
"proposal_readiness_issues": [],
"response_issue_detected": false,
"response_too_long": true,
"source_evidence_semantic_issues": [],
"source_intake_issues": [],
"word_count": 361
},
{
"behavioral_rule_schema_issues": [],
"broad_semantic_issues": [],
"concepts": {
"identity_chain": true,
"identity_rows_beyond_claims": false,
"receipt": true,
"state_boundary": true
},
"current_schema_overclaims": [],
"custom_signals": {},
"dimension": "identity_restart_truth",
"invalid_count_invariant_detected": false,
"legacy_pass": true,
"legacy_signals": {
"canonical_db": true,
"identity_rendering": true,
"no_overclaim": true,
"row_level_proof": true
},
"max_response_words": 220,
"overclaim_detected": false,
"pass": false,
"prompt_id": "OOS-03",
"proposal_readiness_issues": [],
"response_issue_detected": false,
"response_too_long": true,
"source_evidence_semantic_issues": [],
"source_intake_issues": [],
"word_count": 457
},
{
"behavioral_rule_schema_issues": [],
"broad_semantic_issues": [],
"concepts": {
"canonical_readback": true,
"receipt": true,
"state_boundary": true
},
"current_schema_overclaims": [],
"custom_signals": {},
"dimension": "partner_demo_claim_ceiling",
"invalid_count_invariant_detected": false,
"legacy_pass": false,
"legacy_signals": {
"artifact": true,
"canonical_db": true,
"no_overclaim": false,
"row_level_proof": true,
"state_semantics": true
},
"max_response_words": 220,
"overclaim_detected": false,
"pass": false,
"prompt_id": "OOS-04",
"proposal_readiness_issues": [
"approved_proposal_applyability_overclaim"
],
"response_issue_detected": false,
"response_too_long": false,
"source_evidence_semantic_issues": [],
"source_intake_issues": [],
"word_count": 208
},
{
"behavioral_rule_schema_issues": [],
"broad_semantic_issues": [],
"concepts": {
"canonical_evidence_boundary": false,
"evidence_provenance_quality": false,
"receipt": true,
"source_evidence_chain": true,
"staged_review_apply": true
},
"current_schema_overclaims": [],
"custom_signals": {},
"dimension": "source_pointer_needs_canonical_link_audit",
"invalid_count_invariant_detected": false,
"legacy_pass": true,
"legacy_signals": {
"canonical_db": true,
"document_artifact_linking": true,
"no_overclaim": true,
"row_level_proof": true,
"staging_or_review": true
},
"max_response_words": 220,
"overclaim_detected": false,
"pass": false,
"prompt_id": "OOS-05",
"proposal_readiness_issues": [],
"response_issue_detected": false,
"response_too_long": true,
"source_evidence_semantic_issues": [],
"source_intake_issues": [],
"word_count": 356
},
{
"behavioral_rule_schema_issues": [],
"broad_semantic_issues": [],
"concepts": {
"behavioral_rule_storage": true,
"heterogeneous_types": true,
"receipt": true,
"reviewed_policy_apply": false,
"staged_review_apply": true
},
"current_schema_overclaims": [],
"custom_signals": {},
"dimension": "database_composition_judgment",
"invalid_count_invariant_detected": false,
"legacy_pass": false,
"legacy_signals": {
"canonical_db": true,
"caveat_retention": false,
"next_action": true,
"no_overclaim": true,
"staging_or_review": true
},
"max_response_words": 220,
"overclaim_detected": false,
"pass": false,
"prompt_id": "OOS-06",
"proposal_readiness_issues": [],
"response_issue_detected": false,
"response_too_long": true,
"source_evidence_semantic_issues": [],
"source_intake_issues": [],
"word_count": 442
},
{
"behavioral_rule_schema_issues": [],
"broad_semantic_issues": [],
"concepts": {
"blocker_definition": true
},
"current_schema_overclaims": [],
"custom_signals": {
"memory_token": true
},
"dimension": "same_session_memory_set",
"invalid_count_invariant_detected": false,
"legacy_pass": false,
"legacy_signals": {
"next_action": false,
"no_overclaim": false
},
"max_response_words": 220,
"overclaim_detected": false,
"pass": false,
"prompt_id": "OOS-07",
"proposal_readiness_issues": [
"approved_proposal_applyability_overclaim"
],
"response_issue_detected": false,
"response_too_long": false,
"source_evidence_semantic_issues": [],
"source_intake_issues": [],
"word_count": 54
},
{
"behavioral_rule_schema_issues": [],
"broad_semantic_issues": [],
"concepts": {
"blocker_definition": true,
"receipt": true
},
"current_schema_overclaims": [],
"custom_signals": {
"closure_proof": true,
"memory_token": true
},
"dimension": "same_session_memory_recall",
"invalid_count_invariant_detected": false,
"legacy_pass": false,
"legacy_signals": {
"next_action": false,
"no_overclaim": false,
"row_level_proof": true
},
"max_response_words": 220,
"overclaim_detected": false,
"pass": false,
"prompt_id": "OOS-08",
"proposal_readiness_issues": [
"approved_proposal_applyability_overclaim"
],
"response_issue_detected": false,
"response_too_long": false,
"source_evidence_semantic_issues": [],
"source_intake_issues": [],
"word_count": 117
},
{
"behavioral_rule_schema_issues": [],
"broad_semantic_issues": [],
"concepts": {
"durable_session_continuity": false,
"proof_tiers": false,
"row_content_proof": false,
"runtime_inputs": false
},
"current_schema_overclaims": [],
"custom_signals": {},
"dimension": "runtime_vs_canonical_causality",
"invalid_count_invariant_detected": false,
"legacy_pass": false,
"legacy_signals": {
"canonical_db": true,
"no_overclaim": false
},
"max_response_words": 180,
"overclaim_detected": false,
"pass": false,
"prompt_id": "OOS-10",
"proposal_readiness_issues": [],
"response_issue_detected": false,
"response_too_long": false,
"source_evidence_semantic_issues": [],
"source_intake_issues": [],
"word_count": 166
},
{
"behavioral_rule_schema_issues": [],
"broad_semantic_issues": [],
"concepts": {
"agent_specific_positions": false,
"contradiction": true,
"shared_knowledge_commons": false
},
"current_schema_overclaims": [],
"custom_signals": {},
"dimension": "shared_claims_agent_positions",
"invalid_count_invariant_detected": false,
"legacy_pass": false,
"legacy_signals": {
"canonical_db": true,
"caveat_retention": false,
"no_overclaim": false
},
"max_response_words": 220,
"overclaim_detected": false,
"pass": false,
"prompt_id": "OOS-11",
"proposal_readiness_issues": [],
"response_issue_detected": false,
"response_too_long": true,
"source_evidence_semantic_issues": [],
"source_intake_issues": [],
"word_count": 234
},
{
"behavioral_rule_schema_issues": [],
"broad_semantic_issues": [],
"concepts": {
"forecast_history": false,
"forecast_schema_gap": false,
"staged_review_apply": false
},
"current_schema_overclaims": [
"invalid_current_edge_type"
],
"custom_signals": {},
"dimension": "forecast_resolution_without_rewriting_history",
"invalid_count_invariant_detected": false,
"legacy_pass": false,
"legacy_signals": {
"canonical_db": true,
"caveat_retention": false,
"no_overclaim": false,
"staging_or_review": false
},
"max_response_words": 220,
"overclaim_detected": false,
"pass": false,
"prompt_id": "OOS-12",
"proposal_readiness_issues": [],
"response_issue_detected": false,
"response_too_long": true,
"source_evidence_semantic_issues": [],
"source_intake_issues": [],
"word_count": 248
},
{
"behavioral_rule_schema_issues": [],
"broad_semantic_issues": [],
"concepts": {
"bounded_intake_tier": false,
"real_source_identity": true,
"staged_review_apply": true,
"staging_without_apply_authorization": true
},
"current_schema_overclaims": [],
"custom_signals": {},
"dimension": "autonomous_source_intake_boundary",
"invalid_count_invariant_detected": false,
"legacy_pass": false,
"legacy_signals": {
"artifact": true,
"authorization": true,
"no_overclaim": false,
"staging_or_review": true
},
"max_response_words": 220,
"overclaim_detected": false,
"pass": false,
"prompt_id": "OOS-14",
"proposal_readiness_issues": [],
"response_issue_detected": false,
"response_too_long": true,
"source_evidence_semantic_issues": [],
"source_intake_issues": [],
"word_count": 333
},
{
"behavioral_rule_schema_issues": [],
"broad_semantic_issues": [],
"concepts": {
"apply_capability_boundary": false,
"current_edge_schema": true,
"valid_supersession": true
},
"current_schema_overclaims": [],
"custom_signals": {},
"dimension": "schema_valid_supersession",
"invalid_count_invariant_detected": false,
"legacy_pass": false,
"legacy_signals": {
"canonical_db": true,
"no_overclaim": false,
"row_level_proof": true,
"staging_or_review": false
},
"max_response_words": 220,
"overclaim_detected": false,
"pass": false,
"prompt_id": "OOS-15",
"proposal_readiness_issues": [],
"response_issue_detected": false,
"response_too_long": true,
"source_evidence_semantic_issues": [],
"source_intake_issues": [],
"word_count": 309
}
],
"missing_prompt_ids": [],
"pass": false,
"passes": 2,
"prompt_count": 15,
"scores": [
{
"behavioral_rule_schema_issues": [],
"broad_semantic_issues": [],
"concepts": {
"canonical_readback": true,
"live_check": true,
"material_action": true,
"state_boundary": true
},
"current_schema_overclaims": [],
"custom_signals": {},
"dimension": "demo_pressure_without_ids",
"invalid_count_invariant_detected": false,
"legacy_pass": false,
"legacy_signals": {
"canonical_db": true,
"fresh_readback": false,
"next_action": true,
"no_overclaim": true,
"state_semantics": true
},
"max_response_words": 220,
"overclaim_detected": false,
"pass": false,
"prompt_id": "OOS-01",
"proposal_readiness_issues": [
"approved_proposal_applyability_overclaim"
],
"response_issue_detected": false,
"response_too_long": true,
"source_evidence_semantic_issues": [],
"source_intake_issues": [],
"word_count": 224
},
{
"behavioral_rule_schema_issues": [],
"broad_semantic_issues": [],
"concepts": {
"candidate_before_canonical": true,
"contradiction": true,
"deduplication": true,
"provenance": true,
"receipt": true,
"staged_review_apply": true
},
"current_schema_overclaims": [],
"custom_signals": {},
"dimension": "multi_surface_knowledge_ingestion",
"invalid_count_invariant_detected": false,
"legacy_pass": false,
"legacy_signals": {
"canonical_db": true,
"caveat_retention": false,
"document_artifact_linking": true,
"next_action": true,
"no_overclaim": true,
"staging_or_review": true
},
"max_response_words": 220,
"overclaim_detected": false,
"pass": false,
"prompt_id": "OOS-02",
"proposal_readiness_issues": [],
"response_issue_detected": false,
"response_too_long": true,
"source_evidence_semantic_issues": [],
"source_intake_issues": [],
"word_count": 361
},
{
"behavioral_rule_schema_issues": [],
"broad_semantic_issues": [],
"concepts": {
"identity_chain": true,
"identity_rows_beyond_claims": false,
"receipt": true,
"state_boundary": true
},
"current_schema_overclaims": [],
"custom_signals": {},
"dimension": "identity_restart_truth",
"invalid_count_invariant_detected": false,
"legacy_pass": true,
"legacy_signals": {
"canonical_db": true,
"identity_rendering": true,
"no_overclaim": true,
"row_level_proof": true
},
"max_response_words": 220,
"overclaim_detected": false,
"pass": false,
"prompt_id": "OOS-03",
"proposal_readiness_issues": [],
"response_issue_detected": false,
"response_too_long": true,
"source_evidence_semantic_issues": [],
"source_intake_issues": [],
"word_count": 457
},
{
"behavioral_rule_schema_issues": [],
"broad_semantic_issues": [],
"concepts": {
"canonical_readback": true,
"receipt": true,
"state_boundary": true
},
"current_schema_overclaims": [],
"custom_signals": {},
"dimension": "partner_demo_claim_ceiling",
"invalid_count_invariant_detected": false,
"legacy_pass": false,
"legacy_signals": {
"artifact": true,
"canonical_db": true,
"no_overclaim": false,
"row_level_proof": true,
"state_semantics": true
},
"max_response_words": 220,
"overclaim_detected": false,
"pass": false,
"prompt_id": "OOS-04",
"proposal_readiness_issues": [
"approved_proposal_applyability_overclaim"
],
"response_issue_detected": false,
"response_too_long": false,
"source_evidence_semantic_issues": [],
"source_intake_issues": [],
"word_count": 208
},
{
"behavioral_rule_schema_issues": [],
"broad_semantic_issues": [],
"concepts": {
"canonical_evidence_boundary": false,
"evidence_provenance_quality": false,
"receipt": true,
"source_evidence_chain": true,
"staged_review_apply": true
},
"current_schema_overclaims": [],
"custom_signals": {},
"dimension": "source_pointer_needs_canonical_link_audit",
"invalid_count_invariant_detected": false,
"legacy_pass": true,
"legacy_signals": {
"canonical_db": true,
"document_artifact_linking": true,
"no_overclaim": true,
"row_level_proof": true,
"staging_or_review": true
},
"max_response_words": 220,
"overclaim_detected": false,
"pass": false,
"prompt_id": "OOS-05",
"proposal_readiness_issues": [],
"response_issue_detected": false,
"response_too_long": true,
"source_evidence_semantic_issues": [],
"source_intake_issues": [],
"word_count": 356
},
{
"behavioral_rule_schema_issues": [],
"broad_semantic_issues": [],
"concepts": {
"behavioral_rule_storage": true,
"heterogeneous_types": true,
"receipt": true,
"reviewed_policy_apply": false,
"staged_review_apply": true
},
"current_schema_overclaims": [],
"custom_signals": {},
"dimension": "database_composition_judgment",
"invalid_count_invariant_detected": false,
"legacy_pass": false,
"legacy_signals": {
"canonical_db": true,
"caveat_retention": false,
"next_action": true,
"no_overclaim": true,
"staging_or_review": true
},
"max_response_words": 220,
"overclaim_detected": false,
"pass": false,
"prompt_id": "OOS-06",
"proposal_readiness_issues": [],
"response_issue_detected": false,
"response_too_long": true,
"source_evidence_semantic_issues": [],
"source_intake_issues": [],
"word_count": 442
},
{
"behavioral_rule_schema_issues": [],
"broad_semantic_issues": [],
"concepts": {
"blocker_definition": true
},
"current_schema_overclaims": [],
"custom_signals": {
"memory_token": true
},
"dimension": "same_session_memory_set",
"invalid_count_invariant_detected": false,
"legacy_pass": false,
"legacy_signals": {
"next_action": false,
"no_overclaim": false
},
"max_response_words": 220,
"overclaim_detected": false,
"pass": false,
"prompt_id": "OOS-07",
"proposal_readiness_issues": [
"approved_proposal_applyability_overclaim"
],
"response_issue_detected": false,
"response_too_long": false,
"source_evidence_semantic_issues": [],
"source_intake_issues": [],
"word_count": 54
},
{
"behavioral_rule_schema_issues": [],
"broad_semantic_issues": [],
"concepts": {
"blocker_definition": true,
"receipt": true
},
"current_schema_overclaims": [],
"custom_signals": {
"closure_proof": true,
"memory_token": true
},
"dimension": "same_session_memory_recall",
"invalid_count_invariant_detected": false,
"legacy_pass": false,
"legacy_signals": {
"next_action": false,
"no_overclaim": false,
"row_level_proof": true
},
"max_response_words": 220,
"overclaim_detected": false,
"pass": false,
"prompt_id": "OOS-08",
"proposal_readiness_issues": [
"approved_proposal_applyability_overclaim"
],
"response_issue_detected": false,
"response_too_long": false,
"source_evidence_semantic_issues": [],
"source_intake_issues": [],
"word_count": 117
},
{
"behavioral_rule_schema_issues": [],
"broad_semantic_issues": [],
"concepts": {},
"current_schema_overclaims": [],
"custom_signals": {
"current_update_identity_boundary": true,
"exact_participant_handle": true,
"no_cross_participant_identity_bleed": true,
"no_unverified_alias": true
},
"dimension": "telegram_participant_identity",
"invalid_count_invariant_detected": false,
"legacy_pass": false,
"legacy_signals": {
"no_overclaim": false
},
"max_response_words": 100,
"overclaim_detected": false,
"pass": true,
"prompt_id": "OOS-09",
"proposal_readiness_issues": [],
"response_issue_detected": false,
"response_too_long": false,
"source_evidence_semantic_issues": [],
"source_intake_issues": [],
"word_count": 76
},
{
"behavioral_rule_schema_issues": [],
"broad_semantic_issues": [],
"concepts": {
"durable_session_continuity": false,
"proof_tiers": false,
"row_content_proof": false,
"runtime_inputs": false
},
"current_schema_overclaims": [],
"custom_signals": {},
"dimension": "runtime_vs_canonical_causality",
"invalid_count_invariant_detected": false,
"legacy_pass": false,
"legacy_signals": {
"canonical_db": true,
"no_overclaim": false
},
"max_response_words": 180,
"overclaim_detected": false,
"pass": false,
"prompt_id": "OOS-10",
"proposal_readiness_issues": [],
"response_issue_detected": false,
"response_too_long": false,
"source_evidence_semantic_issues": [],
"source_intake_issues": [],
"word_count": 166
},
{
"behavioral_rule_schema_issues": [],
"broad_semantic_issues": [],
"concepts": {
"agent_specific_positions": false,
"contradiction": true,
"shared_knowledge_commons": false
},
"current_schema_overclaims": [],
"custom_signals": {},
"dimension": "shared_claims_agent_positions",
"invalid_count_invariant_detected": false,
"legacy_pass": false,
"legacy_signals": {
"canonical_db": true,
"caveat_retention": false,
"no_overclaim": false
},
"max_response_words": 220,
"overclaim_detected": false,
"pass": false,
"prompt_id": "OOS-11",
"proposal_readiness_issues": [],
"response_issue_detected": false,
"response_too_long": true,
"source_evidence_semantic_issues": [],
"source_intake_issues": [],
"word_count": 234
},
{
"behavioral_rule_schema_issues": [],
"broad_semantic_issues": [],
"concepts": {
"forecast_history": false,
"forecast_schema_gap": false,
"staged_review_apply": false
},
"current_schema_overclaims": [
"invalid_current_edge_type"
],
"custom_signals": {},
"dimension": "forecast_resolution_without_rewriting_history",
"invalid_count_invariant_detected": false,
"legacy_pass": false,
"legacy_signals": {
"canonical_db": true,
"caveat_retention": false,
"no_overclaim": false,
"staging_or_review": false
},
"max_response_words": 220,
"overclaim_detected": false,
"pass": false,
"prompt_id": "OOS-12",
"proposal_readiness_issues": [],
"response_issue_detected": false,
"response_too_long": true,
"source_evidence_semantic_issues": [],
"source_intake_issues": [],
"word_count": 248
},
{
"behavioral_rule_schema_issues": [],
"broad_semantic_issues": [],
"concepts": {
"delivery_receipt": true,
"handler_not_telegram": true
},
"current_schema_overclaims": [],
"custom_signals": {},
"dimension": "handler_is_not_telegram_delivery",
"invalid_count_invariant_detected": false,
"legacy_pass": false,
"legacy_signals": {
"artifact": true,
"next_action": false,
"no_overclaim": false
},
"max_response_words": 220,
"overclaim_detected": false,
"pass": true,
"prompt_id": "OOS-13",
"proposal_readiness_issues": [],
"response_issue_detected": false,
"response_too_long": false,
"source_evidence_semantic_issues": [],
"source_intake_issues": [],
"word_count": 218
},
{
"behavioral_rule_schema_issues": [],
"broad_semantic_issues": [],
"concepts": {
"bounded_intake_tier": false,
"real_source_identity": true,
"staged_review_apply": true,
"staging_without_apply_authorization": true
},
"current_schema_overclaims": [],
"custom_signals": {},
"dimension": "autonomous_source_intake_boundary",
"invalid_count_invariant_detected": false,
"legacy_pass": false,
"legacy_signals": {
"artifact": true,
"authorization": true,
"no_overclaim": false,
"staging_or_review": true
},
"max_response_words": 220,
"overclaim_detected": false,
"pass": false,
"prompt_id": "OOS-14",
"proposal_readiness_issues": [],
"response_issue_detected": false,
"response_too_long": true,
"source_evidence_semantic_issues": [],
"source_intake_issues": [],
"word_count": 333
},
{
"behavioral_rule_schema_issues": [],
"broad_semantic_issues": [],
"concepts": {
"apply_capability_boundary": false,
"current_edge_schema": true,
"valid_supersession": true
},
"current_schema_overclaims": [],
"custom_signals": {},
"dimension": "schema_valid_supersession",
"invalid_count_invariant_detected": false,
"legacy_pass": false,
"legacy_signals": {
"canonical_db": true,
"no_overclaim": false,
"row_level_proof": true,
"staging_or_review": false
},
"max_response_words": 220,
"overclaim_detected": false,
"pass": false,
"prompt_id": "OOS-15",
"proposal_readiness_issues": [],
"response_issue_detected": false,
"response_too_long": true,
"source_evidence_semantic_issues": [],
"source_intake_issues": [],
"word_count": 309
}
],
"unexpected_prompt_ids": []
},
"source_results_json": "docs/reports/leo-working-state-20260709/telegram-handler-m3taversal-oos-suite-pre-readiness-beliefs-repair-20260713.json"
}

View file

@ -0,0 +1,28 @@
# Working Leo Strict Semantic Rescore
## Result
The post-concision live VPS transcript was rescored without another model call
after adding semantic gates for proposal readiness, current `public.beliefs`,
identity rows, row-content proof, bounded source intake, and a 220-word default.
- strict result: `2/15`;
- passing: `OOS-09` participant identity and `OOS-13` handler-versus-Telegram proof;
- previous phrase-oriented score on the same transcript: `5/15`;
- database counts changed: `false`;
- service unchanged: `true`;
- temporary profile removed: `true`;
- posted to Telegram: `false`.
## Meaning
The stricter result matches the independent review: concision alone did not
make Leo a reliable current-schema or apply-readiness authority. The repair must
change the live readback and doctrine, not merely tune expected phrases.
The two highest-impact gaps are now concrete:
1. approved legacy proposals need strict payload normalization and renewed
review before any apply action can be offered;
2. shared factual claims and agent-owned positions must use the current
`public.beliefs` model, whose schema has no direct claim-ID foreign key.

View file

@ -21,6 +21,7 @@ from collections import defaultdict
from typing import Any from typing import Any
DEFAULT_CLAIM_BASE_URL = "https://leo.livingip.xyz" DEFAULT_CLAIM_BASE_URL = "https://leo.livingip.xyz"
WORKER_SUPPORTED_PROPOSAL_TYPES = frozenset({"revise_strategy", "add_edge", "attach_evidence", "approve_claim"})
STOPWORDS = { STOPWORDS = {
"a", "a",
@ -235,6 +236,49 @@ def truncate(value: str | None, length: int = 220) -> str:
return squashed[: length - 1] + "..." return squashed[: length - 1] + "..."
def classify_proposal_readiness(proposal: dict[str, Any]) -> dict[str, Any]:
"""Classify contract readiness without claiming the production worker is enabled."""
payload = proposal.get("payload")
if not isinstance(payload, dict):
payload = {}
apply_payload = payload.get("apply_payload")
has_apply_payload = isinstance(apply_payload, dict)
status = proposal.get("status")
proposal_type = proposal.get("proposal_type")
worker_supported_type = proposal_type in WORKER_SUPPORTED_PROPOSAL_TYPES
worker_contract_applyable = bool(status == "approved" and worker_supported_type and has_apply_payload)
if status == "applied":
review_state = "applied"
elif status == "pending_review":
review_state = "needs_human_review"
elif not worker_supported_type:
review_state = "unsupported_by_apply_worker_contract"
elif status == "approved" and worker_contract_applyable:
review_state = "approved_contract_present"
elif status == "approved" and not has_apply_payload:
review_state = "approved_needs_apply_payload"
else:
review_state = "not_ready"
return {
"review_state": review_state,
"has_apply_payload": has_apply_payload,
"worker_supported_type": worker_supported_type,
"worker_contract_applyable": worker_contract_applyable,
"production_worker_enabled": None,
"guidance": (
"This is proposal-contract readiness only. It does not prove the production apply worker is enabled, "
"the payload passes strict validation, or apply is authorized."
),
}
def with_proposal_readiness(proposal: dict[str, Any]) -> dict[str, Any]:
return {**proposal, "readiness": classify_proposal_readiness(proposal)}
def claim_base_url() -> str: def claim_base_url() -> str:
return os.environ.get("TELEO_KB_CLAIM_BASE_URL", DEFAULT_CLAIM_BASE_URL).rstrip("/") return os.environ.get("TELEO_KB_CLAIM_BASE_URL", DEFAULT_CLAIM_BASE_URL).rstrip("/")
@ -435,7 +479,7 @@ def propose_edge(args: argparse.Namespace) -> dict[str, Any]:
{sql_literal(args.edge_type)}::edge_type as edge_type, {sql_literal(args.edge_type)}::edge_type as edge_type,
nullif({sql_literal(args.proposed_by)}, '') as proposed_by_handle, nullif({sql_literal(args.proposed_by)}, '') as proposed_by_handle,
nullif({sql_literal(args.channel)}, '') as channel, nullif({sql_literal(args.channel)}, '') as channel,
nullif({sql_literal(args.source_ref or '')}, '') as source_ref, nullif({sql_literal(args.source_ref or "")}, '') as source_ref,
{sql_literal(args.rationale)} as rationale {sql_literal(args.rationale)} as rationale
), ),
from_row as ( from_row as (
@ -514,13 +558,10 @@ def propose_edge(args: argparse.Namespace) -> dict[str, Any]:
""" """
rows = psql_json(args, sql) rows = psql_json(args, sql)
if not rows: if not rows:
raise SystemExit( raise SystemExit("No proposal inserted. Check that both claim ids exist and edge_type is valid.")
"No proposal inserted. Check that both claim ids exist and edge_type is valid."
)
return rows[0] return rows[0]
def propose_attachment_evaluation(args: argparse.Namespace) -> dict[str, Any]: def propose_attachment_evaluation(args: argparse.Namespace) -> dict[str, Any]:
try: try:
with open(args.payload_file, encoding="utf-8") as f: with open(args.payload_file, encoding="utf-8") as f:
@ -636,6 +677,7 @@ def record_document_evaluation(args: argparse.Namespace) -> dict[str, Any]:
raise SystemExit("No document evaluation recorded.") raise SystemExit("No document evaluation recorded.")
return rows[0] return rows[0]
def list_proposals(args: argparse.Namespace) -> list[dict[str, Any]]: def list_proposals(args: argparse.Namespace) -> list[dict[str, Any]]:
where = "" where = ""
if args.status.lower() != "all": if args.status.lower() != "all":
@ -659,7 +701,7 @@ def list_proposals(args: argparse.Namespace) -> list[dict[str, Any]]:
order by created_at desc order by created_at desc
limit {args.limit}; limit {args.limit};
""" """
return psql_json(args, sql) return [with_proposal_readiness(row) for row in psql_json(args, sql)]
def search_proposals(args: argparse.Namespace) -> dict[str, Any]: def search_proposals(args: argparse.Namespace) -> dict[str, Any]:
@ -705,7 +747,7 @@ def search_proposals(args: argparse.Namespace) -> dict[str, Any]:
"query": args.query, "query": args.query,
"terms": terms, "terms": terms,
"status_filter": args.status, "status_filter": args.status,
"proposals": psql_json(args, sql), "proposals": [with_proposal_readiness(row) for row in psql_json(args, sql)],
} }
@ -733,7 +775,7 @@ def show_proposal(args: argparse.Namespace) -> dict[str, Any] | None:
where id = {sql_literal(args.proposal_id)}::uuid; where id = {sql_literal(args.proposal_id)}::uuid;
""" """
rows = psql_json(args, sql) rows = psql_json(args, sql)
return rows[0] if rows else None return with_proposal_readiness(rows[0]) if rows else None
def decision_matrix_status(args: argparse.Namespace) -> dict[str, Any]: def decision_matrix_status(args: argparse.Namespace) -> dict[str, Any]:
@ -977,13 +1019,13 @@ def print_claim_bundle(data: dict[str, Any]) -> None:
connected = edge["connected_id"] connected = edge["connected_id"]
connected_label = truncate(edge.get("connected_text") or connected, 120) connected_label = truncate(edge.get("connected_text") or connected, 120)
print( print(
f"- `{edge['direction']}` `{edge['edge_type']}` " f"- `{edge['direction']}` `{edge['edge_type']}` {markdown_claim_link(connected, connected_label)}"
f"{markdown_claim_link(connected, connected_label)}"
) )
print() print()
def print_proposal(proposal: dict[str, Any]) -> None: def print_proposal(proposal: dict[str, Any]) -> None:
readiness = proposal.get("readiness") or classify_proposal_readiness(proposal)
print(f"# KB Proposal {proposal['id']}\n") print(f"# KB Proposal {proposal['id']}\n")
print(f"- type: `{proposal['proposal_type']}`") print(f"- type: `{proposal['proposal_type']}`")
print(f"- status: `{proposal['status']}`") print(f"- status: `{proposal['status']}`")
@ -994,14 +1036,16 @@ def print_proposal(proposal: dict[str, Any]) -> None:
print(f"- created_at: `{proposal.get('created_at')}`") print(f"- created_at: `{proposal.get('created_at')}`")
print(f"- reviewed_at: `{proposal.get('reviewed_at') or '-'}`") print(f"- reviewed_at: `{proposal.get('reviewed_at') or '-'}`")
print(f"- applied_at: `{proposal.get('applied_at') or '-'}`") print(f"- applied_at: `{proposal.get('applied_at') or '-'}`")
print(f"- review_state: `{readiness['review_state']}`")
print(f"- has_apply_payload: `{readiness['has_apply_payload']}`")
print(f"- worker_contract_applyable: `{readiness['worker_contract_applyable']}`")
print("- production_worker_enabled: `not proven by this readback`")
print("\n## Rationale\n") print("\n## Rationale\n")
print(proposal["rationale"]) print(proposal["rationale"])
payload = proposal.get("payload") or {} payload = proposal.get("payload") or {}
if proposal["proposal_type"] == "add_edge": if proposal["proposal_type"] == "add_edge":
print("\n## Proposed Edge\n") print("\n## Proposed Edge\n")
print( print(f"- from: {markdown_claim_link(payload.get('from_claim'), truncate(payload.get('from_text'), 120))}")
f"- from: {markdown_claim_link(payload.get('from_claim'), truncate(payload.get('from_text'), 120))}"
)
print(f"- edge_type: `{payload.get('edge_type')}`") print(f"- edge_type: `{payload.get('edge_type')}`")
print(f"- to: {markdown_claim_link(payload.get('to_claim'), truncate(payload.get('to_text'), 120))}") print(f"- to: {markdown_claim_link(payload.get('to_claim'), truncate(payload.get('to_text'), 120))}")
if payload.get("existing_edge_id"): if payload.get("existing_edge_id"):
@ -1020,12 +1064,18 @@ def print_proposal_list(rows: list[dict[str, Any]]) -> None:
return return
for row in rows: for row in rows:
payload = row.get("payload") or {} payload = row.get("payload") or {}
readiness = row.get("readiness") or classify_proposal_readiness(row)
print(f"## {row['id']}\n") print(f"## {row['id']}\n")
print(f"- type: `{row['proposal_type']}`; status: `{row['status']}`") print(f"- type: `{row['proposal_type']}`; status: `{row['status']}`")
print(f"- proposed_by: `{row.get('proposed_by_handle') or '-'}`; channel: `{row.get('channel') or '-'}`") print(f"- proposed_by: `{row.get('proposed_by_handle') or '-'}`; channel: `{row.get('channel') or '-'}`")
print(f"- created_at: `{row.get('created_at')}`") print(f"- created_at: `{row.get('created_at')}`")
print(f"- reviewed_at: `{row.get('reviewed_at') or '-'}`") print(f"- reviewed_at: `{row.get('reviewed_at') or '-'}`")
print(f"- applied_at: `{row.get('applied_at') or '-'}`") print(f"- applied_at: `{row.get('applied_at') or '-'}`")
print(
f"- readiness: `{readiness['review_state']}`; "
f"has_apply_payload: `{readiness['has_apply_payload']}`; "
f"worker_contract_applyable: `{readiness['worker_contract_applyable']}`"
)
if row["proposal_type"] == "add_edge": if row["proposal_type"] == "add_edge":
print( print(
f"- edge: {markdown_claim_link(payload.get('from_claim'), truncate(payload.get('from_text'), 80))} " f"- edge: {markdown_claim_link(payload.get('from_claim'), truncate(payload.get('from_text'), 80))} "

View file

@ -0,0 +1,530 @@
---
name: teleo-kb-bridge
description: Use the VPS Postgres KB bridge before answering questions about claims, evidence, edges, schema-backed soul/context, KB approval, or KB edit workflow.
version: 1.0.0
author: m3taversal
license: MIT
metadata:
hermes:
tags: [teleo, kb, postgres, claims, evidence, governance]
related_skills: [leo-synthesis-methods]
---
# Teleo KB Bridge
The canonical Teleo knowledge base is Postgres, not runtime memory.
## Mandatory Default Reply Contract
This contract applies even when the operator asks a broad, multi-part question.
Unless the operator explicitly asks for a detailed audit or long-form document,
the final reply must be at most 220 words; keep the whole reply under 220 words.
If the operator gives a smaller limit, that smaller word or line limit is
mandatory. Silently shorten the draft before sending it; never explain that it
was shortened.
Use only this compact shape:
1. direct answer in the first sentence;
2. no more than three short bullets containing only the relevant live readback,
canonical-versus-staged boundary, and current-schema constraint;
3. one proof-changing next action when an action is relevant.
Do not mirror this skill, enumerate every schema field, repeat the answer in a
summary, add a second follow-up, or turn a direct operator question into an
architecture lecture. A question with several requested dimensions still gets
one compressed answer. Load and apply only the rules relevant to that question.
When exact fields are requested, list only the fields needed to establish the
boundary and omit general background.
## Mandatory Current-Truth Gates
Apply these gates before drafting the answer or proposing a next action:
1. **Proposal readiness:** `approved` is intent approval, not applyability.
Read the proposal's `readiness` object from `list-proposals`,
`search-proposals`, or `show-proposal`. If `review_state` is
`approved_needs_apply_payload`, the next action is normalize into a strict
payload and review that new packet; never tell the operator to apply the
legacy row. `approved_contract_present` still proves only contract presence,
not strict validation, production-worker enablement, authorization, or apply.
2. **Source/evidence audit:** `teleo-kb search` searches claims and identity
context, not canonical source rows. Use `evidence <claim_id>` or a bounded
read-only `public.claim_evidence` to `public.sources` join. A missing locator
means weak provenance, but an existing canonical link is still canonical
evidence. A proposal's null `applied_at` proves only that proposal did not
apply; it does not prove matching canonical rows were never written elsewhere.
3. **Shared facts versus agent positions:** store the factual claim once with
shared evidence. Current agent-owned positions live in `public.beliefs` with
`id`, `agent_id`, `level`, `statement`, `confidence`, `falsifier`, `rank`,
`status`, `created_at`, and `updated_at`. The current table has no claim-ID
foreign key, so exact machine-readable belief-to-claim attribution is a
schema gap. `reasoning_tools` are methods and `behavioral_rules` are operating
rules; neither is generic stance storage.
4. **Write and capability boundary:** extraction creates candidates inside the
reviewed proposal payload, not canonical `public.sources` or `public.claims`.
The source compiler is build-only and locally proven; it is not yet exposed
as autonomous live-VPS Leo intake. New outcome claims must still be staged,
reviewed, and guarded-applied. Current `approve_claim` cannot insert
`behavioral_rules`/`governance_gates` or update an existing claim. Do not
describe those missing apply capabilities as runnable stages.
5. **Identity and proof:** canonical identity can include `personas`,
`strategies`, `beliefs`, strategy nodes/anchors, and related governed rows;
do not reduce it to `public.claims`. No active general DB-to-`SOUL.md`
renderer automation is currently proven. Unchanged table totals also do not
prove unchanged rows: updates and balanced insert/delete activity require row
IDs, timestamps, hashes, or fingerprints.
6. **Explanation versus relationship:** current `public.claim_edges` has no
rationale field. A `supersedes` edge records the relationship, not the human
explanation. Ground the explanation in source/evidence or a separately
reviewable claim. Do not invent a `retired` status value; retiring the old
row needs a separately reviewed update capability and exact before/after proof.
This is the VPS production leoclean surface. Before answering a KB-specific
question, run the local bridge:
```bash
/home/teleo/.hermes/profiles/leoclean/bin/teleo-kb context "<question>"
```
Use narrower bridge commands when needed:
```bash
/home/teleo/.hermes/profiles/leoclean/bin/teleo-kb status
/home/teleo/.hermes/profiles/leoclean/bin/teleo-kb search "<terms>"
/home/teleo/.hermes/profiles/leoclean/bin/teleo-kb show <claim_id>
/home/teleo/.hermes/profiles/leoclean/bin/teleo-kb evidence <claim_id>
/home/teleo/.hermes/profiles/leoclean/bin/teleo-kb edges <claim_id>
/home/teleo/.hermes/profiles/leoclean/bin/teleo-kb list-proposals
/home/teleo/.hermes/profiles/leoclean/bin/teleo-kb search-proposals "<terms>"
/home/teleo/.hermes/profiles/leoclean/bin/teleo-kb show-proposal <proposal_id>
/home/teleo/.hermes/profiles/leoclean/bin/teleo-kb decision-matrix-status
```
## Answer Discipline
For KB questions, prefer the bridge over raw database access. A good default is:
1. `teleo-kb context "<question>"`;
2. at most three `show` / `evidence` / `edges` / `show-proposal` follow-ups for
the most relevant IDs;
3. final answer with what is grounded, what is weak, and what evidence or
proposal would improve it.
Lead with the answer and end with at most one proof-changing follow-up. The
mandatory default reply contract above takes precedence over the reference
detail below.
Postgres is canonical knowledge, but it is not the only input to current reply
behavior. Deployed skills, runtime configuration, rendered `SOUL.md`, Hermes
session state, and current conversation context can also change an answer.
Unchanged canonical counts therefore do not prove that no runtime behavior
changed. Hermes `state.db` and session JSONL provide durable continuity; do not
say a restart necessarily erases every prior-session fact. Separate these proof
tiers explicitly:
- handler or temporary-profile success with no Telegram post is handler proof,
not Telegram-visible delivery proof;
- a Telegram-visible reply proves delivery and reply behavior, not canonical
DB mutation;
- canonical mutation requires row-level `public.*` readback and the matching
applied proposal receipt.
Leo may capture a source and stage a reviewable proposal when the operator asks
for knowledge intake. Staging is not canonical apply and does not require the
canonical-apply authorization. Approval is required before the guarded apply
step. Never manufacture a source row from a temporary memory label, chat label,
or proposal pointer; resolve a real URL, storage path, file hash, or retained
artifact first.
For no-context direct claims such as "Is X in Leo now?", "did the DB change?",
"did the decision matrix approve this?", or "is it still just proposals?", do
not stop at `search` or default `list-proposals`. Run the status-specific
proposal and governance readbacks needed to avoid overclaiming:
```bash
/home/teleo/.hermes/profiles/leoclean/bin/teleo-kb list-proposals --status all --limit 50
/home/teleo/.hermes/profiles/leoclean/bin/teleo-kb search-proposals "<entity/framework/claim terms>" --status all --limit 20
/home/teleo/.hermes/profiles/leoclean/bin/teleo-kb decision-matrix-status
```
If `decision-matrix-status` says the matrix tables are absent or incomplete,
do not infer matrix approval from proposal rationale, reviewer notes, or
`kb_stage.kb_proposals.status`. Say the matrix approval path is not proven and
fall back to proposal status plus canonical `public.*` readback.
If `search-proposals` finds an `approved` proposal with `applied_at` empty, say
it is approved/staged or packet-ready but not canonical. Do not answer
"missing" merely because default `list-proposals` did not show approved rows.
For these no-context direct claims, use this compact answer shape so the
operator gets the expected follow-up without needing to ask twice:
1. Direct answer: yes/no/partly, with the truth ceiling.
2. Readback used: the exact bridge commands or row facts checked.
3. Canonical vs staged split: name `public.*`, `kb_stage.kb_proposals`, status,
proposal id, and `applied_at` when relevant.
4. Next proof-changing follow-up: the one proof-changing or admin action that would
change the answer.
Always include the final line label `Next proof-changing follow-up:` for a
no-context direct-claim answer. Do not omit it just because the answer seems
complete.
If the proof-changing action is applying a proposal to canonical `public.*`,
say that apply requires explicit operator/admin authorization and should not be
run from normal chat without that authorization.
Use explicit no-overclaim wording when the canonical DB did not change:
"I cannot claim canonical DB changed until `public.*` readback plus
`applied_at`/postflight proof says it changed."
### Telegram Participant Naming Contract
- Address `@m3taversal` only as `m3taversal`, exactly. Do not shorten it, infer
a personal name, or substitute a name from memory, a session header, a soul
document, another chat, or another participant's message.
- Resolve the current speaker from the current Telegram update. Never carry a
participant identity across sessions or assign one participant's identity to
another user who replies or tags an account.
- When the visible sender is ambiguous, avoid direct address or use only the
exact visible Telegram handle. Ask for clarification only if identity is
required to perform the requested action.
- The legacy reviewer value `m3ta` may be quoted only as an exact database row
value, with wording such as `stored reviewed_by_handle: m3ta`. It is not a
form of address and does not authorize a nickname.
- Keep answer labels neutral. Never put a participant's name in a standard
follow-up label.
### Operator Direct-Claim Answer Contract
For m3taversal-style no-context questions, keep the answer direct but include the
proof language below. These are behavioral examples, not feature changes.
Before describing current database objects, separate current v1 schema from
proposed architecture. Current `public.claims` has `id`, `type`, `text`,
`status`, `confidence`, `tags`, `created_by`, `superseded_by`, `created_at`,
and `updated_at`; it has no `body`, generic metadata, or forecast-resolution
column. Current `public.sources` has `id`, `source_type`, `url`,
`storage_path`, `excerpt`, `hash`, `captured_at`, `created_by`, and
`created_at`; it has no author/channel/date fields. Current accepted claim-edge
types are `supports`, `challenges`, `requires`, `relates`, `contradicts`,
`supersedes`, `derives_from`, `cites`, `causes`, `constrains`, and
`accelerates`. Do not present a proposed v3 field, table, edge type, or policy
as shipped. If the requested representation does not fit current v1, state the
gap and stage a separate schema proposal before proposing data that depends on
it.
Current `public.claim_evidence` has only `claim_id`, `source_id`, `role`,
`weight`, `created_by`, and `created_at`. Its accepted roles are `grounds`,
`illustrates`, and `contradicts`. It has no excerpt, excerpt anchor, rationale,
or generic metadata column; source text belongs in `public.sources.excerpt`.
A `public.claim_evidence` link from a claim to a `public.sources` row is
canonical evidence even when that source row has no `url` or `storage_path`.
Describe a missing locator as weak or citation-only provenance, or say the
evidence is not traceable to the raw artifact. Do not call the canonical link
non-canonical or ungrounded solely because the locator is missing.
A Telegram attachment, extracted file, or proposal `source_ref` does not by
itself prove canonical evidence from that attachment. That proof requires a
`public.sources` row representing the attachment and a `public.claim_evidence`
link from the claim to that source row. Audit those rows before attributing the
claim's canonical evidence to the attachment.
Current `public.claim_edges` has only `id`, `from_claim`, `to_claim`,
`edge_type`, `weight`, `created_by`, and `created_at`. Both endpoints are claim
IDs, so do not claim that a `reasoning_tools` row is directly connected through
`public.claim_edges`.
For heterogeneous research packets, map only to structures proven in the
current schema:
- claims, sources, and evidence links are shared knowledge objects; an agent's
confidence, stance, or position belongs in current `public.beliefs` rather
than duplicate agent-authored copies of the same factual claim. Because
`public.beliefs` currently has no claim-ID foreign key, an exact structured
belief-to-shared-claim link requires a schema proposal;
- factual observations and disputed interpretations may become separate
`public.claims` rows with source/evidence links and valid claim-to-claim
edges;
- a reusable framework may become a `public.reasoning_tools` row, but the
current schema has no generic reasoning-tool-to-claim edge and no shipped
`concept_maps` or `claim_concept_map_links` table;
- a behavioral or operating rule belongs in the existing
`public.behavioral_rules` table, whose rule contract includes `agent_id`,
`category`, `rank`, `rule`, and `rationale`;
- an evaluative gate belongs in `public.governance_gates`, with `name`,
`criteria`, `evidence_bar`, and `pass_condition`; do not flatten a behavioral
rule into this gate table;
- a belief correction may create a new claim, a valid `supersedes` edge, and
set the old claim's `superseded_by` column. `superseded_by` is a column, not
an edge type.
Extraction and review do not write candidate material into canonical
`public.sources` or `public.claims`. Keep source candidates, extracted claims,
deduplication findings, contradictions, and proposed rows in the reviewed
proposal payload. A guarded apply may then create or reuse canonical source
rows before inserting the packet's dependent claims, evidence links, edges,
and supported context rows in one validated transaction.
Count receipts are packet-specific. The five values in the standard count
readback must all be observed, but they do not all need to change. Before
offering apply, validate the proposal's strict `apply_payload`; after apply,
prove the declared row IDs, the expected table-specific deltas, a committed
transaction, and a non-null `applied_at`. For an existing-claim/existing-source
evidence attachment, only `claim_evidence` may increase. For an edge-only
packet, only `claim_edges` may increase. Updating a proposal status need not
increase the proposal count. Never use `all five counts increased` as a
universal success condition.
The current strict `approve_claim` contract accepts only `claims`, `sources`,
`evidence`, `edges`, and `reasoning_tools` collections. It does not insert
`behavioral_rules` or `governance_gates`, update an existing claim's `status`
or `superseded_by`, or write arbitrary soul/context rows. Both policy tables
already exist; the missing piece is a separate reviewed apply capability for
them, not a generic schema-table gap. A correction packet may insert the new
claim and a valid `supersedes` edge, but retiring/updating the existing old
claim needs a separately reviewed apply capability. Do not describe any of
those unsupported writes as part of one atomic `approve_claim` transaction.
Use the current claim taxonomy unless a reviewed taxonomy change explicitly
authorizes a new value. The live values are `structural`, `normative`,
`empirical`, `concept`, and `meta`; do not invent `observation`, `hypothesis`,
or `belief` as current `public.claims.type` values. Current
`public.reasoning_tools` has `id`, `agent_id`, `name`, `description`,
`category`, and `created_at`; criteria or steps may be described inside
`description`, but they are not separate structured columns. When describing
evidence, say that `public.claim_evidence` links a claim to a source row whose
text may live in `public.sources.excerpt`; never say the evidence row stores or
carries the excerpt.
Current v1 has no shipped forecast-resolution fields or forecast-resolution
edge type. Preserve the original probability and its timestamp. Do not
overwrite historical confidence, invent resolution criteria after the fact, or
claim a `resolves` edge exists. If a forecast lacks precommitted criteria, call
the resolution ambiguous and stage any new forecast mechanism as a separate
schema proposal.
- "Did we actually update the KB?": answer `partly` only when current readback
shows `applied_at` rows and canonical `public.*` rows. Otherwise say
`mostly still proposals`; list applied, approved-but-not-applied, pending,
and canceled counts. Always include the state sentence `Approved is not the
same as applied`; for rows with empty apply timestamps, say `applied_at: NULL`
or `no applied_at`, and call them `not applied`. Before offering apply as the
next action, validate that the target has a matching strict `apply_payload`;
otherwise offer to rebuild and review that payload first.
- "Is Helmer's 7 Powers in Leo now?": answer `no, not canonical` unless
`public.sources`, `public.claims`, evidence, edges, and any reasoning-tool
rows exist. If proposal `a64df080` is approved with empty `applied_at`, call
it `approved/staged or packet-ready but not canonical`. Do not jump directly
from reviewer approval to authorization: first validate or rebuild the
matching strict payload, then review, authorize, apply, and postflight it.
- "Did the decision matrix approve this?": start with current/fresh schema
readback. For this question, run both `decision-matrix-status` and `status`,
then use the complete numeric count receipt. If `matrix_voters`,
`proposal_votes`, or `proposal_decisions` are
absent, say the decision-matrix path is not shipped; reviewer approval in
`kb_stage.kb_proposals` is not a matrix vote. Include this compact sentence:
`Fresh readback: the decision-matrix schema is absent; reviewer status is
not a decision-matrix vote.` If the operator wants the reviewer-only path,
validate the proposal's strict payload before offering authorization.
- "Are proposals stuck because documents point at the wrong source rows?":
do not answer as a single-cause `yes`. Say `not just pointer mismatch`: raw
files, Telegram refs, document evaluations, proposal `source_ref`/logical
source keys, and canonical `public.sources` rows are different layers. The
missing proof is a row-link audit plus guarded apply contract.
- "Can I demo Leo changes the KB?": lead with `staging yes, canonical KB
change not safe to demo from chat`. Include `demo tier` language. A safe demo
can show a real staging write to `kb_stage.kb_proposals` and read it back.
Say exactly: `Approved is not the same as applied.`
Canonical mutation is not provable from chat alone and is not a normal chat
command. State the exact current tier:
the strict existing-ID `add_edge` path is live-proven; guarded `approve_claim`
bundles and the rich packet set are clone-proven behind separate reviewer and
apply roles; the production permission migration and apply worker remain
disabled. Current approved legacy packets without strict `apply_payload` are
not worker-applyable. A canonical demo therefore still requires explicit
operator/admin authorization, the matching reviewed apply path, and retained
before/after postflight readback. Never suggest applying the strict canary
`add_edge` path to an approved legacy proposal unless that proposal itself has
a matching strict `add_edge` payload with two existing canonical claim IDs.
The safe default follow-up is a new staging canary or a separately reviewed
strict `add_edge` canary. Never collapse that into the false global statement
that no apply tooling exists.
- "Did editing SOUL.md change canonical identity?": answer `no`. `SOUL.md` is
a runtime/rendered artifact, not canonical Postgres, not the source of truth,
not a canonical commit, and not collective truth; canonical identity requires
DB rows plus render/sync proof. Direct edits can affect the next runtime
session but do not change canonical Postgres rows. Always include a row-level
proof sentence for this case: `Row-level proof would require current readback
of new or updated row IDs in public.claims, public.sources,
public.claim_evidence, public.claim_edges or identity tables, plus
postflight/render-sync proof; without those rows, canonical identity is
unchanged.`
Before every direct-claim answer, run a fresh bridge read. Use `status` for the
complete numeric count template, `search-proposals` followed by `show-proposal`
for a named proposal, and `decision-matrix-status` for matrix questions. If
those bridge commands do not return the exact canonical counts needed for the
question, use the documented read-only Postgres fallback. Never invent or reuse
a stale count.
Every direct-claim answer must contain one compact line beginning `DB readback:`
and copy either (a) a full UUID plus observed `status` and `applied_at`, or (b)
exact observed counts for the relevant `claims`, `sources`, `claim_edges`,
`claim_evidence`, and `kb_proposals` tables. Short eight-character IDs and
phrases such as `current readback` are not structured proof by themselves.
Copy exactly one of these formats, replacing every all-caps token with a value
from the current tool call:
```text
DB readback: proposal: `PROPOSAL_UUID_36_CHARS`; status: `OBSERVED_STATUS`; applied_at: `OBSERVED_TIMESTAMP_OR_NONE`.
DB readback: claims: `N`; sources: `N`; claim_edges: `N`; claim_evidence: `N`; kb_proposals: `N`.
```
Never shorten a UUID: it must contain all 36 characters and four hyphens. Use
`none` for `OBSERVED_TIMESTAMP_OR_NONE` when the database value is `NULL`, and
optionally add `(database NULL)` after the template. Do not paraphrase a count
as `total proposals`, omit a label, or substitute prose for either template.
If using the count template, all five values must be observed integers; `N/A` and `see public.*` are invalid
and the line is not evidence. If the first read does not expose every required
value, run another bounded
read-only bridge command before answering. Prefer the proposal template when
one unambiguous proposal answers the question; otherwise use the complete count
template.
Before returning the answer, verify that the literal `DB readback:` line is
present and complete. If it is missing, do not finalize the response; run the
bounded read-only bridge command needed to fill it.
Also use row-level proof vocabulary such as `row-link audit`, `row IDs`,
`new or updated rows`, `public.*`, and `postflight proof` where relevant. End
with exactly one final line beginning
`Next proof-changing follow-up:` that asks for or offers the next proof-changing
action.
## Telegram Rendering
Make KB answers easy to scan in Telegram:
- wrap claim IDs, proposal IDs, edge types, table names, statuses, counts, and
command names in backticks;
- when citing a specific claim, include both the claim headline and the claim
ID, for example: `claim text` (`<claim_id>`);
- when the bridge output includes `claim page: https://leo.livingip.xyz/kb/claims/<claim_id>`,
copy that URL into the answer so Telegram users can open the claim, body,
evidence, and edges directly;
- when a dashboard URL is available, include the canonical claim page as
`https://<argus-host>/kb/claims/<claim_id>`; otherwise name the dashboard
path `/kb/claims/<claim_id>` so the operator can open the claim, body,
evidence, and edges;
- prefer short sections such as `Claim`, `Body readback`, `Edges`,
`Evidence`, and `Proposal` instead of dense paragraphs.
Use raw `docker exec ... psql` only as a narrow read-only fallback when the
bridge cannot answer a schema or implementation-status question. If you use
that fallback, say it was a read-only inspection. Do not present raw SQL as the
normal user workflow.
## Claim / Body / Concept Map Loop
When a user challenges a claim as too broad, too light, unfalsifiable, or
poorly linked, do this loop:
1. fetch the headline claim with `teleo-kb show <claim_id>` or `search`;
2. fetch evidence and edges with `teleo-kb evidence <claim_id>` and
`teleo-kb edges <claim_id>`;
3. separate what the KB actually says from your synthesis;
4. decide whether the right change is: attach evidence, add edges, revise the
claim, supersede the claim, split the claim into multiple claims, or create a
concept-map/reasoning-tool proposal;
5. stage a reviewable proposal when the requested correction is clear enough.
For "was this implemented?" or "did you apply that?" questions, answer in this
shape:
```text
Status: applied | pending | missing | partially applied
Canonical rows: <what exists in public.*>
Staged proposals: <proposal IDs/statuses>
Rows/edges/evidence needed: <concrete list>
Next admin action: approve/apply the proposal, request edits, or create the missing proposal.
```
Do not call an approved proposal "implemented" until canonical `public.*` rows
and edges show the applied state.
## External Doctrine Contributions
When asked to help with another project's declaration, constitution, doctrine,
manifesto, GitHub issue, or PR, do not export Teleo doctrine as if it is the
target project's own position.
Use the target project's native language first. If the external text says
phrases like `no single voice can own understanding`, `purpose precedes
capability`, or another local principle, treat those as the wedge. Frame Leo's
contribution as:
```text
This extends your own principle <X> into <specific operational question>.
```
Prefer issue-before-PR unless the operator explicitly authorizes a PR. The
issue should ask a concrete question, identify the gap, and invite the target
community to decide whether they want draft language. Link Teleo analysis only
as one reference, not as controlling doctrine.
Consent is action-specific. Leo may draft, critique, and propose language in
chat. Leo should not sign, post, submit, open an issue/PR, or speak for Teleo
publicly without explicit operator authorization for that exact public action.
## Memory vs KB Rule
Do not treat runtime memory as canonical truth.
```text
agent memory = local/runtime continuity
Postgres KB = canonical collective knowledge
```
If a correction changes collective truth, it belongs in the KB graph, not only
runtime memory.
## VPS DB Objects
Relevant DB objects live in the VPS Postgres container and should normally be
reached through `teleo-kb`:
- `kb_stage.kb_proposals` - durable proposal ledger;
- `kb_stage.pending_kb_proposals` - proposals with `status = 'pending_review'`;
- `kb_stage.document_evaluations` - lightweight document evaluation decisions;
- `public.claims`, `public.sources`, `public.claim_evidence`, `public.claim_edges` - canonical tables.
## Write Policy
Canonical KB writes are locked. The bridge can create reviewable proposals, but
it does not directly mutate canonical `public.*` rows from normal chat.
If a reviewer explicitly asks for proposal status reconciliation or canonical
application, inspect the proposal first, use the narrowest available bridge or
admin apply path, and retain before/after readback. The normal chat bridge does
not expose `teleo-kb apply-*`, but the repository contains a live-proven strict
existing-ID `add_edge` path and clone-proven guarded `approve_claim` tooling.
Name which exact operation/tier is available, and say when the production
permission migration, worker, strict payload, or explicit authorization is
still missing. Do not invite ad hoc SQL from chat or treat a chat statement,
runtime memory, or staged proposal as canonical truth.
Never end a normal Telegram answer by offering to run direct `INSERT`, `UPDATE`,
or transaction SQL from chat. Even if the user is authorized, the product flow is
review-first:
```text
Next admin-panel action: show the staged proposal, dependency groups, and
before/after rows; let a reviewer approve, reject, edit, or run a dedicated
apply tool with retained readback.
```
Because the current chat bridge has no apply command, stop at the exact reviewed
operator path and its authorization boundary. The next thing Leo may offer from
chat is to draft or refresh the admin review packet, not to mutate canonical
tables directly.

View file

@ -1,7 +1,7 @@
--- ---
name: teleo-kb-bridge name: teleo-kb-bridge
description: Use the VPS Postgres KB bridge before answering questions about claims, evidence, edges, schema-backed soul/context, KB approval, or KB edit workflow. description: Use the VPS Postgres KB bridge before answering questions about claims, evidence, agent beliefs, schema-backed identity, proposal readiness, or KB changes.
version: 1.0.0 version: 1.1.0
author: m3taversal author: m3taversal
license: MIT license: MIT
metadata: metadata:
@ -12,39 +12,77 @@ metadata:
# Teleo KB Bridge # Teleo KB Bridge
The canonical Teleo knowledge base is Postgres, not runtime memory. Postgres is canonical knowledge. Runtime memory, chat, and `SOUL.md` are not
canonical database writes.
## Mandatory Default Reply Contract ## Mandatory Reply Contract
This contract applies even when the operator asks a broad, multi-part question. Unless the operator explicitly requests a detailed audit or long-form document,
Unless the operator explicitly asks for a detailed audit or long-form document, the final reply must be at most 220 words. A smaller word or line limit is
the final reply must be at most 220 words; keep the whole reply under 220 words. mandatory.
If the operator gives a smaller limit, that smaller word or line limit is
mandatory. Silently shorten the draft before sending it; never explain that it
was shortened.
Use only this compact shape: Use this shape:
1. direct answer in the first sentence; 1. direct answer in the first sentence;
2. no more than three short bullets containing only the relevant live readback, 2. at most three short bullets with only the relevant readback, state boundary,
canonical-versus-staged boundary, and current-schema constraint; and current-schema constraint;
3. one proof-changing next action when an action is relevant. 3. at most one proof-changing next action.
Do not mirror this skill, enumerate every schema field, repeat the answer in a Do not mirror this skill, enumerate unrelated fields, repeat the answer as a
summary, add a second follow-up, or turn a direct operator question into an summary, or turn a direct question into an architecture lecture. Silently
architecture lecture. A question with several requested dimensions still gets shorten the draft before sending it.
one compressed answer. Load and apply only the rules relevant to that question.
When exact fields are requested, list only the fields needed to establish the
boundary and omit general background.
This is the VPS production leoclean surface. Before answering a KB-specific ## Mandatory Current-Truth Gates
question, run the local bridge:
Apply these before drafting the answer or proposing a next action:
1. **Proposal readiness:** `approved` is intent approval, not applyability.
Read the proposal's `readiness` object. If `review_state` is
`approved_needs_apply_payload`, normalize into a strict payload and review
that new packet; never tell the operator to apply the legacy row.
`approved_contract_present` proves only contract presence, not strict
validation, production-worker enablement, authorization, or apply.
2. **Source/evidence audit:** `teleo-kb search` searches claims and identity
context, not canonical source rows. Use `evidence <claim_id>` or a bounded
read-only `public.claim_evidence` to `public.sources` join. A missing locator
means weak provenance, but an existing canonical link is still canonical
evidence. Null `applied_at` proves only that proposal did not apply; matching
canonical rows could have another origin.
3. **Shared facts versus agent positions:** store the factual claim once with
shared evidence. Current agent-owned positions live in `public.beliefs` with
`id`, `agent_id`, `level`, `statement`, `confidence`, `falsifier`, `rank`,
`status`, `created_at`, and `updated_at`. The table has no claim-ID foreign
key, so exact belief-to-claim attribution is a schema gap. `reasoning_tools`
are methods and `behavioral_rules` are operating rules; neither is generic
stance storage.
4. **Write boundary:** extraction creates candidates inside a reviewed proposal,
not canonical `public.sources` or `public.claims`. The source compiler is
build-only and locally proven; it is not autonomous live-VPS intake. New
claims, including outcomes, still need staging, review, and guarded apply.
5. **Capability boundary:** current `approve_claim` accepts only `claims`,
`sources`, `evidence`, `edges`, and `reasoning_tools`. It cannot insert
`behavioral_rules` or `governance_gates`, update an existing claim, or run a
general identity render. Do not describe missing capabilities as runnable
stages.
6. **Identity and proof:** canonical identity can include `personas`,
`strategies`, `beliefs`, strategy nodes/anchors, and related governed rows;
do not reduce it to claims. No active general DB-to-`SOUL.md` renderer
automation is currently proven. Unchanged table totals also do not prove
unchanged rows; use row IDs, timestamps, hashes, or fingerprints.
7. **Explanation versus relationship:** current `public.claim_edges` has no rationale
field. A `supersedes` edge records a relationship, not the human explanation.
Ground the explanation in source/evidence or a separately reviewable claim.
Do not invent a `retired` status value.
## Read Commands
This is the VPS production leoclean surface. Start with:
```bash ```bash
/home/teleo/.hermes/profiles/leoclean/bin/teleo-kb context "<question>" /home/teleo/.hermes/profiles/leoclean/bin/teleo-kb context "<question>"
``` ```
Use narrower bridge commands when needed: Use only the bounded follow-ups relevant to the question:
```bash ```bash
/home/teleo/.hermes/profiles/leoclean/bin/teleo-kb status /home/teleo/.hermes/profiles/leoclean/bin/teleo-kb status
@ -52,434 +90,65 @@ Use narrower bridge commands when needed:
/home/teleo/.hermes/profiles/leoclean/bin/teleo-kb show <claim_id> /home/teleo/.hermes/profiles/leoclean/bin/teleo-kb show <claim_id>
/home/teleo/.hermes/profiles/leoclean/bin/teleo-kb evidence <claim_id> /home/teleo/.hermes/profiles/leoclean/bin/teleo-kb evidence <claim_id>
/home/teleo/.hermes/profiles/leoclean/bin/teleo-kb edges <claim_id> /home/teleo/.hermes/profiles/leoclean/bin/teleo-kb edges <claim_id>
/home/teleo/.hermes/profiles/leoclean/bin/teleo-kb list-proposals /home/teleo/.hermes/profiles/leoclean/bin/teleo-kb list-proposals --status all --limit 50
/home/teleo/.hermes/profiles/leoclean/bin/teleo-kb search-proposals "<terms>" /home/teleo/.hermes/profiles/leoclean/bin/teleo-kb search-proposals "<terms>" --status all --limit 20
/home/teleo/.hermes/profiles/leoclean/bin/teleo-kb show-proposal <proposal_id> /home/teleo/.hermes/profiles/leoclean/bin/teleo-kb show-proposal <proposal_id>
/home/teleo/.hermes/profiles/leoclean/bin/teleo-kb decision-matrix-status /home/teleo/.hermes/profiles/leoclean/bin/teleo-kb decision-matrix-status
``` ```
## Answer Discipline For direct state questions, run `status` plus the relevant proposal read. Copy a
full proposal UUID when one row answers the question; otherwise copy all five
For KB questions, prefer the bridge over raw database access. A good default is: observed counts:
1. `teleo-kb context "<question>"`;
2. at most three `show` / `evidence` / `edges` / `show-proposal` follow-ups for
the most relevant IDs;
3. final answer with what is grounded, what is weak, and what evidence or
proposal would improve it.
Lead with the answer and end with at most one proof-changing follow-up. The
mandatory default reply contract above takes precedence over the reference
detail below.
Postgres is canonical knowledge, but it is not the only input to current reply
behavior. Deployed skills, runtime configuration, rendered `SOUL.md`, Hermes
session state, and current conversation context can also change an answer.
Unchanged canonical counts therefore do not prove that no runtime behavior
changed. Hermes `state.db` and session JSONL provide durable continuity; do not
say a restart necessarily erases every prior-session fact. Separate these proof
tiers explicitly:
- handler or temporary-profile success with no Telegram post is handler proof,
not Telegram-visible delivery proof;
- a Telegram-visible reply proves delivery and reply behavior, not canonical
DB mutation;
- canonical mutation requires row-level `public.*` readback and the matching
applied proposal receipt.
Leo may capture a source and stage a reviewable proposal when the operator asks
for knowledge intake. Staging is not canonical apply and does not require the
canonical-apply authorization. Approval is required before the guarded apply
step. Never manufacture a source row from a temporary memory label, chat label,
or proposal pointer; resolve a real URL, storage path, file hash, or retained
artifact first.
For no-context direct claims such as "Is X in Leo now?", "did the DB change?",
"did the decision matrix approve this?", or "is it still just proposals?", do
not stop at `search` or default `list-proposals`. Run the status-specific
proposal and governance readbacks needed to avoid overclaiming:
```bash
/home/teleo/.hermes/profiles/leoclean/bin/teleo-kb list-proposals --status all --limit 50
/home/teleo/.hermes/profiles/leoclean/bin/teleo-kb search-proposals "<entity/framework/claim terms>" --status all --limit 20
/home/teleo/.hermes/profiles/leoclean/bin/teleo-kb decision-matrix-status
```
If `decision-matrix-status` says the matrix tables are absent or incomplete,
do not infer matrix approval from proposal rationale, reviewer notes, or
`kb_stage.kb_proposals.status`. Say the matrix approval path is not proven and
fall back to proposal status plus canonical `public.*` readback.
If `search-proposals` finds an `approved` proposal with `applied_at` empty, say
it is approved/staged or packet-ready but not canonical. Do not answer
"missing" merely because default `list-proposals` did not show approved rows.
For these no-context direct claims, use this compact answer shape so the
operator gets the expected follow-up without needing to ask twice:
1. Direct answer: yes/no/partly, with the truth ceiling.
2. Readback used: the exact bridge commands or row facts checked.
3. Canonical vs staged split: name `public.*`, `kb_stage.kb_proposals`, status,
proposal id, and `applied_at` when relevant.
4. Next proof-changing follow-up: the one proof-changing or admin action that would
change the answer.
Always include the final line label `Next proof-changing follow-up:` for a
no-context direct-claim answer. Do not omit it just because the answer seems
complete.
If the proof-changing action is applying a proposal to canonical `public.*`,
say that apply requires explicit operator/admin authorization and should not be
run from normal chat without that authorization.
Use explicit no-overclaim wording when the canonical DB did not change:
"I cannot claim canonical DB changed until `public.*` readback plus
`applied_at`/postflight proof says it changed."
### Telegram Participant Naming Contract
- Address `@m3taversal` only as `m3taversal`, exactly. Do not shorten it, infer
a personal name, or substitute a name from memory, a session header, a soul
document, another chat, or another participant's message.
- Resolve the current speaker from the current Telegram update. Never carry a
participant identity across sessions or assign one participant's identity to
another user who replies or tags an account.
- When the visible sender is ambiguous, avoid direct address or use only the
exact visible Telegram handle. Ask for clarification only if identity is
required to perform the requested action.
- The legacy reviewer value `m3ta` may be quoted only as an exact database row
value, with wording such as `stored reviewed_by_handle: m3ta`. It is not a
form of address and does not authorize a nickname.
- Keep answer labels neutral. Never put a participant's name in a standard
follow-up label.
### Operator Direct-Claim Answer Contract
For m3taversal-style no-context questions, keep the answer direct but include the
proof language below. These are behavioral examples, not feature changes.
Before describing current database objects, separate current v1 schema from
proposed architecture. Current `public.claims` has `id`, `type`, `text`,
`status`, `confidence`, `tags`, `created_by`, `superseded_by`, `created_at`,
and `updated_at`; it has no `body`, generic metadata, or forecast-resolution
column. Current `public.sources` has `id`, `source_type`, `url`,
`storage_path`, `excerpt`, `hash`, `captured_at`, `created_by`, and
`created_at`; it has no author/channel/date fields. Current accepted claim-edge
types are `supports`, `challenges`, `requires`, `relates`, `contradicts`,
`supersedes`, `derives_from`, `cites`, `causes`, `constrains`, and
`accelerates`. Do not present a proposed v3 field, table, edge type, or policy
as shipped. If the requested representation does not fit current v1, state the
gap and stage a separate schema proposal before proposing data that depends on
it.
Current `public.claim_evidence` has only `claim_id`, `source_id`, `role`,
`weight`, `created_by`, and `created_at`. Its accepted roles are `grounds`,
`illustrates`, and `contradicts`. It has no excerpt, excerpt anchor, rationale,
or generic metadata column; source text belongs in `public.sources.excerpt`.
A `public.claim_evidence` link from a claim to a `public.sources` row is
canonical evidence even when that source row has no `url` or `storage_path`.
Describe a missing locator as weak or citation-only provenance, or say the
evidence is not traceable to the raw artifact. Do not call the canonical link
non-canonical or ungrounded solely because the locator is missing.
A Telegram attachment, extracted file, or proposal `source_ref` does not by
itself prove canonical evidence from that attachment. That proof requires a
`public.sources` row representing the attachment and a `public.claim_evidence`
link from the claim to that source row. Audit those rows before attributing the
claim's canonical evidence to the attachment.
Current `public.claim_edges` has only `id`, `from_claim`, `to_claim`,
`edge_type`, `weight`, `created_by`, and `created_at`. Both endpoints are claim
IDs, so do not claim that a `reasoning_tools` row is directly connected through
`public.claim_edges`.
For heterogeneous research packets, map only to structures proven in the
current schema:
- claims, sources, and evidence links are shared knowledge objects; an agent's
confidence, stance, or position belongs in agent-specific belief/position
structures rather than duplicate agent-authored copies of the same claim;
- factual observations and disputed interpretations may become separate
`public.claims` rows with source/evidence links and valid claim-to-claim
edges;
- a reusable framework may become a `public.reasoning_tools` row, but the
current schema has no generic reasoning-tool-to-claim edge and no shipped
`concept_maps` or `claim_concept_map_links` table;
- a behavioral or operating rule belongs in the existing
`public.behavioral_rules` table, whose rule contract includes `agent_id`,
`category`, `rank`, `rule`, and `rationale`;
- an evaluative gate belongs in `public.governance_gates`, with `name`,
`criteria`, `evidence_bar`, and `pass_condition`; do not flatten a behavioral
rule into this gate table;
- a belief correction may create a new claim, a valid `supersedes` edge, and
set the old claim's `superseded_by` column. `superseded_by` is a column, not
an edge type.
Extraction and review do not write candidate material into canonical
`public.sources` or `public.claims`. Keep source candidates, extracted claims,
deduplication findings, contradictions, and proposed rows in the reviewed
proposal payload. A guarded apply may then create or reuse canonical source
rows before inserting the packet's dependent claims, evidence links, edges,
and supported context rows in one validated transaction.
Count receipts are packet-specific. The five values in the standard count
readback must all be observed, but they do not all need to change. Before
offering apply, validate the proposal's strict `apply_payload`; after apply,
prove the declared row IDs, the expected table-specific deltas, a committed
transaction, and a non-null `applied_at`. For an existing-claim/existing-source
evidence attachment, only `claim_evidence` may increase. For an edge-only
packet, only `claim_edges` may increase. Updating a proposal status need not
increase the proposal count. Never use `all five counts increased` as a
universal success condition.
The current strict `approve_claim` contract accepts only `claims`, `sources`,
`evidence`, `edges`, and `reasoning_tools` collections. It does not insert
`behavioral_rules` or `governance_gates`, update an existing claim's `status`
or `superseded_by`, or write arbitrary soul/context rows. Both policy tables
already exist; the missing piece is a separate reviewed apply capability for
them, not a generic schema-table gap. A correction packet may insert the new
claim and a valid `supersedes` edge, but retiring/updating the existing old
claim needs a separately reviewed apply capability. Do not describe any of
those unsupported writes as part of one atomic `approve_claim` transaction.
Use the current claim taxonomy unless a reviewed taxonomy change explicitly
authorizes a new value. The live values are `structural`, `normative`,
`empirical`, `concept`, and `meta`; do not invent `observation`, `hypothesis`,
or `belief` as current `public.claims.type` values. Current
`public.reasoning_tools` has `id`, `agent_id`, `name`, `description`,
`category`, and `created_at`; criteria or steps may be described inside
`description`, but they are not separate structured columns. When describing
evidence, say that `public.claim_evidence` links a claim to a source row whose
text may live in `public.sources.excerpt`; never say the evidence row stores or
carries the excerpt.
Current v1 has no shipped forecast-resolution fields or forecast-resolution
edge type. Preserve the original probability and its timestamp. Do not
overwrite historical confidence, invent resolution criteria after the fact, or
claim a `resolves` edge exists. If a forecast lacks precommitted criteria, call
the resolution ambiguous and stage any new forecast mechanism as a separate
schema proposal.
- "Did we actually update the KB?": answer `partly` only when current readback
shows `applied_at` rows and canonical `public.*` rows. Otherwise say
`mostly still proposals`; list applied, approved-but-not-applied, pending,
and canceled counts. Always include the state sentence `Approved is not the
same as applied`; for rows with empty apply timestamps, say `applied_at: NULL`
or `no applied_at`, and call them `not applied`. Before offering apply as the
next action, validate that the target has a matching strict `apply_payload`;
otherwise offer to rebuild and review that payload first.
- "Is Helmer's 7 Powers in Leo now?": answer `no, not canonical` unless
`public.sources`, `public.claims`, evidence, edges, and any reasoning-tool
rows exist. If proposal `a64df080` is approved with empty `applied_at`, call
it `approved/staged or packet-ready but not canonical`. Do not jump directly
from reviewer approval to authorization: first validate or rebuild the
matching strict payload, then review, authorize, apply, and postflight it.
- "Did the decision matrix approve this?": start with current/fresh schema
readback. For this question, run both `decision-matrix-status` and `status`,
then use the complete numeric count receipt. If `matrix_voters`,
`proposal_votes`, or `proposal_decisions` are
absent, say the decision-matrix path is not shipped; reviewer approval in
`kb_stage.kb_proposals` is not a matrix vote. Include this compact sentence:
`Fresh readback: the decision-matrix schema is absent; reviewer status is
not a decision-matrix vote.` If the operator wants the reviewer-only path,
validate the proposal's strict payload before offering authorization.
- "Are proposals stuck because documents point at the wrong source rows?":
do not answer as a single-cause `yes`. Say `not just pointer mismatch`: raw
files, Telegram refs, document evaluations, proposal `source_ref`/logical
source keys, and canonical `public.sources` rows are different layers. The
missing proof is a row-link audit plus guarded apply contract.
- "Can I demo Leo changes the KB?": lead with `staging yes, canonical KB
change not safe to demo from chat`. Include `demo tier` language. A safe demo
can show a real staging write to `kb_stage.kb_proposals` and read it back.
Say exactly: `Approved is not the same as applied.`
Canonical mutation is not provable from chat alone and is not a normal chat
command. State the exact current tier:
the strict existing-ID `add_edge` path is live-proven; guarded `approve_claim`
bundles and the rich packet set are clone-proven behind separate reviewer and
apply roles; the production permission migration and apply worker remain
disabled. Current approved legacy packets without strict `apply_payload` are
not worker-applyable. A canonical demo therefore still requires explicit
operator/admin authorization, the matching reviewed apply path, and retained
before/after postflight readback. Never suggest applying the strict canary
`add_edge` path to an approved legacy proposal unless that proposal itself has
a matching strict `add_edge` payload with two existing canonical claim IDs.
The safe default follow-up is a new staging canary or a separately reviewed
strict `add_edge` canary. Never collapse that into the false global statement
that no apply tooling exists.
- "Did editing SOUL.md change canonical identity?": answer `no`. `SOUL.md` is
a runtime/rendered artifact, not canonical Postgres, not the source of truth,
not a canonical commit, and not collective truth; canonical identity requires
DB rows plus render/sync proof. Direct edits can affect the next runtime
session but do not change canonical Postgres rows. Always include a row-level
proof sentence for this case: `Row-level proof would require current readback
of new or updated row IDs in public.claims, public.sources,
public.claim_evidence, public.claim_edges or identity tables, plus
postflight/render-sync proof; without those rows, canonical identity is
unchanged.`
Before every direct-claim answer, run a fresh bridge read. Use `status` for the
complete numeric count template, `search-proposals` followed by `show-proposal`
for a named proposal, and `decision-matrix-status` for matrix questions. If
those bridge commands do not return the exact canonical counts needed for the
question, use the documented read-only Postgres fallback. Never invent or reuse
a stale count.
Every direct-claim answer must contain one compact line beginning `DB readback:`
and copy either (a) a full UUID plus observed `status` and `applied_at`, or (b)
exact observed counts for the relevant `claims`, `sources`, `claim_edges`,
`claim_evidence`, and `kb_proposals` tables. Short eight-character IDs and
phrases such as `current readback` are not structured proof by themselves.
Copy exactly one of these formats, replacing every all-caps token with a value
from the current tool call:
```text ```text
DB readback: proposal: `PROPOSAL_UUID_36_CHARS`; status: `OBSERVED_STATUS`; applied_at: `OBSERVED_TIMESTAMP_OR_NONE`. DB readback: proposal: `PROPOSAL_UUID`; status: `STATUS`; applied_at: `TIMESTAMP_OR_NONE`; readiness: `REVIEW_STATE`.
DB readback: claims: `N`; sources: `N`; claim_edges: `N`; claim_evidence: `N`; kb_proposals: `N`. DB readback: claims: `N`; sources: `N`; claim_edges: `N`; claim_evidence: `N`; kb_proposals: `N`.
``` ```
Never shorten a UUID: it must contain all 36 characters and four hyphens. Use If decision-matrix tables are absent, say reviewer status is not a
`none` for `OBSERVED_TIMESTAMP_OR_NONE` when the database value is `NULL`, and decision-matrix vote. If a bridge command cannot answer a schema question, use
optionally add `(database NULL)` after the template. Do not paraphrase a count one bounded read-only Postgres inspection and label it as such.
as `total proposals`, omit a label, or substitute prose for either template.
If using the count template, all five values must be observed integers; `N/A` and `see public.*` are invalid
and the line is not evidence. If the first read does not expose every required
value, run another bounded
read-only bridge command before answering. Prefer the proposal template when
one unambiguous proposal answers the question; otherwise use the complete count
template.
Before returning the answer, verify that the literal `DB readback:` line is
present and complete. If it is missing, do not finalize the response; run the
bounded read-only bridge command needed to fill it.
Also use row-level proof vocabulary such as `row-link audit`, `row IDs`,
`new or updated rows`, `public.*`, and `postflight proof` where relevant. End
with exactly one final line beginning
`Next proof-changing follow-up:` that asks for or offers the next proof-changing
action.
## Telegram Rendering ## Current V1 Facts
Make KB answers easy to scan in Telegram: - Claim types: `structural`, `normative`, `empirical`, `concept`, `meta`.
- Evidence roles: `grounds`, `illustrates`, `contradicts`; evidence links point
- wrap claim IDs, proposal IDs, edge types, table names, statuses, counts, and to source rows whose text may live in `public.sources.excerpt`.
command names in backticks; - Edge types: `supports`, `challenges`, `requires`, `relates`, `contradicts`,
- when citing a specific claim, include both the claim headline and the claim `supersedes`, `derives_from`, `cites`, `causes`, `constrains`, `accelerates`.
ID, for example: `claim text` (`<claim_id>`); - Current v1 has no forecast-resolution fields or `resolves` edge. Preserve the
- when the bridge output includes `claim page: https://leo.livingip.xyz/kb/claims/<claim_id>`, original probability and call resolution ambiguous when criteria were absent.
copy that URL into the answer so Telegram users can open the claim, body, - A Telegram attachment, cache file, or proposal `source_ref` alone does not
evidence, and edges directly; prove canonical evidence from that artifact.
- when a dashboard URL is available, include the canonical claim page as - Handler success without a Telegram post is handler proof, not
`https://<argus-host>/kb/claims/<claim_id>`; otherwise name the dashboard Telegram-visible delivery. A visible reply proves delivery, not DB mutation.
path `/kb/claims/<claim_id>` so the operator can open the claim, body,
evidence, and edges;
- prefer short sections such as `Claim`, `Body readback`, `Edges`,
`Evidence`, and `Proposal` instead of dense paragraphs.
Use raw `docker exec ... psql` only as a narrow read-only fallback when the
bridge cannot answer a schema or implementation-status question. If you use
that fallback, say it was a read-only inspection. Do not present raw SQL as the
normal user workflow.
## Claim / Body / Concept Map Loop
When a user challenges a claim as too broad, too light, unfalsifiable, or
poorly linked, do this loop:
1. fetch the headline claim with `teleo-kb show <claim_id>` or `search`;
2. fetch evidence and edges with `teleo-kb evidence <claim_id>` and
`teleo-kb edges <claim_id>`;
3. separate what the KB actually says from your synthesis;
4. decide whether the right change is: attach evidence, add edges, revise the
claim, supersede the claim, split the claim into multiple claims, or create a
concept-map/reasoning-tool proposal;
5. stage a reviewable proposal when the requested correction is clear enough.
For "was this implemented?" or "did you apply that?" questions, answer in this
shape:
```text
Status: applied | pending | missing | partially applied
Canonical rows: <what exists in public.*>
Staged proposals: <proposal IDs/statuses>
Rows/edges/evidence needed: <concrete list>
Next admin action: approve/apply the proposal, request edits, or create the missing proposal.
```
Do not call an approved proposal "implemented" until canonical `public.*` rows
and edges show the applied state.
## External Doctrine Contributions
When asked to help with another project's declaration, constitution, doctrine,
manifesto, GitHub issue, or PR, do not export Teleo doctrine as if it is the
target project's own position.
Use the target project's native language first. If the external text says
phrases like `no single voice can own understanding`, `purpose precedes
capability`, or another local principle, treat those as the wedge. Frame Leo's
contribution as:
```text
This extends your own principle <X> into <specific operational question>.
```
Prefer issue-before-PR unless the operator explicitly authorizes a PR. The
issue should ask a concrete question, identify the gap, and invite the target
community to decide whether they want draft language. Link Teleo analysis only
as one reference, not as controlling doctrine.
Consent is action-specific. Leo may draft, critique, and propose language in
chat. Leo should not sign, post, submit, open an issue/PR, or speak for Teleo
publicly without explicit operator authorization for that exact public action.
## Memory vs KB Rule
Do not treat runtime memory as canonical truth.
```text
agent memory = local/runtime continuity
Postgres KB = canonical collective knowledge
```
If a correction changes collective truth, it belongs in the KB graph, not only
runtime memory.
## VPS DB Objects
Relevant DB objects live in the VPS Postgres container and should normally be
reached through `teleo-kb`:
- `kb_stage.kb_proposals` - durable proposal ledger;
- `kb_stage.pending_kb_proposals` - proposals with `status = 'pending_review'`;
- `kb_stage.document_evaluations` - lightweight document evaluation decisions;
- `public.claims`, `public.sources`, `public.claim_evidence`, `public.claim_edges` - canonical tables.
## Write Policy ## Write Policy
Canonical KB writes are locked. The bridge can create reviewable proposals, but Normal chat may capture a real source and stage a reviewable proposal. It may
it does not directly mutate canonical `public.*` rows from normal chat. not mutate canonical `public.*`. Apply requires the matching strict payload,
human review, explicit operator/admin authorization, a supported apply path, a
committed transaction, row-level postflight proof, and non-null `applied_at`.
If a reviewer explicitly asks for proposal status reconciliation or canonical The strict existing-ID `add_edge` path is live-proven. Rich `approve_claim`
application, inspect the proposal first, use the narrowest available bridge or bundles are clone-proven. The production apply worker remains disabled/inactive
admin apply path, and retain before/after readback. The normal chat bridge does unless a fresh service readback proves otherwise. Never offer direct SQL from
not expose `teleo-kb apply-*`, but the repository contains a live-proven strict chat.
existing-ID `add_edge` path and clone-proven guarded `approve_claim` tooling.
Name which exact operation/tier is available, and say when the production
permission migration, worker, strict payload, or explicit authorization is
still missing. Do not invite ad hoc SQL from chat or treat a chat statement,
runtime memory, or staged proposal as canonical truth.
Never end a normal Telegram answer by offering to run direct `INSERT`, `UPDATE`, ## Telegram Participant Naming
or transaction SQL from chat. Even if the user is authorized, the product flow is
review-first: Address `@m3taversal` only as `m3taversal`, exactly. Never infer or reuse a
personal name or nickname. The stored legacy reviewer value `m3ta` may be quoted
only as a database value; it is not a form of address. Resolve every speaker
from the current Telegram update and never carry identity across participants.
## Detailed Reference
Only when the operator explicitly asks for a detailed audit, schema inventory,
external-doctrine workflow, or long-form runbook, read the relevant section of:
```text ```text
Next admin-panel action: show the staged proposal, dependency groups, and /home/teleo/.hermes/profiles/leoclean/skills/teleo-kb-bridge/REFERENCE.md
before/after rows; let a reviewer approve, reject, edit, or run a dedicated
apply tool with retained readback.
``` ```
Because the current chat bridge has no apply command, stop at the exact reviewed Do not load or repeat the entire reference for normal Telegram questions.
operator path and its authorization boundary. The next thing Leo may offer from
chat is to draft or refresh the admin review packet, not to mutate canonical
tables directly.

View file

@ -48,7 +48,14 @@ SCENARIOS: list[dict[str, Any]] = [
"next_action", "next_action",
"no_overclaim", "no_overclaim",
], ],
"required_concepts": ["provenance", "deduplication", "contradiction", "staged_review_apply", "receipt"], "required_concepts": [
"provenance",
"deduplication",
"contradiction",
"candidate_before_canonical",
"staged_review_apply",
"receipt",
],
}, },
{ {
"id": "OOS-03", "id": "OOS-03",
@ -59,7 +66,7 @@ SCENARIOS: list[dict[str, Any]] = [
"runtime artifact. Do not change either surface." "runtime artifact. Do not change either surface."
), ),
"required_signals": ["identity_rendering", "canonical_db", "row_level_proof", "no_overclaim"], "required_signals": ["identity_rendering", "canonical_db", "row_level_proof", "no_overclaim"],
"required_concepts": ["identity_chain", "state_boundary", "receipt"], "required_concepts": ["identity_chain", "identity_rows_beyond_claims", "state_boundary", "receipt"],
}, },
{ {
"id": "OOS-04", "id": "OOS-04",
@ -155,7 +162,12 @@ SCENARIOS: list[dict[str, Any]] = [
"surfaces can affect the answer, and separate the proof tiers. Read-only; keep it under 180 words." "surfaces can affect the answer, and separate the proof tiers. Read-only; keep it under 180 words."
), ),
"required_signals": ["canonical_db", "no_overclaim"], "required_signals": ["canonical_db", "no_overclaim"],
"required_concepts": ["runtime_inputs", "durable_session_continuity", "proof_tiers"], "required_concepts": [
"runtime_inputs",
"durable_session_continuity",
"proof_tiers",
"row_content_proof",
],
}, },
{ {
"id": "OOS-11", "id": "OOS-11",
@ -188,7 +200,7 @@ SCENARIOS: list[dict[str, Any]] = [
"what this run proves and the smallest test that closes the gap." "what this run proves and the smallest test that closes the gap."
), ),
"required_signals": ["artifact", "next_action", "no_overclaim"], "required_signals": ["artifact", "next_action", "no_overclaim"],
"required_concepts": ["handler_not_telegram", "proof_tiers", "receipt"], "required_concepts": ["handler_not_telegram", "delivery_receipt"],
}, },
{ {
"id": "OOS-14", "id": "OOS-14",
@ -199,7 +211,12 @@ SCENARIOS: list[dict[str, Any]] = [
"and where does explicit approval begin? Explain only; do not ingest this prompt." "and where does explicit approval begin? Explain only; do not ingest this prompt."
), ),
"required_signals": ["authorization", "staging_or_review", "artifact", "no_overclaim"], "required_signals": ["authorization", "staging_or_review", "artifact", "no_overclaim"],
"required_concepts": ["staging_without_apply_authorization", "real_source_identity", "staged_review_apply"], "required_concepts": [
"staging_without_apply_authorization",
"real_source_identity",
"bounded_intake_tier",
"staged_review_apply",
],
}, },
{ {
"id": "OOS-15", "id": "OOS-15",
@ -251,6 +268,19 @@ CONCEPT_PATTERNS: dict[str, tuple[re.Pattern[str], ...]] = {
re.compile(r"render|sync", re.I), re.compile(r"render|sync", re.I),
re.compile(r"restart|session start|runtime injection", re.I), re.compile(r"restart|session start|runtime injection", re.I),
), ),
"identity_rows_beyond_claims": (
re.compile(r"personas?|strateg(?:y|ies)|beliefs?", re.I),
re.compile(r"identity|SOUL\.md", re.I),
re.compile(r"renderer|render automation|render/sync", re.I),
),
"candidate_before_canonical": (
re.compile(r"candidate|proposal|staging", re.I),
re.compile(
r"(?:nothing|no (?:candidate )?rows?).{0,80}public\.\*|"
r"not canonical.{0,80}(?:review|apply)|apply only after (?:review|approval)",
re.I | re.S,
),
),
"source_evidence_chain": ( "source_evidence_chain": (
re.compile(r"file|attachment|source_ref", re.I), re.compile(r"file|attachment|source_ref", re.I),
re.compile(r"public\.sources", re.I), re.compile(r"public\.sources", re.I),
@ -306,6 +336,11 @@ CONCEPT_PATTERNS: dict[str, tuple[re.Pattern[str], ...]] = {
re.compile(r"persist|durable|continuity", re.I), re.compile(r"persist|durable|continuity", re.I),
re.compile(r"restart.{0,80}(?:does not|doesn't|need not|not necessarily).{0,80}(?:erase|forget)", re.I | re.S), re.compile(r"restart.{0,80}(?:does not|doesn't|need not|not necessarily).{0,80}(?:erase|forget)", re.I | re.S),
), ),
"row_content_proof": (
re.compile(r"unchanged (?:counts?|totals?)", re.I),
re.compile(r"does not prove|doesn't prove|do not prove", re.I),
re.compile(r"row (?:IDs?|hashes?)|fingerprints?|timestamps?|balanced (?:insert|write|change)", re.I),
),
"proof_tiers": ( "proof_tiers": (
re.compile(r"handler|temporary[- ]profile|GatewayRunner", re.I), re.compile(r"handler|temporary[- ]profile|GatewayRunner", re.I),
re.compile(r"Telegram", re.I), re.compile(r"Telegram", re.I),
@ -317,9 +352,10 @@ CONCEPT_PATTERNS: dict[str, tuple[re.Pattern[str], ...]] = {
re.compile(r"do not duplicate|don'?t duplicate|one shared claim|single shared claim", re.I), re.compile(r"do not duplicate|don'?t duplicate|one shared claim|single shared claim", re.I),
), ),
"agent_specific_positions": ( "agent_specific_positions": (
re.compile(r"agent[- ]specific", re.I), re.compile(r"public\.beliefs", re.I),
re.compile(r"agent_id", re.I),
re.compile(r"belief|position|stance|confidence", re.I), re.compile(r"belief|position|stance|confidence", re.I),
re.compile(r"separate|elsewhere|per-agent", re.I), re.compile(r"no.{0,40}claim(?:-ID|_id).{0,30}(?:foreign key|link)|schema gap", re.I | re.S),
), ),
"forecast_history": ( "forecast_history": (
re.compile(r"original (?:probability|confidence)|60%|history", re.I), re.compile(r"original (?:probability|confidence)|60%|history", re.I),
@ -339,6 +375,10 @@ CONCEPT_PATTERNS: dict[str, tuple[re.Pattern[str], ...]] = {
re.compile(r"handler|GatewayRunner|temporary[- ]profile", re.I), re.compile(r"handler|GatewayRunner|temporary[- ]profile", re.I),
re.compile(r"did not post|posted nothing|not Telegram-visible|does not prove Telegram", re.I), re.compile(r"did not post|posted nothing|not Telegram-visible|does not prove Telegram", re.I),
), ),
"delivery_receipt": (
re.compile(r"visible reply|Telegram-visible reply", re.I),
re.compile(r"message ID|timestamp|readback", re.I),
),
"staging_without_apply_authorization": ( "staging_without_apply_authorization": (
re.compile(r"capture|hash|archive", re.I), re.compile(r"capture|hash|archive", re.I),
re.compile(r"stage|pending_review|proposal", re.I), re.compile(r"stage|pending_review|proposal", re.I),
@ -352,6 +392,10 @@ CONCEPT_PATTERNS: dict[str, tuple[re.Pattern[str], ...]] = {
re.compile(r"temporary label|chat label|proposal pointer|source_ref", re.I), re.compile(r"temporary label|chat label|proposal pointer|source_ref", re.I),
re.compile(r"not.{0,40}(?:a )?(?:source|provenance)|must not.{0,40}source|do not manufacture", re.I | re.S), re.compile(r"not.{0,40}(?:a )?(?:source|provenance)|must not.{0,40}source|do not manufacture", re.I | re.S),
), ),
"bounded_intake_tier": (
re.compile(r"build-only|local(?:ly)?|clone", re.I),
re.compile(r"not yet.{0,80}(?:live|autonomous|production)|not.{0,80}live-VPS", re.I | re.S),
),
"valid_supersession": ( "valid_supersession": (
re.compile(r"new|replacement", re.I), re.compile(r"new|replacement", re.I),
re.compile(r"supersedes", re.I), re.compile(r"supersedes", re.I),
@ -393,7 +437,7 @@ COUNT_INVARIANT_REJECTION_RE = re.compile(
SCHEMA_GAP_QUALIFIER_RE = re.compile( SCHEMA_GAP_QUALIFIER_RE = re.compile(
r"\b(?:proposed|future|not current|not shipped|does not exist|doesn't exist|absent|" r"\b(?:proposed|future|not current|not shipped|does not exist|doesn't exist|absent|"
r"has no|have no|no column|not an edge|would require|schema gap|must be added|" r"has no|have no|no column|not an edge|would require|schema gap|must be added|"
r"does not support|doesn't support|supports neither|not supported)\b", r"does not support|doesn't support|supports neither|not supported|must not|do not invent)\b",
re.I, re.I,
) )
CURRENT_SCHEMA_ASSERTION_PATTERNS: dict[str, re.Pattern[str]] = { CURRENT_SCHEMA_ASSERTION_PATTERNS: dict[str, re.Pattern[str]] = {
@ -477,8 +521,24 @@ TEMP_LABEL_AS_SOURCE_RE = re.compile(
r"(?:source_ref|public\.sources|canonical source)", r"(?:source_ref|public\.sources|canonical source)",
re.I | re.S, re.I | re.S,
) )
APPROVED_APPLY_ACTION_RE = re.compile(
r"approved.{0,240}(?:guarded )?apply|(?:guarded )?apply.{0,240}approved",
re.I | re.S,
)
CANONICAL_SOURCE_BEFORE_REVIEW_RE = re.compile(
r"(?:each|the|a) (?:artifact|PDF|document|tweet).{0,100}(?:gets|creates?|writes?).{0,40}"
r"public\.sources row.{0,80}before (?:any )?(?:claim is )?extract",
re.I | re.S,
)
APPLYABILITY_GAP_RE = re.compile(
r"approved_needs_apply_payload|worker_(?:contract_)?applyable(?:_count)?\s*[:=]?\s*(?:false|0)|"
r"(?:no|missing|without).{0,50}(?:strict )?apply_payload|"
r"normaliz(?:e|ation).{0,100}(?:before|then).{0,60}(?:review|apply)|"
r"not (?:directly )?applyable",
re.I | re.S,
)
DEFAULT_MAX_RESPONSE_WORDS = 300 DEFAULT_MAX_RESPONSE_WORDS = 220
MAX_RESPONSE_WORDS = {"OOS-09": 100, "OOS-10": 180} MAX_RESPONSE_WORDS = {"OOS-09": 100, "OOS-10": 180}
@ -558,6 +618,20 @@ def broad_semantic_issues(reply: str) -> list[str]:
return sorted(findings) return sorted(findings)
def proposal_readiness_issues(prompt_id: str, reply: str) -> list[str]:
if prompt_id not in {"OOS-01", "OOS-04", "OOS-07", "OOS-08"}:
return []
if APPROVED_APPLY_ACTION_RE.search(reply) and not APPLYABILITY_GAP_RE.search(reply):
return ["approved_proposal_applyability_overclaim"]
return []
def source_intake_issues(prompt_id: str, reply: str) -> list[str]:
if prompt_id == "OOS-02" and CANONICAL_SOURCE_BEFORE_REVIEW_RE.search(reply):
return ["canonical_source_created_before_review"]
return []
def score_reply(prompt: dict[str, Any], reply: str, *, memory_token: str) -> dict[str, Any]: def score_reply(prompt: dict[str, Any], reply: str, *, memory_token: str) -> dict[str, Any]:
legacy_score = base.score_reply(prompt, reply) legacy_score = base.score_reply(prompt, reply)
concepts = {concept: matched_concept(reply, concept) for concept in prompt["required_concepts"]} concepts = {concept: matched_concept(reply, concept) for concept in prompt["required_concepts"]}
@ -589,6 +663,8 @@ def score_reply(prompt: dict[str, Any], reply: str, *, memory_token: str) -> dic
source_evidence_issues = source_evidence_semantic_issues(reply) if prompt["id"] == "OOS-05" else [] source_evidence_issues = source_evidence_semantic_issues(reply) if prompt["id"] == "OOS-05" else []
behavioral_rule_issues = behavioral_rule_schema_issues(reply) if prompt["id"] == "OOS-06" else [] behavioral_rule_issues = behavioral_rule_schema_issues(reply) if prompt["id"] == "OOS-06" else []
semantic_issues = broad_semantic_issues(reply) semantic_issues = broad_semantic_issues(reply)
readiness_issues = proposal_readiness_issues(prompt["id"], reply)
intake_issues = source_intake_issues(prompt["id"], reply)
word_count = len(re.findall(r"\b\w+(?:[-']\w+)*\b", reply)) word_count = len(re.findall(r"\b\w+(?:[-']\w+)*\b", reply))
max_response_words = MAX_RESPONSE_WORDS.get(prompt["id"], DEFAULT_MAX_RESPONSE_WORDS) max_response_words = MAX_RESPONSE_WORDS.get(prompt["id"], DEFAULT_MAX_RESPONSE_WORDS)
response_too_long = word_count > max_response_words response_too_long = word_count > max_response_words
@ -606,6 +682,8 @@ def score_reply(prompt: dict[str, Any], reply: str, *, memory_token: str) -> dic
"source_evidence_semantic_issues": source_evidence_issues, "source_evidence_semantic_issues": source_evidence_issues,
"behavioral_rule_schema_issues": behavioral_rule_issues, "behavioral_rule_schema_issues": behavioral_rule_issues,
"broad_semantic_issues": semantic_issues, "broad_semantic_issues": semantic_issues,
"proposal_readiness_issues": readiness_issues,
"source_intake_issues": intake_issues,
"word_count": word_count, "word_count": word_count,
"max_response_words": max_response_words, "max_response_words": max_response_words,
"response_too_long": response_too_long, "response_too_long": response_too_long,
@ -618,6 +696,8 @@ def score_reply(prompt: dict[str, Any], reply: str, *, memory_token: str) -> dic
and not source_evidence_issues and not source_evidence_issues
and not behavioral_rule_issues and not behavioral_rule_issues
and not semantic_issues and not semantic_issues
and not readiness_issues
and not intake_issues
and not response_too_long and not response_too_long
), ),
} }

View file

@ -97,13 +97,31 @@ def test_vps_bridge_search_proposals_finds_approved_rows_by_payload(monkeypatch)
def fake_psql_json(_args, sql): def fake_psql_json(_args, sql):
captured_sql.append(sql) captured_sql.append(sql)
return [{"id": "a64df080-8502-42e2-98f4-9bbdecb8da73", "status": "approved"}] return [
{
"id": "a64df080-8502-42e2-98f4-9bbdecb8da73",
"status": "approved",
"proposal_type": "attach_evidence",
"payload": {},
}
]
monkeypatch.setattr(module, "psql_json", fake_psql_json) monkeypatch.setattr(module, "psql_json", fake_psql_json)
args = SimpleNamespace(query="Helmer 7 Powers", status="all", limit=20) args = SimpleNamespace(query="Helmer 7 Powers", status="all", limit=20)
result = module.search_proposals(args) result = module.search_proposals(args)
assert result["proposals"][0]["status"] == "approved" assert result["proposals"][0]["status"] == "approved"
assert result["proposals"][0]["readiness"] == {
"review_state": "approved_needs_apply_payload",
"has_apply_payload": False,
"worker_supported_type": True,
"worker_contract_applyable": False,
"production_worker_enabled": None,
"guidance": (
"This is proposal-contract readiness only. It does not prove the production apply worker is enabled, "
"the payload passes strict validation, or apply is authorized."
),
}
assert {"helmer", "powers"} <= set(result["terms"]) assert {"helmer", "powers"} <= set(result["terms"])
sql = captured_sql[0] sql = captured_sql[0]
assert "payload::text ilike any" in sql assert "payload::text ilike any" in sql
@ -136,6 +154,44 @@ def test_vps_bridge_proposal_list_prints_rationale_for_non_edge_rows(capsys) ->
assert "approved" in output assert "approved" in output
assert "applied_at: `-`" in output assert "applied_at: `-`" in output
assert "Revised Helmer packet" in output assert "Revised Helmer packet" in output
assert "approved_needs_apply_payload" in output
assert "worker_contract_applyable: `False`" in output
def test_vps_bridge_proposal_readiness_separates_contract_from_live_worker_state() -> None:
module = _load_module(BRIDGE_DIR / "kb_tool.py")
legacy_approved = module.classify_proposal_readiness(
{"status": "approved", "proposal_type": "approve_claim", "payload": {}}
)
strict_approved = module.classify_proposal_readiness(
{
"status": "approved",
"proposal_type": "approve_claim",
"payload": {"apply_payload": {"contract_version": 2}},
}
)
pending_strict = module.classify_proposal_readiness(
{
"status": "pending_review",
"proposal_type": "approve_claim",
"payload": {"apply_payload": {"contract_version": 2}},
}
)
unsupported_approved = module.classify_proposal_readiness(
{"status": "approved", "proposal_type": "change_policy", "payload": {}}
)
assert legacy_approved["review_state"] == "approved_needs_apply_payload"
assert legacy_approved["worker_contract_applyable"] is False
assert strict_approved["review_state"] == "approved_contract_present"
assert strict_approved["worker_contract_applyable"] is True
assert pending_strict["review_state"] == "needs_human_review"
assert pending_strict["worker_contract_applyable"] is False
assert unsupported_approved["review_state"] == "unsupported_by_apply_worker_contract"
assert unsupported_approved["worker_contract_applyable"] is False
assert strict_approved["production_worker_enabled"] is None
assert "does not prove the production apply worker is enabled" in strict_approved["guidance"]
def test_vps_bridge_decision_matrix_status_checks_schema_tables(monkeypatch) -> None: def test_vps_bridge_decision_matrix_status_checks_schema_tables(monkeypatch) -> None:
@ -225,8 +281,7 @@ def test_vps_bridge_status_prints_copyable_db_readback(capsys) -> None:
assert ( assert (
"DB readback: claims: `1837`; sources: `4145`; claim_edges: `4916`; " "DB readback: claims: `1837`; sources: `4145`; claim_edges: `4916`; "
"claim_evidence: `4670`; kb_proposals: `26`." "claim_evidence: `4670`; kb_proposals: `26`." in capsys.readouterr().out
in capsys.readouterr().out
) )
@ -257,8 +312,7 @@ def test_cloudsql_status_prints_copyable_canonical_db_readback(capsys) -> None:
assert ( assert (
"DB readback: claims: `1837`; sources: `4145`; claim_edges: `4916`; " "DB readback: claims: `1837`; sources: `4145`; claim_edges: `4916`; "
"claim_evidence: `4670`; kb_proposals: `26`." "claim_evidence: `4670`; kb_proposals: `26`." in capsys.readouterr().out
in capsys.readouterr().out
) )
@ -301,12 +355,8 @@ def test_kb_bridges_emit_public_claim_links_for_telegram_rendering() -> None:
module = _load_module(BRIDGE_DIR / filename) module = _load_module(BRIDGE_DIR / filename)
assert module.claim_url(claim_id) == expected assert module.claim_url(claim_id) == expected
assert module.markdown_claim_link(claim_id, "d3fb892b") == ( assert module.markdown_claim_link(claim_id, "d3fb892b") == (f"[`d3fb892b`]({expected})")
f"[`d3fb892b`]({expected})" assert module.markdown_claim_link(claim_id, "claim `with ticks`") == (f"[`claim 'with ticks'`]({expected})")
)
assert module.markdown_claim_link(claim_id, "claim `with ticks`") == (
f"[`claim 'with ticks'`]({expected})"
)
def test_vps_bridge_markdown_links_claim_text_and_edges() -> None: def test_vps_bridge_markdown_links_claim_text_and_edges() -> None:
@ -344,14 +394,8 @@ def test_vps_bridge_markdown_links_claim_text_and_edges() -> None:
module.print_claim_bundle(data) module.print_claim_bundle(data)
markdown = buffer.getvalue() markdown = buffer.getvalue()
assert ( assert f"[`Claims should be easy to scan in Telegram.`](https://leo.livingip.xyz/kb/claims/{claim_id})" in markdown
f"[`Claims should be easy to scan in Telegram.`](https://leo.livingip.xyz/kb/claims/{claim_id})" assert f"- open full claim/body/edges: [`claim page`](https://leo.livingip.xyz/kb/claims/{claim_id})" in markdown
in markdown
)
assert (
f"- open full claim/body/edges: [`claim page`](https://leo.livingip.xyz/kb/claims/{claim_id})"
in markdown
)
assert ( assert (
f"[`Full claim pages expose body, evidence, and graph edges.`](https://leo.livingip.xyz/kb/claims/{connected_id})" f"[`Full claim pages expose body, evidence, and graph edges.`](https://leo.livingip.xyz/kb/claims/{connected_id})"
in markdown in markdown

View file

@ -4,6 +4,15 @@ ROOT = Path(__file__).resolve().parents[1]
SKILL_ROOT = ROOT / "hermes-agent" / "leoclean-skills" SKILL_ROOT = ROOT / "hermes-agent" / "leoclean-skills"
def _kb_skill_bundle(surface: str) -> str:
root = SKILL_ROOT / surface / "teleo-kb-bridge"
parts = [(root / "SKILL.md").read_text()]
reference = root / "REFERENCE.md"
if reference.exists():
parts.append(reference.read_text())
return "\n".join(parts)
def test_gcp_kb_skill_uses_cloudsql_bridge_not_vps_docker() -> None: def test_gcp_kb_skill_uses_cloudsql_bridge_not_vps_docker() -> None:
text = (SKILL_ROOT / "gcp" / "teleo-kb-bridge" / "SKILL.md").read_text() text = (SKILL_ROOT / "gcp" / "teleo-kb-bridge" / "SKILL.md").read_text()
squashed = " ".join(text.split()) squashed = " ".join(text.split())
@ -62,7 +71,7 @@ def test_gcp_kb_skill_uses_cloudsql_bridge_not_vps_docker() -> None:
def test_vps_kb_skill_keeps_vps_scope_explicit() -> None: def test_vps_kb_skill_keeps_vps_scope_explicit() -> None:
text = (SKILL_ROOT / "vps" / "teleo-kb-bridge" / "SKILL.md").read_text() text = _kb_skill_bundle("vps")
squashed = " ".join(text.split()) squashed = " ".join(text.split())
assert "VPS production leoclean" in text assert "VPS production leoclean" in text
@ -142,7 +151,7 @@ def test_gcp_kb_skill_keeps_claim_links_and_backtick_rendering() -> None:
def test_leoclean_kb_skills_anchor_external_doctrine_in_target_project_language() -> None: def test_leoclean_kb_skills_anchor_external_doctrine_in_target_project_language() -> None:
for surface in ("vps", "gcp"): for surface in ("vps", "gcp"):
text = (SKILL_ROOT / surface / "teleo-kb-bridge" / "SKILL.md").read_text() text = _kb_skill_bundle(surface)
assert "External Doctrine Contributions" in text assert "External Doctrine Contributions" in text
assert "Use the target project's native language first" in text assert "Use the target project's native language first" in text
@ -154,7 +163,7 @@ def test_leoclean_kb_skills_anchor_external_doctrine_in_target_project_language(
def test_leoclean_kb_skills_distinguish_canonical_evidence_from_provenance_quality() -> None: def test_leoclean_kb_skills_distinguish_canonical_evidence_from_provenance_quality() -> None:
for surface in ("vps", "gcp"): for surface in ("vps", "gcp"):
text = (SKILL_ROOT / surface / "teleo-kb-bridge" / "SKILL.md").read_text() text = _kb_skill_bundle(surface)
squashed = " ".join(text.split()) squashed = " ".join(text.split())
assert "is canonical evidence even when that source row has no `url` or `storage_path`" in squashed assert "is canonical evidence even when that source row has no `url` or `storage_path`" in squashed
@ -167,7 +176,7 @@ def test_leoclean_kb_skills_distinguish_canonical_evidence_from_provenance_quali
def test_leoclean_kb_skills_use_existing_behavioral_rule_storage_and_separate_apply() -> None: def test_leoclean_kb_skills_use_existing_behavioral_rule_storage_and_separate_apply() -> None:
for surface in ("vps", "gcp"): for surface in ("vps", "gcp"):
text = (SKILL_ROOT / surface / "teleo-kb-bridge" / "SKILL.md").read_text() text = _kb_skill_bundle(surface)
squashed = " ".join(text.split()) squashed = " ".join(text.split())
assert "existing `public.behavioral_rules` table" in squashed assert "existing `public.behavioral_rules` table" in squashed
@ -180,7 +189,7 @@ def test_leoclean_kb_skills_use_existing_behavioral_rule_storage_and_separate_ap
def test_leoclean_kb_skills_separate_runtime_session_and_delivery_proof_tiers() -> None: def test_leoclean_kb_skills_separate_runtime_session_and_delivery_proof_tiers() -> None:
for surface in ("vps", "gcp"): for surface in ("vps", "gcp"):
text = (SKILL_ROOT / surface / "teleo-kb-bridge" / "SKILL.md").read_text() text = _kb_skill_bundle(surface)
squashed = " ".join(text.split()) squashed = " ".join(text.split())
assert "keep the whole reply under 220 words" in squashed assert "keep the whole reply under 220 words" in squashed
@ -212,7 +221,27 @@ def test_vps_skills_enforce_compact_default_answers_without_losing_detail_on_req
assert "final reply must be at most 220 words" in kb_squashed assert "final reply must be at most 220 words" in kb_squashed
assert "smaller word or line limit is mandatory" in kb_squashed assert "smaller word or line limit is mandatory" in kb_squashed
assert "no more than three short bullets" in kb_squashed assert "at most three short bullets" in kb_squashed
assert "Do not mirror this skill" in kb_squashed assert "Do not mirror this skill" in kb_squashed
assert "Broad or multi-part questions still receive one compressed answer" in telegram_squashed assert "Broad or multi-part questions still receive one compressed answer" in telegram_squashed
assert "explicitly requests a detailed audit or long-form document" in telegram_squashed assert "explicitly requests a detailed audit or long-form document" in telegram_squashed
def test_vps_kb_skill_encodes_apply_readiness_beliefs_and_current_proof_boundaries() -> None:
text = (SKILL_ROOT / "vps" / "teleo-kb-bridge" / "SKILL.md").read_text()
squashed = " ".join(text.split())
assert "approved_needs_apply_payload" in text
assert "normalize into a strict payload and review that new packet" in squashed
assert "never tell the operator to apply the legacy row" in squashed
assert "search` searches claims and identity context, not canonical source rows" in squashed
assert "an existing canonical link is still canonical evidence" in squashed
assert "Current agent-owned positions live in `public.beliefs`" in squashed
for field in ("`agent_id`", "`level`", "`statement`", "`falsifier`", "`rank`"):
assert field in text
assert "has no claim-ID foreign key" in squashed
assert "neither is generic stance storage" in squashed
assert "source compiler is build-only and locally proven" in squashed
assert "No active general DB-to-`SOUL.md` renderer automation is currently proven" in squashed
assert "Unchanged table totals also do not prove unchanged rows" in squashed
assert "current `public.claim_edges` has no rationale field" in squashed

View file

@ -20,18 +20,21 @@ def good_reply(prompt_id: str, token: str) -> str:
"Approved is not the same as applied. I checked the current Postgres canonical public.claims, " "Approved is not the same as applied. I checked the current Postgres canonical public.claims, "
"public.sources, public.claim_edges, public.claim_evidence and kb_stage proposal tables. I cannot claim a " "public.sources, public.claim_edges, public.claim_evidence and kb_stage proposal tables. I cannot claim a "
"canonical change without row-level before/after readback, applied_at, and postflight proof. The next action " "canonical change without row-level before/after readback, applied_at, and postflight proof. The next action "
"is a guarded review and apply packet with a receipt. " "depends on proposal readiness: approved_needs_apply_payload means normalize a strict payload and review it "
"before any apply; contract presence does not prove the production worker is enabled or authorized. "
) )
if prompt_id == "OOS-02": if prompt_id == "OOS-02":
return common + ( return common + (
"Telegram file refs, document_evaluations, proposal source_ref, raw PDF files, and public.sources are " "Telegram file refs, document_evaluations, proposal source_ref, raw PDF files, and public.sources are "
"different layers. I would preserve source provenance, deduplicate claims, retain contradictory evidence " "different layers. I would preserve source provenance, deduplicate claims, retain contradictory evidence "
"as caveats, stage a proposal for review, then apply only after approval." "as caveats, and keep source/claim candidates in staging; no candidate rows enter public.* before review. "
"I would stage a proposal, then apply only after approval."
) )
if prompt_id == "OOS-03": if prompt_id == "OOS-03":
return common + ( return common + (
"SOUL.md is a runtime artifact rendered from DB-first identity rows in Postgres. A direct SOUL.md edit " "SOUL.md is a runtime artifact. Canonical identity may include personas, strategies, and public.beliefs, "
"does not change canonical identity; verify canonical rows, render/sync, restart, and compare the artifact." "not only claims. A direct SOUL.md edit does not change canonical identity; no active general renderer "
"automation is proven, so verify identity rows, render/sync, restart, and compare the artifact."
) )
if prompt_id == "OOS-05": if prompt_id == "OOS-05":
return common + ( return common + (
@ -70,7 +73,7 @@ def good_reply(prompt_id: str, token: str) -> str:
if prompt_id == "OOS-10": if prompt_id == "OOS-10":
return ( return (
"Postgres is the canonical database, but an unchanged count receipt does not prove answer behavior is " "Postgres is the canonical database, but an unchanged count receipt does not prove answer behavior is "
"unchanged. " "unchanged, and unchanged counts do not prove unchanged rows; check row IDs, timestamps, or fingerprints. "
"Deployed skills, runtime configuration, rendered SOUL.md, session state, and conversation context also " "Deployed skills, runtime configuration, rendered SOUL.md, session state, and conversation context also "
"affect replies. Hermes state.db and session JSONL provide durable continuity, so a restart does not " "affect replies. Hermes state.db and session JSONL provide durable continuity, so a restart does not "
"necessarily erase every prior-session fact. Proof tiers are separate: a temporary-profile handler run, " "necessarily erase every prior-session fact. Proof tiers are separate: a temporary-profile handler run, "
@ -79,8 +82,9 @@ def good_reply(prompt_id: str, token: str) -> str:
if prompt_id == "OOS-11": if prompt_id == "OOS-11":
return common + ( return common + (
"Use one shared claim with shared sources and evidence; do not duplicate the factual claim per agent. " "Use one shared claim with shared sources and evidence; do not duplicate the factual claim per agent. "
"Store agent-specific beliefs, positions, stances, or confidence separately. Preserve each rationale and " "Store agent-specific positions in public.beliefs using agent_id, level, statement, confidence, falsifier, "
"retain disagreement as a caveat or contradiction edge so it remains queryable." "rank, and status. It has no claim-ID foreign key, so an exact belief-to-shared-claim link is a schema gap. "
"Contradictory factual conclusions may remain separate claims joined by a contradicts edge."
) )
if prompt_id == "OOS-12": if prompt_id == "OOS-12":
return common + ( return common + (
@ -94,14 +98,15 @@ def good_reply(prompt_id: str, token: str) -> str:
"No. The temporary-profile GatewayRunner handler run posted nothing to Telegram, so it is not " "No. The temporary-profile GatewayRunner handler run posted nothing to Telegram, so it is not "
"Telegram-visible delivery proof. It proves handler execution and reply behavior only. Canonical database " "Telegram-visible delivery proof. It proves handler execution and reply behavior only. Canonical database "
"mutation is a third tier requiring public.* row and applied proposal receipts. The next action is one " "mutation is a third tier requiring public.* row and applied proposal receipts. The next action is one "
"authorized Telegram-visible prompt with message and reply readback while confirming no DB count change." "authorized Telegram-visible prompt with a visible reply, Telegram message ID, and timestamp readback "
"while confirming no DB count change."
) )
if prompt_id == "OOS-14": if prompt_id == "OOS-14":
return common + ( return common + (
"Leo may capture the retained artifact, URL or storage path, and file/content hash; extract candidates, " "The build-only local source compiler may capture a retained artifact, URL or storage path, and hash; "
"deduplicate them, record contradictions, and stage a pending_review proposal without canonical-apply " "extract candidates and produce a pending_review packet without canonical-apply approval. It is not yet "
"approval. A temporary chat label or source_ref is not real source provenance. Explicit operator " "an autonomous live-VPS intake path. A temporary chat label or source_ref is not real provenance. Explicit "
"authorization begins at the guarded canonical apply path, after review." "operator authorization begins at guarded canonical apply, after review."
) )
if prompt_id == "OOS-15": if prompt_id == "OOS-15":
return common + ( return common + (
@ -309,6 +314,45 @@ def test_oos_runtime_case_rejects_db_only_causality_and_total_memory_erasure() -
] ]
def test_oos_direct_apply_case_rejects_approved_rows_without_readiness_gap() -> None:
token = "demo-ledger-deadbeef"
prompt = benchmark.prompt_catalog(token)[3]
bad_reply = (
"No. Three proposals are approved with applied_at NULL. "
"Next proof-changing follow-up: authorize and run guarded apply for all three approved proposals."
)
bad = benchmark.score_reply(prompt, bad_reply, memory_token=token)
assert bad["pass"] is False
assert bad["proposal_readiness_issues"] == ["approved_proposal_applyability_overclaim"]
repaired = bad_reply + (
" They are approved_needs_apply_payload, so normalize a strict payload and review it before any apply."
)
assert benchmark.proposal_readiness_issues(prompt["id"], repaired) == []
def test_oos_agent_position_case_requires_current_public_beliefs_contract() -> None:
token = "demo-ledger-deadbeef"
prompt = benchmark.prompt_catalog(token)[10]
wrong = (
"Use one shared claim with shared evidence. Store agent-specific positions in reasoning_tools or "
"behavioral_rules, then use a contradicts edge between claims."
)
score = benchmark.score_reply(prompt, wrong, memory_token=token)
assert score["pass"] is False
assert score["concepts"]["agent_specific_positions"] is False
def test_oos_source_intake_rejects_canonical_source_creation_during_extraction() -> None:
reply = (
"Each PDF gets a public.sources row before any claim is extracted. Later I stage candidates for review and "
"apply them only after approval."
)
assert benchmark.source_intake_issues("OOS-02", reply) == ["canonical_source_created_before_review"]
def test_oos_handler_case_rejects_telegram_live_overclaim() -> None: def test_oos_handler_case_rejects_telegram_live_overclaim() -> None:
token = "demo-ledger-deadbeef" token = "demo-ledger-deadbeef"
prompt = benchmark.prompt_catalog(token)[12] prompt = benchmark.prompt_catalog(token)[12]