Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions docs/explanation/artifacts.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,36 @@ print(f"Steps: {len(state['plan']['steps'])}")
print(f"Status: {state['outcomes']['status']}")
```

### Authority model

`state.json` is a snapshot assembled from multiple authorities. Use the source
that owns a field when debugging drift:

| State slice | Authority | Operational note |
| --- | --- | --- |
| `episode.started_at` | `start` event timestamp | Stable across continuation |
| `plan` | Latest `plan` event plus later `act.step_status` evidence | Reconstruct with `noesis.runtime.plan_projection.project_plan_state(...)` |
| `outcomes` and `links` | `run.state_projection` runtime event | Terminal values mirror persisted `state.json` |
| `process` | Process registry allocation | Use `noesis processes` for current liveness |
| `beliefs` and `memory` | Runtime state repository | Treat as persisted state, not trace-derived evidence |

For process-scoped runs, Noēsis copies the registry allocation into both
`summary.json` and `state.json`:

```json
{
"process": {
"id": "c0ffee123456",
"name": "release-notes",
"kind": "oneshot",
"run_index": 3
}
}
```

`process.run_index` is monotonic inside one process. It is required for same-run
continuation and should not be interpreted as a global episode sequence.

## events.jsonl

The event timeline records every phase transition with timing and lineage.
Expand Down
63 changes: 59 additions & 4 deletions docs/reference/cli.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ Most commands accept:

| Command | Purpose |
| --- | --- |
| `noesis` | Show the compact home screen and recent-run shortcuts |
| `noesis run` | Start a baseline episode |
| `noesis view` | Inspect an episode by ID or path |
| `noesis ps` | List recent episodes |
Expand All @@ -51,6 +52,19 @@ Most commands accept:

## Core workflows

### Open the home screen

```bash
# Compact dashboard, exits 0
noesis

# Include detailed config and recent-run sections
noesis --full
```

The home screen is safe for empty workspaces: failures while reading recent
episodes are treated as empty results.

### Run an episode

```bash
Expand Down Expand Up @@ -122,13 +136,41 @@ noesis events ep_01JH6Z2V9Q2K6Y6N0QZ7K2QW8C --phase governance
### Process-oriented inspection

```bash
# Process liveness and metadata
noesis processes --limit 20
# Group a run under a stable process label
noesis run "Backfill invoice summaries" --process billing-sync

# Process liveness and metadata, optionally filtered by exact id or name
noesis processes --process billing-sync --json | jq '.processes[0]'

# Runs for a specific process
# Recent episodes for one process id or name
noesis ps --process billing-sync --json | jq '.episodes[].process'

# Runs for a specific process id or name
noesis runs --process alpha --limit 20
```

Process identity is derived from the resolved workspace plus the optional label
passed to `--process`. If no label is supplied, Noēsis derives a display name
from the workspace directory and the process id prefix.

`processes` reads the registry and reports liveness fields:

| Field | Meaning |
| --- | --- |
| `process_id` | Opaque stable id |
| `process_name` | Human label or auto-derived workspace label |
| `kind` | Process type; runtime-created runs currently use `oneshot` |
| `status` | `running`, `idle`, `stale`, or `error` |
| `last_seen_at` | Last registry update |
| `last_heartbeat_at` | Last running-process heartbeat |
| `active_run_id` | Current episode id when a run is active |
| `last_run_outcome` | Outcome from the most recent finished run |
| `next_run_index` | Next one-based index to allocate inside this process |

`ps --process` and `runs --process` filter episodes by exact `process.id` or
`process.name` from run summaries. `runs --json` returns the filtered episode
array directly; `ps --json` wraps rows in a `cli/1.1` envelope.

### Diagnostics and integrity

```bash
Expand Down Expand Up @@ -177,9 +219,10 @@ The verification parser enforces:

### JSON output shape by command

- `run --json` emits a run-result envelope (`episode_id`, `episode_dir`, `artifacts`, `outcome`, `verification`, `capabilities`)
- `run --json` emits a run-result envelope (`episode_id`, `episode_dir`, `artifacts`, `outcome`, `verification`, `capabilities`, and `process` when present)
- `ps --json` emits `{"episodes":[...], ...}`
- `processes --json` emits `{"processes":[...], ...}`
- `runs --json --process NAME` emits a filtered episode array
- `events --json` emits JSONL (one event per line)
- `events --envelope` emits one JSON object containing all selected events

Expand Down Expand Up @@ -207,6 +250,18 @@ Fix:
- pass a direct episode directory path to `noesis view`
- inspect available episodes with `noesis ps`

### `noesis runs --process <name>` returns no rows

Cause: the process filter matches exact `process.id` or `process.name` values
stored in run summaries; it does not fuzzy-match task text or tags.

Fix:

- inspect registry names with `noesis processes`
- run future episodes with an explicit label, for example
`noesis run "task" --process <name>`
- verify that the same `NOESIS_RUNS_DIR` / runtime config is active

### `error: corrupted events.jsonl at ...`

Cause: the episode trace failed integrity checks (for example malformed JSON, invalid UTF-8, or a truncated record).
Expand Down
16 changes: 15 additions & 1 deletion docs/reference/python-api.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ episode_id = ns.run(
tags: dict[str, object] | None = None,
context: Any | None = None,
workspace: str | Path | None = None,
process: str | None = None,
verify: VerifySpec | Sequence[VerifySpec] | None = None,
) -> str
```
Expand Down Expand Up @@ -57,6 +58,11 @@ Optional runtime context. If provided, execution bypasses the default session.
Workspace root to snapshot for verification.
</ParamField>

