Skip to content
Merged
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
1 change: 1 addition & 0 deletions .agents/DECISIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@
| Stable latest-release Unix installer asset | Accepted | `.agents/decisions/0015-stable-latest-release-installer.md` |
| Local runtime wiring without embedded scheduling | Accepted | `.agents/decisions/0016-runtime-wiring-boundary.md` |
| Declarative QA profiles around the canonical gate | Accepted | `.agents/decisions/0017-declarative-qa-profiles.md` |
| Versioned Paseo action adapter with explicit lifecycle authority | Accepted | `.agents/decisions/0018-paseo-action-adapter.md` |
7 changes: 7 additions & 0 deletions .agents/PASEO.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ Paseo is Weft's first documented agent launcher integration. The integration is

Paseo may stop or archive a session at any point. Resume uses the durable procedure in [AGENT_PROTOCOL.md](AGENT_PROTOCOL.md), optionally in a new workspace and with a new agent. Workspace removal must never precede successful canonical capture when uncommitted work is intended to survive.

The concrete Unix action mapping is documented in
[PASEO_INTEGRATION.md](../docs/PASEO_INTEGRATION.md). Release archives expose
`bin/weft-paseo-action`, a thin adapter for assignment/lease acquisition,
checkpoint, exact-revision materialization, session replacement, observation,
and release. It consumes the bridge-resolved state directory and explicit
caller-owned IDs; it never launches or schedules the agent.

## Launch guidance

Register the repository with `paseo project create`. Prefer worktree isolation for mutating agents and local isolation for read-only review. Select a human-configured Paseo agent profile by its current notes rather than hard-coding a provider/model in the repository. Use completion notifications to trigger inspection; do not poll agents as a scheduler loop.
Expand Down
21 changes: 17 additions & 4 deletions .agents/PROGRESS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Current phase

Phase 9 — runtime wiring in progress.
Phase 9 — runtime wiring and the first native Paseo adapter complete.

## Completed

Expand Down Expand Up @@ -211,12 +211,25 @@ Phase 9 — runtime wiring in progress.
a small operator reference, embedded SBOM, and manifest; project documentation
and development helpers remain source-only, while releases publish only the
installable archive.
- Replaced stale Paseo helper invocations with the current `weft.cli.v1`
noun/verb grammar and added strict action-argument validation.
- Added a versioned Unix `weft-paseo-action` adapter to the runtime bridge for
explicit assignment/lease acquisition and renewal, Native Git checkpoint,
exact-revision materialization/observation, handoff, session replacement, and
release; it never launches or schedules processes.
- Added bridge metadata and diagnostics for the Paseo adapter, packaged it in
Unix runtime archives, and made the release smoke invoke the archived adapter
with the installed binary.
- Proved the Paseo lifecycle across two isolated workspaces, including expired
lease reclaim and separate-process checkpoint/resume; the full `make check`
gate and v0.2.1 Linux archive smoke are green.

## Next checkpoint

Prove a native runtime adapter that carries the bridge's shared state into an
isolated agent workspace and covers acquisition, checkpoint, session replacement,
and release without making Weft a scheduler.
The first native runtime adapter is proven for Unix Paseo. Future expansion can
add a Windows PowerShell adapter or other runtime-specific lifecycle adapters
only with their own versioned end-to-end proof; OMC, OMG, and OMX remain
bridge-only, and Codex/Claude Code/Gemini CLI remain instruction integrations.

## Known gaps

Expand Down
71 changes: 71 additions & 0 deletions .agents/decisions/0018-paseo-action-adapter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# ADR-0018: Paseo action adapter and lifecycle proof

- **Status:** Accepted
- **Date:** 2026-09-10

## Context

Runtime wiring made Weft discoverable to Paseo, but a bridge by itself still
left each launcher integration to translate durable assignments, leases,
materializations, and revisions into the current CLI grammar. The old helper
scripts also used obsolete flags and commands, so a freshly wired runtime could
not reliably resume work across processes.

The adapter must preserve Weft's identity, compare-and-swap, canonical-content,
lease, and reconciliation invariants while keeping process launch and
scheduling in Paseo.

## Decision

Keep the existing `weft.runtime-bridge.v1` shape backward-readable and add an
optional `adapter` entry. A Paseo setup entry identifies the current
`paseo-action-adapter-v2` integration and the `weft-paseo-action` executable.
Older bridge entries without that field remain parseable, but `weft doctor`
reports the missing adapter until setup refreshes the bridge.

