Sample 08 — PostgreSQL StorageBundle
This sample shows how to attach the canonical DIR StorageBundle to PostgreSQL using only samples.shared.bootstrap.setup_environment (Sample Development Guide §2, §10). Topology is classic: one agent, one decision flow (new_dfid), ROA in user space, DIM in kernel space, rows in decision_audit_events via DecisionRuntime.audit.
Mechanisms used: setup_environment, DecisionRuntime, ContextStore / AgentRegistry (through the runtime facade), ROA stages Explain → Policy → Self-Check → Proposal, evaluate_proposal, SIMULATION_START / SIMULATION_END, AGENT_DECISION.
Use cases
---
title: Sample 08 — operator flow
config:
layout: elk
theme: neutral
look: classic
---
flowchart TB
classDef userSpace fill:#E8EAF6,stroke:#3F51B5,stroke-width:2px,color:#1A237E,font-weight:bold;
classDef kernelSpace fill:#E8F5E9,stroke:#388E3C,stroke-width:2px,color:#1B5E20,font-weight:bold;
classDef infraSpace fill:#FFF3E0,stroke:#F57C00,stroke-width:2px,color:#E65100,font-weight:bold;
A(["`**Operator / learner**`"]):::userSpace --> B["`**Run sample 08**`"]:::userSpace
B --> C["`**DIR kernel + PostgreSQL**`"]:::kernelSpace
C --> D["`**Audit trail**<br/>decision_audit_events`"]:::infraSpace
Architecture
---
title: Sample 08 — User space, wall, kernel space
config:
layout: elk
theme: neutral
look: classic
---
flowchart TB
classDef userSpace fill:#E8EAF6,stroke:#3F51B5,stroke-width:2px,color:#1A237E,font-weight:bold;
classDef kernelSpace fill:#E8F5E9,stroke:#388E3C,stroke-width:2px,color:#1B5E20,font-weight:bold;
classDef infraSpace fill:#FFF3E0,stroke:#F57C00,stroke-width:2px,color:#E65100,font-weight:bold;
subgraph US["`**User space**`"]
LLM["`**LLM client**`"]:::userSpace
AG["`**agent.run_roa_cycle**`"]:::userSpace
end
subgraph WALL["`**The wall**`"]
DIM["`**DIM**<br/>evaluate_proposal`"]:::kernelSpace
end
subgraph KS["`**Kernel space**`"]
RT["`**DecisionRuntime**`"]:::kernelSpace
PG[("`**PostgreSQL**`")]:::infraSpace
end
LLM --> AG
AG --> DIM
DIM --> RT
RT --> PG
style US fill:#FAFAFA,stroke:#3F51B5,stroke-width:3px
style WALL fill:#FAFAFA,stroke:#388E3C,stroke-width:3px
style KS fill:#FAFAFA,stroke:#F57C00,stroke-width:3px
Execution flow
---
title: Sample 08 — one decision end-to-end
config:
layout: elk
theme: neutral
look: classic
---
sequenceDiagram
participant R as run.py
participant B as setup_environment
participant RT as DecisionRuntime
participant A as agent ROA
participant D as DIM
participant P as PostgreSQL
R->>B: load config + open StorageBundle
B->>P: connect + apply_schema
R->>RT: register_agent
RT->>P: agent_registry upsert
R->>R: SIMULATION_START (dfid = simulation_id)
R->>A: Explain / Policy / Self-Check
A-->>R: PolicyProposal
R->>D: evaluate_proposal
D->>P: optional kernel audit rows when enabled
R->>P: AGENT_DECISION telemetry
R->>R: SIMULATION_END
How to run
pip install -e .
pip install pyyaml psycopg2-binary
Create a database and user once (example names match config.yaml; override with DB_*):
createuser dir_user
createdb -O dir_user dir_quickstart
Set a password through the environment — do not commit secrets:
set DB_PASS=your_secret
python samples/08_custom_repo_psql/run.py
Ollama (live LLM): ensure llm_defaults points at your instance and the model is pulled.
Gemini: set llm_defaults.provider: gemini, supply an API key via env (GOOGLE_API_KEY or GEMINI_API_KEY), and keep PostgreSQL configured as above.
Mock LLM (no network, no API key):
set USE_MOCK_LLM=1
python samples/08_custom_repo_psql/run.py
Configuration
Annotated excerpt — full file is config.yaml.
database.provider: postgres— selectssamples.shared.storage.pg_repo. Host, port, dbname, user, and optionalpasswordcome from YAML but are overridden whenDB_HOST,DB_PORT,DB_NAME,DB_USER, orDB_PASSare set.simulation.run_id— used assimulation_idin telemetry payloads (Guide §9.4).simulation.seeds— recorded onSIMULATION_START; keep keys here for any seeded behaviour (this sample uses a deterministic mock strategy without RNG).demo.note— short text placed intocontext_sessionunderinputfor the ROA prompts.llm_defaults— same resolution rules as other samples (USE_MOCK_LLM, provider).contracts.provider: yaml— loadsResponsibilityContractobjects from the same file as bootstrap’sconfig_path; do not setcontracts.pathto a bareconfig.yamlunless your working directory is the sample folder (Guide §7).agents— each row suppliesagent_id,priority, governance metadata (owner,version,effective_from, optionaleffective_until,approved_by) copied intoSIMULATION_START.details.agents[], plus nestedcontractfor the kernel model. Handshakeagent_versioncomes fromagents[].version(fallback:agent_versionat file root if present).
Database storage
| Domain meaning | Canonical surface | Event / table |
|---|---|---|
| Run envelope | decision_audit_events |
SIMULATION_START (topology, llm_backend, agents[], seeds, timestamps), SIMULATION_END (status, error_message, elapsed_seconds, decisions_total, executions_total; dfid = simulation_id) |
| Decision narrative | decision_audit_events |
AGENT_DECISION |
| Agent row | agent_registry |
Written by register_agent / handshake |
| Working context | context_session |
Merged JSON per dfid |
Filter a run by simulation_id (PostgreSQL):
SELECT dfid, event, detail_json
FROM decision_audit_events
WHERE detail_json->>'simulation_id' = 'psql_repo_demo_001'
ORDER BY id;
Same intent on SQLite (if you ever point the sample at SQLite):
SELECT dfid, event, detail_json
FROM decision_audit_events
WHERE json_extract(detail_json, '$.simulation_id') = 'psql_repo_demo_001'
ORDER BY id;
Expected output
You should see INFO lines tagged with a DFID, a DIM line (ACCEPT or REJECT for the single HOLD proposal), and a closing summary similar to:
SUMMARY / 08_custom_repo_psql simulation_id=psql_repo_demo_001 elapsed=…s DIM=ValidationVerdict.ACCEPT
DDL and wiring notes
PostgreSQL DDL lives in samples/shared/storage/pg_schema.sql and is applied by apply_schema inside open_storage_bundle — there is no duplicate schema.sql under this sample directory.