<ParamField body="process" type="str | None" default="None">
Optional process label for grouping related episodes in `summary.json`,
`state.json`, and CLI process views.
</ParamField>

<ParamField body="verify" type="VerifySpec | Sequence[VerifySpec] | None" default="None">
Verification assertions to evaluate against the workspace.
</ParamField>
Expand All @@ -70,6 +76,7 @@ import noesis as ns

episode_id = ns.run("Draft release notes")
episode_id = ns.run("Draft release notes", intuition=False, tags={"env": "staging"})
episode_id = ns.run("Draft release notes", process="release-notes")
```

**Runtime context example**:
Expand Down Expand Up @@ -99,6 +106,7 @@ episode_id = ns.solve(
tags: dict[str, object] | None = None,
context: Any | None = None,
workspace: str | Path | None = None,
process: str | None = None,
verify: VerifySpec | Sequence[VerifySpec] | None = None,
) -> str
```
Expand All @@ -118,6 +126,7 @@ def to_upper(task: str) -> dict:

episode_id = ns.solve("process this", using=to_upper)
episode_id = ns.solve("process this", using="my.module:adapter_fn")
episode_id = ns.solve("process this", using=to_upper, process="batch-transform")
```

### Run lifecycle APIs
Expand Down Expand Up @@ -519,13 +528,18 @@ from noesis import SessionBuilder

session = SessionBuilder.from_env().build()
ep = session.run("Process customer request")
grouped_ep = session.run("Process customer request", process="support-triage")

# Read artifacts using the session's RuntimeContext
import noesis as ns
summary = ns.summary.read(ep, context=session.context)
```

`SessionBuilder` reads config from env/TOML; you can also inject ports before building. Within a session, `run`/`solve` behave like the module-level helpers but share the session’s config and runtime context.
`SessionBuilder` reads config from env/TOML; you can also inject ports before
building. Within a session, `run`/`solve` behave like the module-level helpers
but share the session’s config and runtime context. The optional `process` label
uses the session workspace/config to derive the same stable process identity used
by CLI process views.

## Module facades

Expand Down
57 changes: 57 additions & 0 deletions docs/reference/state.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ The `state.json` artifact captures cognitive state snapshots during execution an
"version": "1.0",
"state_schema_version": "1.0.0",
"episode": { ... },
"process": { ... },
"goal": { ... },
"beliefs": [ ... ],
"plan": { ... },
Expand All @@ -36,6 +37,11 @@ Relative artifact paths for this run (for example `events.jsonl`,
`learn.jsonl`, `summary.json`, `manifest.json`). Keys vary by lifecycle state.
</ResponseField>

<ResponseField name="process" type="object">
Stable process identity for grouping related episodes. Present for runtime-created
episodes and required for same-run continuation.
</ResponseField>

## Episode

Episode metadata and timestamps.
Expand Down Expand Up @@ -80,6 +86,48 @@ Intuition posture for the episode (advisory | interventive | hybrid). Runs creat
with older Noēsis versions may omit this field (default advisory).
</ResponseField>

## Process

`process` ties an episode to the filesystem-backed process registry used by
operator views.

```json
{
"process": {
"id": "c0ffee123456",
"name": "release-notes",
"run_index": 3,
"kind": "oneshot"
}
}
```

<ResponseField name="process.id" type="string" required>
Opaque stable identifier derived from the resolved workspace identity plus the
optional process label.
</ResponseField>

<ResponseField name="process.name" type="string" required>
Human-readable process label. When no label is supplied, Noēsis derives a name
from the workspace directory and process id prefix.
</ResponseField>

<ResponseField name="process.run_index" type="number" required>
Monotonic, one-based counter allocated by the process registry for this process.
It orders runs inside one process and is not a global episode count.
</ResponseField>

<ResponseField name="process.kind" type="string">
Execution style for the process. Runtime-created CLI and Python runs currently
use `"oneshot"`.
</ResponseField>

<Info>
`state.process` is a snapshot of registry allocation, not a value reconstructed
from `events.jsonl`. `resume_run` requires `process.id` and `process.run_index`
in `state.json`.
</Info>

## Artifact links

`links` points to run-local artifacts in the episode directory.
Expand Down Expand Up @@ -409,6 +457,9 @@ Outcome metrics (e.g., total duration).

- `episode.started_at` is aligned to the `start` event timestamp and remains
stable across continuation.
- `process` is registry-backed metadata captured at run allocation time. Use it
to group and resume runs, but use the process registry/CLI for current
liveness.
- Whenever `state.json` is persisted, runtime emits
`phase="runtime"`, `event_type="run.state_projection"` with projection
evidence for outcomes and links.
Expand All @@ -428,6 +479,12 @@ Outcome metrics (e.g., total duration).
"using": "baseline",
"started_at": "2024-01-15T10:30:00Z"
},
"process": {
"id": "c0ffee123456",
"name": "release-notes",
"run_index": 3,
"kind": "oneshot"
},
"goal": {
"task": "Draft release notes for v1.2.0",
"type": "task"
Expand Down
Loading