Provide `weft-paseo-action` as a thin Unix shell adapter. Each action maps to
one current `weft.cli.v1` command and receives the state directory, Change ID,
actor, timestamp, repository identity, workspace identity, operation IDs, and
expected versions explicitly. It does not generate durable identities, launch
or supervise processes, retry ambiguous provider mutations, or alter user-home
configuration.

The supported lifecycle is assignment/lease acquisition, exact Native Git
checkpoint, exact-revision materialization, observation, handoff, session
replacement with a new lease/workspace, and explicit release. The repository
gate runs the lifecycle proof and a separate-process resume proof. Unix-like
release archives ship the adapter; Windows archives retain the bridge and CLI
contract but do not claim to ship a Bash adapter.

## Consequences

- Paseo can call a stable action boundary without duplicating Weft's durable
state machine or using stale command syntax.
- A replacement session can reclaim an expired scope, materialize the exact
recorded revision in a new workspace, and release all durable ownership.
- Callers still supply IDs and expected versions, so malformed or stale actions
fail as explicit usage or concurrency errors.
- OMC, OMG, OMX, Codex, Claude Code, and Gemini CLI remain bridge/instruction
integrations rather than unproven native lifecycle adapters.
- A PowerShell adapter remains future work if Windows Paseo lifecycle parity is
required.

## Alternatives rejected

- Embedding a process scheduler or agent runner in Weft: violates the external
scheduler boundary.
- Keeping the obsolete helper grammar as a compatibility alias: hides caller
drift and would make the public v1 command contract ambiguous.
- Inventing IDs or silently recovering stale versions in the adapter: breaks
durable identity and compare-and-swap guarantees.

## Required proof

- Current CLI and bridge-schema tests prove setup metadata, idempotency, and
legacy bridge diagnosis.
- `scripts/test-paseo-weft-bridge.sh` proves two isolated workspaces,
acquisition, renewal, checkpoint, dirty observation, handoff, expiry/reclaim,
exact replacement materialization, and release.
- `scripts/test-cli-session-resume.sh` proves the checkpoint survives a separate
process.
- `make check` and the Linux release archive smoke test must remain green.
1 change: 1 addition & 0 deletions .agents/decisions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ Each ADR records status, context, decision, alternatives, consequences, migratio
- [ADR-0015: Stable latest-release Unix installer asset](0015-stable-latest-release-installer.md)
- [ADR-0016: Local runtime wiring without embedded scheduling](0016-runtime-wiring-boundary.md)
- [ADR-0017: Declarative QA profiles around the canonical gate](0017-declarative-qa-profiles.md)
- [ADR-0018: Paseo action adapter and lifecycle proof](0018-paseo-action-adapter.md)
42 changes: 30 additions & 12 deletions .agents/tasks/phase-9-runtime-wiring.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@

- **User/operator result:** One `weft setup` invocation initializes local state,
wires supported project instruction surfaces, and exposes a runtime-neutral
bridge for existing agent runtimes and orchestrators.
bridge for existing agent runtimes and orchestrators. Paseo can then use the
shipped Unix action adapter to carry durable work across agent sessions.
- **In scope:** Idempotent local setup, runtime discovery, managed instruction
blocks, a machine-readable manifest, wiring diagnosis, a Codex/Claude
Code/Gemini CLI reference path, and orchestrator-facing bridge semantics.
Code/Gemini CLI reference path, orchestrator-facing bridge semantics, and the
versioned Paseo assignment/lease/checkpoint/materialization/release adapter.
- **Out of scope:** Launching, scheduling, monitoring, selecting models for, or
taking authority over external agents; remote control planes; credentials.
- **Affected domain invariants:** Assignment and lease authority, exact revision
Expand All @@ -29,6 +31,13 @@
5. `weft doctor` validates local state and wiring without mutating it.
6. Tests cover setup, repeat setup, existing instruction preservation, malformed
marker denial, unavailable runtimes, and machine-readable output.
7. The Paseo adapter uses the current CLI grammar and rejects missing or extra
action arguments instead of silently invoking stale commands.
8. A deterministic proof covers acquisition, renewal, checkpoint, dirty
observation, handoff, expired-lease reclaim, exact session replacement, and
release across two isolated workspaces.
9. Unix release archives contain the executable adapter and their smoke test
verifies its lifecycle entry point.

## Risks

Expand All @@ -37,6 +46,8 @@
failed setup must not claim a completed wiring.
- **Provider divergence/compatibility:** Runtime-specific files are convenience
adapters, not proof that a provider accepts or executes Weft operations.
- **Runtime lifecycle:** The Paseo adapter must carry explicit IDs and expected
versions without becoming a launcher, scheduler, or blind retry loop.
- **Upgrade/rollback:** The manifest format is versioned and remains local;
removal must preserve user-authored instruction content.

