Skip to content

Commit 9197c37

Browse files
authored
feat(config): persist durable runtime settings (#178)
* feat(config): persist durable runtime settings Move transcript capture settings and forge scoped identity onto the project config surface while preserving environment variables as per-invocation overrides. Propagate the resolved forge identity and transcript settings into launched agent and MCP processes so config-only projects carry their runtime context locally.\n\nRefs #177 * fix(config): forward default forge identity
1 parent 1599a70 commit 9197c37

20 files changed

Lines changed: 1126 additions & 94 deletions

File tree

ARCHITECTURE.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@ These are library capabilities exposed by libagent and consumed by both the CLI
2626

2727
6. **Protocol selection.** `selection::discover_ready_candidates` evaluates protocols in topological order under an explicit `EvaluationScope`. `EvaluationScope::Unscoped` evaluates only protocols with `scoped = false` and always uses `work_unit = None`. `EvaluationScope::Scoped(id)` evaluates only protocols with `scoped = true` for that exact delegated work unit. Readiness no longer discovers sibling work units from artifact instances. Current work is suppressed only when outputs are valid and trusted plus either: the current freshness-relevant input snapshot matches the last successful execution record for that `(protocol, work_unit)` pair, or no execution record exists and the timestamp fallback still shows outputs newer than all relevant inputs. Execution-record snapshots are mode-aware: `on_change`/`on_invalid` preserve any recorded matching instance, while `on_artifact` and `requires` compare only valid instances. The timestamp fallback still considers the latest recorded modification across relevant inputs.
2828

29-
7. **Scoped work-unit identity validation.** After workspace scan and before scoped readiness evaluation, `runa state`, `runa step`, `runa run`, and `runa go` validate that the supplied `--work-unit` exactly matches a recorded `work-unit` instance id when any are recorded. Invalid and malformed recorded roots still establish canonical ids. Valid tracker-backed roots also enforce instance-id/handle number agreement, duplicate tracker-root rejection, and agreement with the active `GROUNDWORK_*` deployment identity. With no recorded `work-unit` roots, scoped evaluation remains inert.
29+
7. **Scoped work-unit identity validation.** After workspace scan and before scoped readiness evaluation, `runa state`, `runa step`, `runa run`, and `runa go` validate that the supplied `--work-unit` exactly matches a recorded `work-unit` instance id when any are recorded. Invalid and malformed recorded roots still establish canonical ids. Valid tracker-backed roots also enforce instance-id/handle number agreement, duplicate tracker-root rejection, and agreement with the active forge deployment identity resolved from `.runa/config.toml` with `GROUNDWORK_FORGE_*` env overrides. With no recorded `work-unit` roots, scoped evaluation remains inert.
3030

3131
8. **Tracing bootstrap.** Both binaries bootstrap tracing with env/default settings before any config lookup, then reconfigure the shared subscriber from `config.toml` when logging settings are available. Tracing events always go to stderr; operator-facing command output stays on stdout.
3232

3333
9. **Status and session evaluation.** `status::evaluate_protocols` is the shared readiness classification path used by CLI state reporting and MCP session readiness. `session::SessionState` layers current-step lifecycle state over that evaluator for scoped sessions: every public operation scans first, immediately revalidates the scoped work-unit identity for the session, readiness preserves an existing current step but may select the first ready step when none is active, exhausted work is reopened by the same relevant-input-change rule used by the live runner, and only `advance` retires the current step after postcondition enforcement, next-step validation, and execution-record persistence.
3434

35-
10. **CLI execution commands.** `runa state`, `runa step`, `runa run`, and `runa go` share the same scope-resolved topology, readiness evaluation, and scope handling. Without `--work-unit`, commands evaluate only unscoped protocols; with `--work-unit <ID>`, they evaluate only scoped protocols for that delegated work unit after canonical identity validation. `step --dry-run` previews only the next concrete execution, while `run --dry-run` projects the full optimistic cascade to quiescence from declared `produces` outputs plus already-known required output choice branches within that same scope and scope-filtered execution order; optional `may_produce` outputs do not advance the projection unless they already exist, and required output choice branches are not synthesized unless exactly one member already exists. Live execution targets Linux. Live `step` executes exactly one ready candidate through fixed-protocol MCP, then re-scans and prints the refreshed state. Live `run` repeats the execute → scan → enforce → re-evaluate cycle until quiescence, resolves its agent command from `--agent-command -- <argv...>` or `[agent].command` in config, reopens exhausted work when a later reconciliation changes relevant inputs, and exits with outcome-specific status codes. Live `go` launches the configured agent with `runa-mcp --session --work-unit <ID>`, sends a generic one-tick prompt, and verifies that the selected session step recorded execution through the session surface. Before launching an agent, live execution builds a `runa-mcp` config, exports it through `RUNA_MCP_CONFIG`, and launches the configured argv unmodified; runtime-specific MCP adaptation belongs to the runtime or adapter. When `RUNA_TRANSCRIPT_DIR` is set, live execution also appends structured transcript events for the rendered prompt, agent stdout/stderr, and exit status.
35+
10. **CLI execution commands.** `runa state`, `runa step`, `runa run`, and `runa go` share the same scope-resolved topology, readiness evaluation, and scope handling. Without `--work-unit`, commands evaluate only unscoped protocols; with `--work-unit <ID>`, they evaluate only scoped protocols for that delegated work unit after canonical identity validation. `step --dry-run` previews only the next concrete execution, while `run --dry-run` projects the full optimistic cascade to quiescence from declared `produces` outputs plus already-known required output choice branches within that same scope and scope-filtered execution order; optional `may_produce` outputs do not advance the projection unless they already exist, and required output choice branches are not synthesized unless exactly one member already exists. Live execution targets Linux. Live `step` executes exactly one ready candidate through fixed-protocol MCP, then re-scans and prints the refreshed state. Live `run` repeats the execute → scan → enforce → re-evaluate cycle until quiescence, resolves its agent command from `--agent-command -- <argv...>` or `[agent].command` in config, reopens exhausted work when a later reconciliation changes relevant inputs, and exits with outcome-specific status codes. Live `go` launches the configured agent with `runa-mcp --session --work-unit <ID>`, sends a generic one-tick prompt, and verifies that the selected session step recorded execution through the session surface. Before launching an agent, live execution builds a `runa-mcp` config, exports it through `RUNA_MCP_CONFIG`, injects config-resolved transcript and forge identity environment into both the agent process and MCP config, and launches the configured argv unmodified; runtime-specific MCP adaptation belongs to the runtime or adapter. When transcript capture is enabled by config or `RUNA_TRANSCRIPT_DIR`, live execution also appends structured transcript events for the rendered prompt, agent stdout/stderr, and exit status.
3636

3737
11. **MCP runtime loop.** In fixed-protocol mode, `runa-mcp` parses `--protocol` and optional `--work-unit`, loads the project, scans the workspace, resolves the named protocol from the manifest, validates declared scope and canonical work-unit identity against the provided arguments, validates that its outputs can be served as MCP tools, and serves output tools via stdio. In session mode, `runa-mcp --session --work-unit <ID>` opens a scoped `SessionState`, advertises driver tools plus current-step output tools, and emits `notifications/tools/list_changed` whenever a session verb changes the current step and therefore the advertised output tools. When transcript capture is enabled, tool calls and tool results are appended to the same JSONL stream as CLI execution events.
3838

@@ -94,7 +94,7 @@ Returns `EnforcementError` on failure, containing the protocol name, enforcement
9494

9595
### `project.rs`
9696

97-
Shared project loading logic used by both `runa-cli` and `runa-mcp`. Config resolution chain (explicit override → `.runa/config.toml` → XDG config → error), config parsing for logging plus optional agent execution command, manifest parsing, dependency graph construction, and artifact store initialization.
97+
Shared project loading logic used by both `runa-cli` and `runa-mcp`. Config resolution chain (explicit override → `.runa/config.toml` → XDG config → error), config parsing for logging, optional agent execution command, transcript defaults, and forge identity defaults, manifest parsing, dependency graph construction, and artifact store initialization.
9898

9999
### `selection.rs`
100100

@@ -122,7 +122,7 @@ Canonical scoped work-unit identity validation shared by CLI commands and
122122
including invalid and malformed records. Valid tracker-backed roots receive the
123123
runtime checks that schema validation cannot express: id/handle number
124124
agreement, duplicate tracker identity detection, and active deployment
125-
agreement from `GROUNDWORK_*` atoms.
125+
agreement from config-resolved `GROUNDWORK_FORGE_*` atoms.
126126

127127
## runa-mcp Modules
128128

@@ -142,7 +142,7 @@ output-tool surface.
142142

143143
```
144144
.runa/
145-
config.toml # Created by `runa init`: methodology_path, optional logging, optional agent.command
145+
config.toml # Created by `runa init`: methodology_path, optional logging, agent.command, transcript, forge defaults
146146
state.toml # Created by `runa init`: initialized_at, runa_version
147147
workspace/ # Artifact workspace (non-configurable)
148148
{type_name}/
@@ -157,11 +157,11 @@ output-tool surface.
157157

158158
Commands that operate on a loaded methodology share `project::load`, which resolves the config file, reads the methodology path from it, parses the manifest, builds the dependency graph, and opens the artifact store.
159159

160-
Config resolution is whole-file (first found wins, no per-field merging): `--config` CLI flag → `RUNA_CONFIG` env var → `.runa/config.toml``$XDG_CONFIG_HOME/runa/config.toml` → error.
160+
Config resolution is whole-file (first found wins, no per-field merging): `--config` CLI flag → `RUNA_CONFIG` env var → `.runa/config.toml``$XDG_CONFIG_HOME/runa/config.toml` → error. Within the selected file, durable transcript and forge settings are field-level defaults that matching environment variables override for one invocation.
161161

162162
### `runa init --methodology <PATH> [--config <PATH>]`
163163

164-
Parses the manifest at `<PATH>` via `libagent::manifest::parse`, canonicalizes the path, creates `.runa/config.toml` (or writes to the `--config` path) containing the canonical methodology path plus optional logging settings and optional agent execution settings. Creates `.runa/state.toml`, `.runa/store/`, and the fixed artifact workspace directory at `.runa/workspace/`. Reports the artifact type and protocol counts on success.
164+
Parses the manifest at `<PATH>` via `libagent::manifest::parse`, canonicalizes the path, creates `.runa/config.toml` (or writes to the `--config` path) containing the canonical methodology path plus optional logging, agent execution, transcript, and forge settings. Creates `.runa/state.toml`, `.runa/store/`, and the fixed artifact workspace directory at `.runa/workspace/`. Reports the artifact type and protocol counts on success.
165165

166166
### `runa list`
167167

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ Semantic Versioning.
99

1010
### Changed
1111

12+
- Durable transcript capture settings and scoped forge identity can now live in
13+
`.runa/config.toml`, with `RUNA_TRANSCRIPT_*` and `GROUNDWORK_FORGE_*`
14+
environment variables retained as per-invocation overrides. Resolved forge
15+
identity is forwarded into launched agent and MCP environments.
1216
- Live agent launch is now agent-agnostic. A command named `claude` is no
1317
longer launched with injected `--mcp-config` / `--strict-mcp-config` flags;
1418
it receives the MCP session config through `RUNA_MCP_CONFIG` like every other

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,14 @@ Both projects are in early development.
167167

168168
See [CLI Reference](docs/cli-reference.md) for flags, exit codes, configuration, and behavioral details.
169169

170+
## Configuration
171+
172+
`runa init` creates `.runa/config.toml` with the methodology path. Operators
173+
may also set durable project defaults there for live agent command,
174+
transcript capture, and scoped forge identity. Environment variables such as
175+
`RUNA_TRANSCRIPT_DIR` and `GROUNDWORK_FORGE_*` remain per-invocation overrides;
176+
config is the project-local default.
177+
170178
## Build
171179

172180
Rust 2024 edition. Runa targets Linux.

docs/cli-reference.md

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,38 @@ Commands that load a methodology share a common config resolution chain. The fir
1313

1414
For `runa init`, `--config` controls where the config file is written. For all other commands, it controls where the config file is read from.
1515

16+
### Durable Project Settings
17+
18+
Durable project settings live in `.runa/config.toml`. Environment variables
19+
with matching runtime meaning remain per-invocation overrides.
20+
21+
```toml
22+
[transcript]
23+
dir = "transcripts"
24+
redact_env = ["SECRET_TOKEN", "API_KEY"]
25+
26+
[forge]
27+
type = "github"
28+
owner = "tesserine"
29+
name = "runa"
30+
tracker_id = "4"
31+
```
32+
33+
`[transcript].dir` enables transcript capture and is resolved relative to the
34+
project directory when it is not absolute. `RUNA_TRANSCRIPT_DIR` overrides it
35+
for one invocation. `[transcript].redact_env` names environment variables whose
36+
current values should be redacted from transcript events.
37+
`RUNA_TRANSCRIPT_REDACT_ENV`, when set to a comma-separated list, overrides the
38+
configured list.
39+
40+
`[forge]` supplies the active scoped work-unit deployment identity. Runa uses
41+
it when validating tracker-backed `work-unit` roots and injects the resolved
42+
`GROUNDWORK_FORGE_TYPE`, `GROUNDWORK_FORGE_OWNER`, `GROUNDWORK_FORGE_NAME`, and
43+
`GROUNDWORK_FORGE_TRACKER_ID` values into launched agent and MCP environments.
44+
Any non-empty matching `GROUNDWORK_FORGE_*` environment variable overrides the
45+
configured field for that invocation. The forge type still defaults to `github`
46+
when neither config nor env specifies one.
47+
1648
### Logging
1749

1850
Runtime diagnostics use `tracing` on stderr. Command output stays on stdout.
@@ -51,14 +83,15 @@ payload containing the resolved `runa-mcp` command, arguments, and environment
5183
child process. Runtime-specific translation, such as wrapping that payload in a
5284
client-specific config file, belongs to the runtime or adapter, not to runa.
5385

54-
When `RUNA_TRANSCRIPT_DIR` is set, live execution appends JSON Lines transcript
55-
events to `$RUNA_TRANSCRIPT_DIR/events.jsonl`. Events include protocol prompts,
86+
When transcript capture is enabled through `[transcript].dir` or
87+
`RUNA_TRANSCRIPT_DIR`, live execution appends JSON Lines transcript events to
88+
`events.jsonl` in the resolved directory. Events include protocol prompts,
5689
agent stdout/stderr chunks, agent exit status, and `runa-mcp` tool call/result
57-
events when the agent runtime launches the configured MCP server.
58-
`RUNA_TRANSCRIPT_REDACT_ENV` may name comma-separated environment variables;
59-
their current non-empty values are replaced with `[REDACTED:<name>]` before
60-
events are written. Hidden model reasoning and runtime-private provider events
61-
are outside runa's observable boundary.
90+
events when the agent runtime launches the configured MCP server. Configured or
91+
environment-supplied redaction names cause their current non-empty values to be
92+
replaced with `[REDACTED:<name>]` before events are written. Hidden model
93+
reasoning and runtime-private provider events are outside runa's observable
94+
boundary.
6295

6396
## Commands
6497

@@ -291,10 +324,18 @@ resolves them from the local filesystem, so adapters do not depend on child
291324
process cwd to launch `runa-mcp`. Transcript environment variables are forwarded
292325
into the MCP config when transcript capture is enabled, which lets the MCP
293326
server append tool events to the same transcript stream as the CLI execution
294-
events.
327+
events. Configured forge identity is forwarded the same way through
328+
`GROUNDWORK_FORGE_*` entries so methodology tooling inside the agent session can
329+
use the project-local identity without user-global shell state.
295330

296331
**Environment variables:**
297332

298333
- `RUNA_WORKING_DIR` — Project directory. Defaults to the current directory.
299334
- `RUNA_CONFIG` — Config file override (same as `--config` in the CLI).
335+
- `RUNA_TRANSCRIPT_DIR` — Transcript directory override for one invocation.
336+
- `RUNA_TRANSCRIPT_REDACT_ENV` — Comma-separated transcript redaction-name
337+
override for one invocation.
338+
- `GROUNDWORK_FORGE_TYPE`, `GROUNDWORK_FORGE_OWNER`, `GROUNDWORK_FORGE_NAME`,
339+
`GROUNDWORK_FORGE_TRACKER_ID` — Forge identity field overrides for one
340+
invocation.
300341
- `RUST_LOG` — Tracing filter override for stderr diagnostics.

libagent/src/lib.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,19 @@ pub use model::{
4949
ArtifactType, Manifest, ProtocolDeclaration, RequiredOutputChoice, TriggerCondition,
5050
UnscopedOutputRequiresWorkUnitError, validate_output_scope,
5151
};
52-
pub use project::{Config, LoadedProject, LogFormat, LoggingConfig, ProjectError, State};
52+
pub use project::{
53+
Config, ForgeConfig, LoadedProject, LogFormat, LoggingConfig, ProjectError, State,
54+
TranscriptConfig,
55+
};
5356
pub use projection::{ProjectionCandidate, ProjectionClass, project_cascade};
5457
pub use scan::{
5558
ArtifactRef, InvalidArtifact, MalformedArtifact, PartiallyScannedType, ScanError, ScanResult,
5659
UnreadableArtifact, scan,
5760
};
58-
pub use scoped_identity::{ScopedWorkUnitError, validate_scoped_work_unit};
61+
pub use scoped_identity::{
62+
ScopedWorkUnitError, resolve_forge_environment, validate_scoped_work_unit,
63+
validate_scoped_work_unit_with_env,
64+
};
5965
pub use selection::{
6066
Candidate, CandidateKey, CandidateStatus, ClassifiedCandidate, EvaluationScope,
6167
EvaluationTopology, ScanTrust, WaitingReason, classify_candidates,

0 commit comments

Comments
 (0)