diff --git a/docs/docs.json b/docs/docs.json
index 955c7ba..df6db55 100644
--- a/docs/docs.json
+++ b/docs/docs.json
@@ -95,7 +95,8 @@
"pages": [
"reference/events",
"reference/state",
- "reference/summary"
+ "reference/summary",
+ "reference/final"
]
},
{
diff --git a/docs/explanation/artifacts.mdx b/docs/explanation/artifacts.mdx
index e27739f..11f82dd 100644
--- a/docs/explanation/artifacts.mdx
+++ b/docs/explanation/artifacts.mdx
@@ -1,6 +1,6 @@
---
title: "Artifacts"
-description: "Understanding the files Noēsis produces: summary.json, state.json, events.jsonl, and more."
+description: "Understanding the files Noēsis produces: summary.json, state.json, events.jsonl, final.json, and more."
---
Every Noēsis episode produces a set of structured artifacts that capture the complete cognitive trace. These files enable replay, debugging, auditing, and analysis.
@@ -14,15 +14,22 @@ Every Noēsis episode produces a set of structured artifacts that capture the co
events.jsonl # cognitive event timeline with lineage
summary.json # metrics and KPIs (insight.metrics)
state.json # plan, beliefs, memory, outcomes
+ final.json # terminal seal marker
manifest.json # SHA-256 catalog + optional HMAC
learn.jsonl # learning signals (optional)
prompts.jsonl # prompt provenance (opt-in, ADR-005)
+ checkpoints/ # checkpoint anchors for paused runs
```
**Episode IDs** use ULID format (monotonic, sortable, 48-bit timestamp + 80-bit entropy). **Directive and governance IDs** use deterministic UUIDv5 for reproducible lineage tracking.
+Terminal runs seal with both `final.json` and `manifest.json`. Paused or
+interrupted runs are intentionally unsealed until they continue or terminate;
+expect `events.jsonl`, `state.json`, `learn.jsonl`, and checkpoint files before
+the terminal artifacts appear.
+
## summary.json
The summary captures episode outcomes, metrics, and cross-references.
@@ -334,6 +341,55 @@ noesis events ep_abc123 --phase act
noesis events ep_abc123 -j | jq '.[] | select(.phase == "plan")'
```
+## final.json
+
+`final.json` is the terminal seal marker for a run. If it exists, Noesis has
+finished finalization for the run and lifecycle writes such as `interrupt`,
+`checkpoint`, and `resume_run` are rejected.
+
+### Schema
+
+```json
+{
+ "schema_version": "final/2.0.0",
+ "episode_id": "ep_2024_abc123_s0",
+ "process_id": "proc_abc123",
+ "run_index": 1,
+ "finalized_at": "2024-01-15T10:30:05Z",
+ "outcome": "success",
+ "verification_status": "verified"
+}
+```
+
+| Field | Description |
+| --- | --- |
+| `schema_version` | Final marker schema (`final/2.0.0`) |
+| `episode_id` | Run or episode ID being sealed |
+| `process_id` | Process identity that owns the run |
+| `run_index` | One-based run number within that process |
+| `finalized_at` | ISO 8601 finalization timestamp |
+| `outcome` | Terminal outcome (`success`, `failed`, `vetoed`, `cancelled`, `interrupted`, `error`) |
+| `verification_status` | Verification class (`verified`, `unverified`, `not_applicable`) |
+
+### Sealing order
+
+Noesis writes `final.json` before `manifest.json`, then writes the manifest with
+`final.json` included in its file list. If manifest writing fails, Noesis removes
+`final.json` so the run is not incorrectly marked sealed.
+
+
+Treat a run as sealed only when `final.json` exists and `manifest.json` includes
+an entry for `final.json`.
+
+
+### Paused runs
+
+Approval pauses and pause-on-veto flows emit `run.interrupt` and
+`run.checkpoint` events, then write checkpoint anchors under
+`checkpoints//checkpoint.json`. These runs remain unsealed: no
+`final.json` or `manifest.json` is required until the run continues to a terminal
+outcome.
+
## manifest.json
The manifest provides integrity verification for all artifacts with SHA-256 hashes and optional signatures.
@@ -365,6 +421,11 @@ The manifest provides integrity verification for all artifacts with SHA-256 hash
| `files` | Array of files with `name`, `sha256`, `size_bytes`, `kind` (`summary`, `state`, `events`, `learn`, `attachment`, `custom`) |
| `signature` | Optional signature block with algorithm, key ID, value, timestamp |
+`summary.json`, `state.json`, `events.jsonl`, and `learn.jsonl` are tracked by
+default when present. Other files in the run directory, including `final.json`
+and checkpoint files, are included as attachments unless explicitly tracked with
+a narrower kind.
+
### Artifact immutability guarantees
All artifacts are written atomically using this pattern:
@@ -389,16 +450,16 @@ def verify_manifest(episode_dir: Path) -> bool:
with open(manifest_path) as f:
manifest = json.load(f)
- for filename, info in manifest["files"].items():
- file_path = episode_dir / filename
+ for info in manifest["files"]:
+ file_path = episode_dir / info["name"]
# Check size
- if file_path.stat().st_size != info["size"]:
+ if file_path.stat().st_size != info["size_bytes"]:
return False
# Check hash
with open(file_path, "rb") as f:
- actual_hash = hashlib.sha256(f.read()).hexdigest()
+ actual_hash = f"sha256:{hashlib.sha256(f.read()).hexdigest()}"
if actual_hash != info["sha256"]:
return False
@@ -535,4 +596,7 @@ store.vacuum() # Remove episodes older than 14 days
Complete summary schema documentation.
+
+ Complete final.json sealing contract.
+
diff --git a/docs/reference/final.mdx b/docs/reference/final.mdx
new file mode 100644
index 0000000..46c6c70
--- /dev/null
+++ b/docs/reference/final.mdx
@@ -0,0 +1,141 @@
+---
+title: "Final schema"
+description: "Complete reference for the Noesis final.json sealing marker."
+---
+
+The `final.json` artifact is the terminal marker for a run. Its presence means
+Noesis finished finalization, wrote the final contract, and then wrote
+`manifest.json` with `final.json` included in the manifest file list.
+
+Current final schema version: `final/2.0.0` ([JSON schema](/schema/final/2.0.0.json)).
+
+## Schema overview
+
+```json
+{
+ "schema_version": "final/2.0.0",
+ "episode_id": "ep_2024_abc123_s0",
+ "process_id": "proc_a1b2c3",
+ "run_index": 1,
+ "finalized_at": "2024-01-15T10:30:05Z",
+ "outcome": "success",
+ "verification_status": "verified"
+}
+```
+
+## Root fields
+
+
+Final artifact schema. Currently `"final/2.0.0"`.
+
+
+
+Run or episode identifier for the artifact directory being sealed.
+
+
+
+Process identity that owns this run. Finalization requires this value so sealed
+artifacts can be tied back to the process registry.
+
+
+
+One-based run number within the process.
+
+
+
+ISO 8601 timestamp when `final.json` was written.
+
+
+
+Terminal outcome class. Allowed values are `"success"`, `"failed"`,
+`"vetoed"`, `"cancelled"`, `"interrupted"`, and `"error"`.
+
+
+
+Verification class for the terminal outcome. Allowed values are `"verified"`,
+`"unverified"`, and `"not_applicable"`.
+
+
+## Outcome mapping
+
+The current core finalization path maps runtime and verification outcomes into
+the final contract as follows:
+
+| Runtime condition | `outcome` | `verification_status` |
+| --- | --- | --- |
+| Adapter succeeded and verification assertions passed | `success` | `verified` |
+| Adapter succeeded and no verification was provided | `success` | `unverified` |
+| Adapter succeeded but verification assertions failed | `failed` | `verified` |
+| Adapter errored or was skipped | `error` | `not_applicable` |
+| Terminal state was explicitly vetoed | `vetoed` | `not_applicable` |
+
+`cancelled` and `interrupted` are part of the schema's terminal outcome
+vocabulary. Current pause/checkpoint flows keep interrupted runs unsealed until
+they continue or terminate.
+
+## Sealing lifecycle
+
+Terminal finalization uses this order:
+
+1. Write `summary.json`, `state.json`, `learn.jsonl` when applicable, and other
+ runtime artifacts.
+2. Write `final.json`.
+3. Write `manifest.json`, which includes `final.json`.
+
+If manifest writing fails after `final.json` is written, Noesis removes
+`final.json` before raising the error. A run should not be treated as sealed
+unless both `final.json` and `manifest.json` are present and the manifest lists
+the final marker.
+
+
+Once `final.json` exists, lifecycle mutations such as `interrupt`, `checkpoint`,
+`resume`, and `resume_run` are rejected with `RunSealedError`.
+
+
+## Paused and interrupted runs
+
+Paused approval flows are intentionally unsealed:
+
+- `run.interrupt` and `run.checkpoint` are recorded in `events.jsonl`
+- checkpoint files are written under `checkpoints//checkpoint.json`
+- `run.state_projection.payload.links` includes non-terminal links such as
+ `events` and `learn`
+- `summary.json`, `manifest.json`, and `final.json` are not guaranteed until the
+ run continues to a terminal state
+
+Use `resume_run(...)` after approval to continue the same run ID. When
+continuation reaches a terminal outcome, Noesis emits terminal projection
+evidence and seals the run.
+
+## Operational checks
+
+```bash
+# Confirm a run is sealed.
+test -f .noesis/episodes//final.json
+test -f .noesis/episodes//manifest.json
+
+# Inspect the final contract.
+jq '{episode_id, process_id, run_index, outcome, verification_status}' \
+ .noesis/episodes//final.json
+
+# Confirm the manifest covers final.json.
+jq '.files[] | select(.name == "final.json")' \
+ .noesis/episodes//manifest.json
+```
+
+## Related references
+
+
+
+ How final markers fit into the episode artifact bundle.
+
+
+ Lifecycle APIs and common sealed-run errors.
+
+
+ Runtime lifecycle events for interrupts, checkpoints, resumes, and state projections.
+
+
+ Terminal and non-terminal state projection links.
+
+