Expand All @@ -51,7 +62,9 @@
1. Define the runtime-wiring boundary and manifest — ADR and roadmap proof.
2. Implement setup/doctor and managed instruction blocks — focused CLI tests.
3. Add runtime/reference documentation — link checker and JSON contract proof.
4. Run full workspace, clean setup/repeat/denial checks — `make check`.
4. Add the current-grammar Paseo action adapter and lifecycle proof — runtime
scripts and installed archive smoke test.
5. Run full workspace, clean setup/repeat/denial checks — `make check`.

## Validation record

Expand All @@ -60,17 +73,22 @@
| Focused | `cargo test -p weft-cli wiring` | Passed | Managed-block, runtime detection, and non-mutating doctor coverage |
| CLI/JSON contract | `cargo test -p weft-cli setup_` | Passed | Setup, doctor, JSON envelope, and preflight path |
| Idempotency and denial | `setup_wires_project_context_idempotently_and_denies_malformed_markers` | Passed | Existing content, repeat bytes, malformed-marker, and no-state denial proof |
| Static/harness | `make check` | Passed | Documentation links, 17 CLI tests, full workspace tests, formatting, strict Clippy |
| Package/deployment | `make package-release VERSION=v0.2.0 && make test-release ARCHIVE=dist/weft-0.2.0-x86_64-unknown-linux-musl.tar.gz` | Passed | Clean installed binary ran setup, doctor, durable Change restart, and uninstall retention |
| Static/harness | `make check` | Passed | Documentation links, 18 CLI tests, full workspace tests, formatting, strict Clippy, and runtime adapter proofs |
| Runtime lifecycle | `bash scripts/test-paseo-weft-bridge.sh` | Passed | Two isolated workspaces proved acquisition, renewal, checkpoint, observation, handoff, expiry/reclaim, replacement, and release. |
| Session resume | `bash scripts/test-cli-session-resume.sh` | Passed | Canonical checkpoint and Change head survived a separate process. |
| Package/deployment | `make package-release VERSION=v0.2.1 && make test-release ARCHIVE=dist/weft-0.2.1-x86_64-unknown-linux-musl.tar.gz` | Passed | The installed archive contained and exercised `bin/weft-paseo-action` with the installed binary, then passed setup, doctor, restart, and uninstall retention. |

## Decision and follow-up

- **Decision and alternatives rejected:** ADR-0016; reject embedded scheduling,
user-home configuration mutation, and unsupported native-adapter claims.
- **Residual risks:** Native runtime plug-ins and lifecycle hooks require
runtime-specific capability proof before Weft can claim automatic lifecycle
capture.
- **Unavailable evidence:** No runtime process is launched by setup; native
runtime hooks and cross-worktree environment injection remain unproven.
- **Follow-up:** Add native adapters only after each runtime's versioned config
and lifecycle semantics are proved end-to-end.
- **Residual risks:** The adapter is proven as an explicit Unix action boundary;
it does not configure Paseo itself, and Windows has no shipped PowerShell
equivalent yet. OMC, OMG, OMX, Codex, Claude Code, and Gemini CLI still need
their own lifecycle proof before native adapter claims.
- **Unavailable evidence:** No runtime process is launched by setup; Paseo's
host-specific project/workspace registration and Windows action mapping remain
external to this repository proof.
- **Follow-up:** Keep launch/scheduling in Paseo. Add another native adapter only
after its versioned lifecycle and crash/recovery semantics are proved
end-to-end.
10 changes: 8 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
.PHONY: check docs-check harness-check qa-self-test rust-check phase0-spike phase0-native-git-spike phase0-gitbutler-spike phase3-gitbutler-live package-release test-release test-upgrade-rollback test-release-reproducibility
.PHONY: check docs-check harness-check qa-self-test rust-check agent-runtime-check phase6-paseo-bridge phase0-spike phase0-native-git-spike phase0-gitbutler-spike phase3-gitbutler-live package-release test-release test-upgrade-rollback test-release-reproducibility

RUST_HOST := $(shell rustc -vV | sed -n 's/^host: //p')

check: harness-check docs-check qa-self-test rust-check
check: harness-check docs-check qa-self-test rust-check agent-runtime-check

harness-check:
./scripts/verify-harness.sh
Expand All @@ -18,6 +18,12 @@ rust-check:
cargo test --workspace --all-targets --target $(RUST_HOST)
cargo clippy --workspace --all-targets --target $(RUST_HOST) -- -D warnings

