Implement local runtime persistence and policy gates

This commit is contained in:
2026-05-18 18:21:27 +02:00
parent 7f9913c45a
commit 8089a7c8fa
23 changed files with 2263 additions and 42 deletions

View File

@@ -146,6 +146,49 @@ The slice should emit plans first, not mutate durable memory by surprise.
Durable writes, external adapters, live LLM extraction, vector retrieval, and
service deployment can follow once the plan model is stable.
## Local Runtime Facade
The second implementation slice adds a local facade, `PhaseMemoryRuntime`, over
the deterministic core. The facade is not a service runner. It is the small
application surface that adjacent tools can call before a service deployment
exists.
Runtime operations currently include:
- profile import
- graph import
- profile execution planning
- graph lifecycle planning
- graph activation planning
- package compilation handoff through `ContextPackageCompiler`
Each operation returns a JSON-serializable envelope with:
- `schema_version`
- `operation_id`
- `operation`
- `dry_run`
- `valid`
- `subject`
- `source`
- `policy_decision`
- `audit_receipt`
- `diagnostics`
- `data`
The local CLI exposes the same facade for fixtures and developer workflows:
```bash
phase-memory profile plan profile.json
phase-memory graph lifecycle graph.json --stale-after-days 7 --delete-after-days 30
phase-memory graph activate graph.json --max-items 3 --max-tokens 60
```
The default implementation uses in-memory stores, an allow-all local policy
gateway, a recording audit sink, and a noop context-package compiler. These are
test and integration adapters, not a claim that durable persistence, policy, or
package internals belong in this repository.
## Open Questions
- Should `phase-memory` depend directly on `markitect-tool` for validation, or

91
docs/local-persistence.md Normal file
View File

@@ -0,0 +1,91 @@
# Local Persistence
`phase-memory` can run against a versioned local file workspace. This is a
developer and integration adapter, not a production graph database.
## Layout
```text
memory-store/
phase-memory.json
profiles/
<profile-id>.json
nodes/
<node-id>.json
edges/
<edge-id>.json
paths/
<path-id>.json
activations/
events.jsonl
audit.jsonl
```
The root `phase-memory.json` declares:
```json
{
"schema_version": "phase_memory.local_store.v1"
}
```
Profiles, nodes, edges, and paths are stored as deterministic JSON files.
Events and audit records are append-only JSONL files. The current local runtime
does not compact, delete, or rewrite append-only logs.
## CLI
Import local fixtures:
```bash
PYTHONPATH=src python3 -m phase_memory.cli store import \
--store .phase-memory-local \
--profile tests/fixtures/memory-profile.json \
--graph tests/fixtures/memory-graph.json
```
Export a Markitect-compatible graph envelope:
```bash
PYTHONPATH=src python3 -m phase_memory.cli store export \
--store .phase-memory-local \
--graph-id local-dev
```
Inspect repair diagnostics:
```bash
PYTHONPATH=src python3 -m phase_memory.cli store repair \
--store .phase-memory-local
```
Repair diagnostics report malformed JSONL event lines, unknown event schema
versions, missing edge endpoints, and path records that reference events not
present in the event log.
## Paths
Conversational paths are structured records, not transcript blobs. A path can
record:
- `path_id`
- `parent_path_id`
- ordered `event_ids`
- active, merged, abandoned, or compacted state
- merge target
- abandoned reason
- compacted summary id
Helper functions in `phase_memory.paths` create, branch, merge, abandon, and
compact paths while also producing structured path events for the fluid memory
event log.
## Review-Gated Apply
Lifecycle planning remains dry-run by default. The runtime exposes an optional
`apply_lifecycle_actions` operation for local stores. Actions marked
`requires_review` are denied unless the caller provides an explicit
`approval_marker`.
This keeps the local adapter useful for development while preserving the
project rule that durable memory changes must be inspectable and deliberate.

113
docs/policy-audit.md Normal file
View File

@@ -0,0 +1,113 @@
# Policy And Audit
`phase-memory` keeps policy enforcement adapter-based. The local runtime
defines deterministic operation points, review records, audit envelopes, and
redaction behavior without becoming an identity or authorization platform.
## Operation Points
Canonical operation names live in `phase_memory.policy.MemoryOperation`.
Current operation points include:
- `profile.import`
- `graph.import`
- `node.read`
- `event.read`
- `profile.plan`
- `graph.lifecycle.plan`
- `graph.activation.plan`
- `package.compile`
- `lifecycle.apply`
- `memory.stabilize`
- `memory.compact`
- `memory.refresh`
- `memory.delete_request`
- `memory.archive`
- `graph.export`
- `store.repair.diagnostics`
Runtime operations call the configured `PolicyGateway` before emitting an
envelope. The default local adapter is allow-all and exists only for
dependency-light tests and local development.
## Review Records
Review-required lifecycle actions fail closed unless a caller provides an
approved review record or the legacy local `approval_marker` shorthand.
Review records capture:
- review id
- reviewed action id
- reviewer
- approval or rejection
- timestamp
- reason
- obligations
- source digests
The reviewed action id is deterministic:
```text
action:<digest-of-lifecycle-action>
```
This lets the runtime reject a review record that was issued for a different
planned action.
## Activation Policy
Activation planning can receive a local policy context:
```python
runtime.plan_activation(
graph,
max_items=4,
max_tokens=80,
policy_context={
"required_labels": ["project-local"],
"denied_labels": ["restricted"],
"trust_zone": "local",
"secrets_allowed": False,
"approved_reauthorizations": [],
},
)
```
Nodes denied by policy are omitted before selection. The runtime returns
diagnostics with code `activation_policy_denied` and a deterministic redacted
record where policy-sensitive fields and text are replaced with `[REDACTED]`.
## Audit Envelope
Audit events use schema `phase_memory.audit.event.v1` and include:
- operation id
- operation kind
- subject id
- profile id
- graph id
- policy decision
- dry-run flag
- planned action id
- actor label
- timestamp
- source reference
The local audit sinks record these events either in memory or as append-only
JSONL. External audit systems should implement the `AuditSink` port.
## Boundaries
This repository does not own:
- user identity
- enterprise policy languages
- remote policy decision points
- long-term audit retention
- legal hold or compliance workflows
Those belong behind adapters. `phase-memory` owns the memory-native points
where policy, review, redaction, and audit decisions must be requested and
explained.