212 lines
12 KiB
Markdown
212 lines
12 KiB
Markdown
# Reproducible Leo identity
|
|
|
|
## Definition of Working
|
|
|
|
- **Working target:** generate one pinned Leo identity manifest, compile a
|
|
disposable profile, answer the fixed identity query in a fresh process,
|
|
restart in a second process, and reject any drift before an answer.
|
|
- **Operator path:** run
|
|
`python -m scripts.run_leo_identity_reconstruction_canary` with the committed
|
|
identity fixtures from a clean checkout.
|
|
- **Done means:** the T2 receipt reports two distinct stopped processes with the
|
|
same manifest, identity inputs, query, answer hash, and output provenance;
|
|
the second process starts from a newly compiled empty profile; the drift
|
|
attempt exits before answering; every process group and temporary profile is
|
|
removed.
|
|
- **Not done:** a hand-edited `SOUL.md`, a manifest self-hash, unit tests without
|
|
process restart, or prior VPS restart evidence that did not reconstruct from
|
|
this manifest.
|
|
- **Required tier:** `T2_runtime`. T3 VPS restart/readback is a post-merge
|
|
graduation target.
|
|
|
|
## Identity input inventory
|
|
|
|
`GatewayRunner` and the surrounding Hermes profile can change an answer through
|
|
the following surfaces. The manifest either pins each surface or explicitly
|
|
keeps it outside identity authority.
|
|
|
|
| Surface | Binding | Authority |
|
|
| --- | --- | --- |
|
|
| Model provider, route, limits, and reasoning settings | secret-free semantic config hash | runtime input; the actual model remains a per-turn receipt |
|
|
| Hosted model weights | provider-managed, explicitly not locally hashable | never claimed as a local hash |
|
|
| Hermes and Teleo code | clean Git commits plus executable source-tree hashes | runtime input |
|
|
| Python ABI and imported runtime packages | exact implementation/version and curated package versions | runtime input |
|
|
| Skills, plugins, and database tools | content hashes | runtime input |
|
|
| Gateway/tool permissions | strict allowlisted config hash plus exact no-send/read-only policy | runtime input; this local compiler does not claim an authorizer readback |
|
|
| Static constitutional rules | committed source path, byte hash, and semantic hash | static authority |
|
|
| Database snapshot version | database/user/system identity plus content, row, structure, and capture-provenance hashes; only WAL position is excluded | T2 pins a noncanonical fixture; it cannot grant canonical authority |
|
|
| Database-derived identity rows | typed table/row/kind/rank/evidence bindings plus source mode | synthetic fixtures are explicitly noncanonical; T3 requires independently verified database-exporter output |
|
|
| `SOUL.md` and `identity.json` | deterministic compiled-view hashes | generated views, never authorities |
|
|
| Sessions, state, and memory | excluded from the identity input hash and labeled temporary | continuity only; never evidence |
|
|
|
|
The disposable profile is compiled from a strict non-secret configuration
|
|
allowlist; unrecognized transport/credential fields are not copied. Credential
|
|
values are omitted rather than hashed. Rotating a bot token changes the broad
|
|
operational behavior observation but does not change Leo's identity.
|
|
Changing a non-secret permission, model setting, skill, code file, database
|
|
fingerprint, constitutional rule, database identity row, or compiled view does
|
|
change a pinned input and fails closed.
|
|
|
|
### Current `GatewayRunner` consumption map
|
|
|
|
The T2 compiler contract was checked against the current live runtime source.
|
|
This inventory defines what the post-merge T3 receipt must observe rather than
|
|
silently assuming that a profile file is the whole prompt:
|
|
|
|
- Startup resolves `-p leoclean` to `HERMES_HOME`, then loads profile and project
|
|
environment files before `config.yaml`; environment expansion, legacy
|
|
`gateway.json`, and defaults can change the effective configuration. The T2
|
|
compiler therefore enforces an exact top-level profile allowlist (including
|
|
rejection of dangling symlinks) rather than relying on a filename denylist.
|
|
- Routing consumes the requested default model, the top-level
|
|
`smart_model_routing` switch, provider endpoints/defaults, auth pool choice,
|
|
reasoning settings, token/turn limits, and fallbacks. The pinned top-level
|
|
routing switch must be a boolean; arbitrary nested routing data is rejected.
|
|
Credentials are runtime capability, not identity, and their values are never
|
|
retained.
|
|
- Prompt construction consumes generated `SOUL.md`, the gateway/agent system
|
|
message, persistent `MEMORY.md`/`USER.md`, compiled skills, project-context
|
|
files, current time/timezone, and the effective model/provider. T2 requires
|
|
memory and project context absent; T3 records the effective system-prompt and
|
|
compiled-skills-prompt hashes.
|
|
- Plugin discovery can include profile plugins, optional project plugins, and
|
|
installed entry points. The live database-context hook can inject a
|
|
query-bound contract before the model and can reject or replace the reply
|
|
afterward, so T3 retains plugin registry, contract, retrieval, and delivered
|
|
response hashes.
|
|
- Effective tools come from platform toolsets and the runtime registry, not a
|
|
descriptive fixture field. T3 must read back exact tool schemas, prove the
|
|
send tool absent, and keep terminal restricted to the clone-bound read-only
|
|
wrapper.
|
|
- Authorization consumes chat/user allowlists and pairing-store state. T2 starts
|
|
with pairing absent; T3 records the fixed synthetic source and the actual
|
|
authorization decision without exposing IDs or tokens.
|
|
- Session keys consume platform/chat/user/thread/topic fields. Auto-skill,
|
|
reply/media/file context, persisted transcripts, and a reused system prompt
|
|
can all change a turn. Verification therefore happens before every child
|
|
starts, and T3 binds session key, persisted session ID, message-context
|
|
absence, and prompt hashes.
|
|
|
|
The committed T2 database/identity inputs are synthetic fixtures and the
|
|
compiled view labels them `synthetic_noncanonical_fixture`; they do not stand
|
|
in for approved production rows. This T2 schema never grants canonical
|
|
authority from caller-supplied or merely self-hashed JSON. T3 must use output
|
|
that is independently verified by the database-owning same-transaction exporter
|
|
and bound to the database fingerprint, database user, system identifier, and
|
|
row digest.
|
|
|
|
Every pinned source tree and profile component is structurally revalidated:
|
|
the verifier recomputes its file count, total bytes, sorted unique paths, and
|
|
canonical content hash, and rejects missing files, unreadable entries, or
|
|
symlinks. Rehashing forged structural metadata cannot make it authoritative.
|
|
|
|
The current live Hermes checkout is not clean, so its Git SHA alone is not a
|
|
reproducible source bundle. This T2 receipt intentionally uses the committed
|
|
clean local runtime and the committed database/identity snapshot fixture. It
|
|
does not claim to reconstruct the current dirty VPS runtime. T3 must run only
|
|
after the merged identity code is deployed and must bind the deployed clean
|
|
content (or a content-addressed dirty-source bundle if the live checkout has not
|
|
yet been repaired).
|
|
|
|
## Commands
|
|
|
|
The complete canary is the preferred operator command:
|
|
|
|
```bash
|
|
python -m scripts.run_leo_identity_reconstruction_canary \
|
|
--profile-template fixtures/working-leo/leo-identity-v1/profile-template \
|
|
--hermes-root fixtures/working-leo/leo-identity-v1/hermes-runtime \
|
|
--deployment-root . \
|
|
--source-root . \
|
|
--database-fingerprint fixtures/working-leo/leo-identity-v1/leo-database-fingerprint-v1.json \
|
|
--constitution fixtures/working-leo/leo-identity-v1/leo-constitution-v1.json \
|
|
--database-identity fixtures/working-leo/leo-identity-v1/leo-database-identity-v1.json \
|
|
--output docs/reports/leo-working-state-20260709/leo-reproducible-identity-t2-current.json
|
|
```
|
|
|
|
The negative lifecycle is separately runnable. It proves drift is caught before
|
|
the second process starts:
|
|
|
|
```bash
|
|
python -m scripts.run_leo_identity_reconstruction_canary \
|
|
--profile-template fixtures/working-leo/leo-identity-v1/profile-template \
|
|
--hermes-root fixtures/working-leo/leo-identity-v1/hermes-runtime \
|
|
--deployment-root . \
|
|
--source-root . \
|
|
--database-fingerprint fixtures/working-leo/leo-identity-v1/leo-database-fingerprint-v1.json \
|
|
--constitution fixtures/working-leo/leo-identity-v1/leo-constitution-v1.json \
|
|
--database-identity fixtures/working-leo/leo-identity-v1/leo-database-identity-v1.json \
|
|
--inject-drift-before-restart \
|
|
--output /tmp/leo-identity-drift-rejection.json
|
|
```
|
|
|
|
For inspection, the lifecycle can be decomposed into explicit manifest and
|
|
compile commands:
|
|
|
|
```bash
|
|
python -m scripts.leo_behavior_manifest \
|
|
--profile fixtures/working-leo/leo-identity-v1/profile-template \
|
|
--hermes-root fixtures/working-leo/leo-identity-v1/hermes-runtime \
|
|
--deployment-root . \
|
|
--output /tmp/leo-behavior.json
|
|
|
|
python -m scripts.leo_identity_manifest generate \
|
|
--behavior-manifest /tmp/leo-behavior.json \
|
|
--database-fingerprint fixtures/working-leo/leo-identity-v1/leo-database-fingerprint-v1.json \
|
|
--constitution fixtures/working-leo/leo-identity-v1/leo-constitution-v1.json \
|
|
--database-identity fixtures/working-leo/leo-identity-v1/leo-database-identity-v1.json \
|
|
--source-root . \
|
|
--output /tmp/leo-identity-manifest.json
|
|
|
|
python -m scripts.leo_identity_profile compile \
|
|
--manifest /tmp/leo-identity-manifest.json \
|
|
--source-root . \
|
|
--profile-template fixtures/working-leo/leo-identity-v1/profile-template \
|
|
--profile /tmp/leo-disposable-profile \
|
|
--hermes-root fixtures/working-leo/leo-identity-v1/hermes-runtime \
|
|
--deployment-root .
|
|
```
|
|
|
|
All commands require a clean Git worktree because a commit plus an unretained
|
|
dirty source tree is not reproducible.
|
|
|
|
Child processes execute the real interpreter and temporary-profile paths only
|
|
in process-local memory. Retained receipts persist a repo-relative
|
|
`logical_command`, the stable `<python>` and `<temporary-profile>` placeholders,
|
|
and an explicit `profile_role`; they never serialize the checkout, interpreter,
|
|
or temporary directory path.
|
|
|
|
## State inventory and transitions
|
|
|
|
| State | Meaning | Next valid transition |
|
|
| --- | --- | --- |
|
|
| `inputs_unverified` | source paths exist but are not yet bound | validate clean Git, runtime, DB, rules, rows, and permissions |
|
|
| `manifest_pinned` | every material identity input has a stable hash | compile deterministic views |
|
|
| `profile_compiled` | static profile copied and generated views match the manifest | verify immediately before start |
|
|
| `runtime_verified` | runtime/code/view hashes match and session state is non-authoritative | answer fixed query |
|
|
| `stopped_cleanly` | child and process group are absent | compile a new empty profile from the manifest |
|
|
| `restart_reproduced` | child from the newly compiled profile has the same identity/query/provenance inputs | retain receipt and clean every profile |
|
|
| `drift_detected` | any input or generated view differs | fail closed; no answer and no next start |
|
|
|
|
The irreversible boundary is not a database or production mutation: this T2
|
|
canary is a local compiler/process runtime, no-send, database-read-only, and
|
|
disposable. The successful-restart receipt reaches `T2_runtime`; the standalone
|
|
pre-restart drift receipt is a passing negative component but reports
|
|
`T1_model` and `goal_tier_satisfied=false`. Neither proves the live VPS
|
|
`GatewayRunner`, Telegram delivery, hosted-model behavior, production database
|
|
state, or T3 restart recovery.
|
|
|
|
Here `T2_runtime` uses the required Capability Tier Proof vocabulary: local
|
|
runtime behavior with restart. It does not imply Hermes/GatewayRunner or a
|
|
hosted model; those exclusions are explicit in the receipt's `runtime_variant`,
|
|
`tier_basis`, and `claim_ceiling`.
|
|
|
|
## T3 graduation
|
|
|
|
After merge and rollback proof, extend the schema with independent verification
|
|
of the database-owning same-transaction exporter, generate the manifest from
|
|
that live read-only canonical capture and deployed clean commits, compile a
|
|
disposable VPS profile, invoke the real no-send `GatewayRunner`, terminate that
|
|
child, open a new child from the same manifest, and retain model-call, DB-read,
|
|
service, cleanup, and drift-negative readbacks. Do not replace the production
|
|
profile during that graduation.
|