agent-runtime-check:
./scripts/test-paseo-weft-bridge.sh
./scripts/test-cli-session-resume.sh

phase6-paseo-bridge: agent-runtime-check

phase0-native-git-spike:
./scripts/phase0-native-git-spike.sh

Expand Down
8 changes: 8 additions & 0 deletions crates/weft-cli/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,14 @@ fn setup_wires_project_context_idempotently_and_denies_malformed_markers() {
assert_eq!(setup["data"]["bridge_schema"], "weft.runtime-bridge.v1");
assert_eq!(setup["data"]["initialized"], true);
assert_eq!(setup["data"]["runtimes"].as_array().unwrap().len(), 4);
let paseo = setup["data"]["runtimes"]
.as_array()
.unwrap()
.iter()
.find(|runtime| runtime["name"] == "paseo")
.unwrap();
assert_eq!(paseo["integration"], "paseo-action-adapter-v2");
assert_eq!(paseo["adapter"], "weft-paseo-action");

let agents_path = project.join("AGENTS.md");
let agents = fs::read_to_string(&agents_path).unwrap();
Expand Down
47 changes: 41 additions & 6 deletions crates/weft-cli/src/wiring.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,22 @@ impl Runtime {
}

const fn integration(self) -> &'static str {
match self.instruction_file() {
Some(_) => "project-instructions",
None => "runtime-bridge",
match self {
Self::Paseo => "paseo-action-adapter-v2",
Self::Omc | Self::Omg | Self::Omx => "runtime-bridge",
Self::Codex | Self::ClaudeCode | Self::GeminiCli => "project-instructions",
}
}

const fn adapter(self) -> Option<&'static str> {
match self {
Self::Paseo => Some("weft-paseo-action"),
Self::Codex
| Self::ClaudeCode
| Self::GeminiCli
| Self::Omc
| Self::Omg
| Self::Omx => None,
}
}

Expand Down Expand Up @@ -107,6 +120,8 @@ struct RuntimeEntry {
detected: bool,
integration: String,
instruction_file: Option<String>,
#[serde(default)]
adapter: Option<String>,
}

pub(crate) fn setup(
Expand All @@ -131,6 +146,7 @@ pub(crate) fn setup(
detected: executable_in_paths(runtime.executable(), &path_entries),
integration: runtime.integration().to_owned(),
instruction_file: runtime.instruction_file().map(ToOwned::to_owned),
adapter: runtime.adapter().map(ToOwned::to_owned),
})
.collect(),
};
Expand Down Expand Up @@ -359,6 +375,17 @@ fn configured_bridge_problems(
if bridge.state_dir != display_path(state_dir) {
problems.push("runtime bridge points to a different state directory".to_owned());
}
for runtime in &bridge.runtimes {
if runtime.name == "paseo"
&& (runtime.integration != "paseo-action-adapter-v2"
|| runtime.adapter.as_deref() != Some("weft-paseo-action"))
{
problems.push(
"paseo runtime bridge entry lacks the current weft-paseo-action adapter; run `weft setup`"
.to_owned(),
);
}
}
}

fn bridge_view(bridge: &RuntimeBridge, initialized: bool, problems: &[String]) -> Value {
Expand All @@ -373,7 +400,8 @@ fn bridge_view(bridge: &RuntimeBridge, initialized: bool, problems: &[String]) -
"executable": runtime.executable,
"detected": runtime.detected,
"integration": runtime.integration,
"instruction_file": runtime.instruction_file
"instruction_file": runtime.instruction_file,
"adapter": runtime.adapter
})).collect::<Vec<_>>(),
"problems": problems
})
Expand Down Expand Up @@ -517,11 +545,12 @@ mod tests {
state_dir: state.display().to_string(),
protocol: "weft.cli.v1".to_owned(),
runtimes: vec![RuntimeEntry {
name: "test".to_owned(),
name: "paseo".to_owned(),
executable: "weft-test-runtime-never-present".to_owned(),
detected: false,
integration: "runtime-bridge".to_owned(),
integration: "paseo-action-adapter-v2".to_owned(),
instruction_file: None,
adapter: None,
}],
};
fs::create_dir(project.join(".weft")).unwrap();
Expand All @@ -540,6 +569,12 @@ mod tests {
.iter()
.any(|value| value.as_str().unwrap().contains("unavailable on PATH"))
);
assert!(report["problems"].as_array().unwrap().iter().any(|value| {
value
.as_str()
.unwrap()
.contains("lacks the current weft-paseo-action adapter")
}));
assert_eq!(
fs::read(state.join("metadata.sqlite3")).unwrap(),
b"not sqlite"
Expand Down
Loading