Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
6194f88
Preregister fresh-agent restart receipt acceptance
Tom-R-Main Oct 10, 2026
d5de5fc
Add bounded fresh-agent restart acceptance harness
Tom-R-Main Oct 10, 2026
94d0325
Predict operation-specific collection after control API mismatch
Tom-R-Main Oct 10, 2026
600dacf
Collect explicit operation status in acceptance controls
Tom-R-Main Oct 10, 2026
be446d5
Record native acceptance controls before fresh host trials
Tom-R-Main Oct 10, 2026
ec89e6e
Retain failed agent claims and predict bounded reporting repair
Tom-R-Main Oct 10, 2026
08f4cf8
Clarify effect reporting and isolate authorized Codex trials
Tom-R-Main Oct 10, 2026
b178ff3
Record full checks and retain unavailable fixture prerequisite
Tom-R-Main Oct 10, 2026
b89236c
Predict precise host shutdown classification before Codex requalifica…
Tom-R-Main Oct 10, 2026
2c69cd8
Distinguish idle shutdown from interrupted relay calls
Tom-R-Main Oct 10, 2026
e9424b9
Record bounded restart host acceptance and remaining reporting failure
Tom-R-Main Oct 10, 2026
e64bf17
Preregister enforced completion reports and offline packaging
Tom-R-Main Oct 10, 2026
c23a934
Amend completion qualification for safe retries and durable run identity
Tom-R-Main Oct 10, 2026
7449164
Validate host reports against complete durable task ledgers
Tom-R-Main Oct 10, 2026
0c255de
Register separate completion trial after connector isolation failure
Tom-R-Main Oct 10, 2026
e7a5d9b
Handle malformed report recursion and expose ledger scope; isolate na…
Tom-R-Main Oct 10, 2026
65c514d
Record qualified completion adapter gate and retained invalid trial
Tom-R-Main Oct 10, 2026
b5d5330
Package scoped capabilities as an offline pure Python wheel with per-…
Tom-R-Main Oct 10, 2026
4e887c2
Record installed trial path-normalization diagnosis before native ver…
Tom-R-Main Oct 10, 2026
787d429
Normalize installed trial paths without resolving the virtual environ…
Tom-R-Main Oct 10, 2026
6a719a8
Record macOS worker identity evidence and revise installed probe
Tom-R-Main Oct 10, 2026
b7a79f3
Compare installed worker against observed macOS interpreter identity
Tom-R-Main Oct 10, 2026
2c5b7dc
Record installed worker success and packaging review follow-ups
Tom-R-Main Oct 10, 2026
1d9ec75
Derive diagnostic requirements and protocol version from installed ar…
Tom-R-Main Oct 10, 2026
b28af7c
Qualify installed version and generated scope; decouple metadata fixture
Tom-R-Main Oct 10, 2026
ae07e43
Record reviewed local package qualification and fixture cleanup
Tom-R-Main Oct 10, 2026
76608b6
Register fresh dependency installation and consumer setup qualification
Tom-R-Main Oct 10, 2026
091d9c3
Add isolated fresh-cache binary installation qualification harness
Tom-R-Main Oct 10, 2026
e771889
Record invalid install flags and predict strict prerequisite diagnostics
Tom-R-Main Oct 10, 2026
23bab30
Use supported binary-only uv option without conflicting alias
Tom-R-Main Oct 10, 2026
3d4baa6
Register deeper home and actual-runtime isolation after clean depende…
Tom-R-Main Oct 10, 2026
eac65c6
Add strict prerequisites and private actual-runtime provenance for fr…
Tom-R-Main Oct 10, 2026
445e3c8
Register isolated launch remediation after cwd shadowing review
Tom-R-Main Oct 10, 2026
2613cff
Isolate server worker and report imports from host working directories
Tom-R-Main Oct 10, 2026
1dffc6b
Record reviewed fresh dependency and isolated native installation qua…
Tom-R-Main Oct 10, 2026
1ce65f8
Register consumer artifact CI and delivery verification
Tom-R-Main Oct 10, 2026
fc525da
Check the standalone capability artifact in dedicated consumer CI
Tom-R-Main Oct 10, 2026
b6c971a
Record final review and register direct parser dependency qualification
Tom-R-Main Oct 10, 2026
2ea6ff9
Declare the strict diagnostic parser and clarify isolated automation
Tom-R-Main Oct 10, 2026
75994a7
Record independently reviewed capability delivery qualification
Tom-R-Main Oct 10, 2026
7669d3d
Preserve verified hosted artifacts and close delivery predictions
Tom-R-Main Oct 10, 2026
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
57 changes: 57 additions & 0 deletions .github/workflows/capability-package.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
name: capability-package

on:
push:
branches: [main]
pull_request:

permissions:
contents: read

jobs:
consumer-artifact:
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
python: "3.12"
- os: macos-15
python: "3.12"
- os: macos-15
python: "3.14"
runs-on: ${{ matrix.os }}
timeout-minutes: 15
defaults:
run:
working-directory: jevdual
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python }}
- uses: astral-sh/setup-uv@v5
with:
enable-cache: false
version: "0.12.5"
- name: Rebuild and check dependency-free installed CLI
run: python scripts/capability_package_check.py --run-dir "$GITHUB_WORKSPACE/output/ci-package"
- name: Fresh binary dependency installation on macOS
if: runner.os == 'macOS'
run: |
python scripts/capability_clean_install.py \
--python "$(command -v python)" \
--wheel "$GITHUB_WORKSPACE"/output/ci-package/build-one/*.whl \
--run-dir "$GITHUB_WORKSPACE/output/ci-dependencies"
- name: Retain candidate artifact and qualification receipts
if: always()
uses: actions/upload-artifact@v4
with:
name: capability-${{ matrix.os }}-py${{ matrix.python }}
if-no-files-found: error
path: |
output/ci-package/receipt.json
output/ci-package/build-one/*.whl
output/ci-package/*.log
output/ci-dependencies/receipt.json
output/ci-dependencies/*.log
2 changes: 2 additions & 0 deletions jevdual/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ accessibility tree (`cua-driver` 0.28.2, pinned). The operator surface is the `f
(`python/jevdual/cli.py`, `session.py`); see the [top-level README](../README.md#drive-it). Measured hot
paths in the DOM pipeline run in Rust through PyO3, each with a pure-Python twin and equality tests.

For the standalone agent capability wheel, see [installation and per-job configuration](docs/capability-packaging.md). It includes a journal-validated completion adapter for the scoped browser-to-TextEdit flow; it does not require Rust or a model provider account.

The browser tables below are the Q6 to Q9 measurements; native and coexistence results (Q10, Q15) are in
the top-level README and `docs/experiments/`.

Expand Down
109 changes: 109 additions & 0 deletions jevdual/docs/capability-packaging.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# Install the capability slice

The capability wheel runs the current macOS browser-to-TextEdit slice without Rust or a model provider account. It contains Python runtime modules, `arbiter.toml`, the MIT license, dependency metadata and the `footwork-capability` entry point. The existing maturin build still provides optional Rust acceleration.

The scope is narrow: read one loopback HTTP browser fixture and append to one identified TextEdit document. The caller supplies existing process IDs, window IDs, URL and document path. Footwork checks identity when binding and before input. This package does not discover arbitrary apps or authorize a broader job.

## Build and inspect the actual artifact

From `jevdual/`, use Python 3.11 or newer:

```sh
python scripts/build_capability_wheel.py --output /absolute/owned/artifacts
python scripts/build_capability_wheel.py --verify /absolute/owned/artifacts/jevdual-0.0.1-py3-none-any.whl
```

The builder uses the standard library and downloads nothing. It refuses to replace an existing wheel. The verifier compares the ZIP inventory and bytes with the permitted source inventory, checks required runtime resources, verifies every RECORD hash and size, and checks the declared license path. Fixed ZIP timestamps and ordering make builds from unchanged source repeatable in the same Python environment.

Only `.py` runtime files, the policy file and generated distribution metadata ship. Research runs, tests, caches and compiled extensions are excluded; source symlinks are rejected. Dependencies are declared rather than bundled. The wheel retains the full current dependency list, including exact MCP, browser-use, cua-driver and psutil pins. It is not a reduced-dependency distribution.

### Consumer artifact checks

The separate `capability-package` CI workflow builds the wheel twice, verifies its bytes and RECORD, and installs it offline without dependencies in a fresh environment. It checks the installed console entry point, isolated imports from a directory containing shadow packages, strict refusal of missing prerequisites, and conservative reporting when the journal is absent. Run the same check locally with an existing Python and uv:

```sh
python scripts/capability_package_check.py --run-dir /absolute/footwork/output/new-package-check
```

CI defines Linux/Python 3.12 and macOS arm64/Python 3.12 and 3.14 profiles. The macOS profiles also run U1's fresh-cache, private-state, binary-only dependency installer. Receipts and the candidate wheel are retained as CI artifacts. These checks neither launch native input nor qualify macOS permission prompts. Each interpreter's two builds must match; byte identity across operating systems or compression-library versions is not required.

## Install into a fresh environment

With an existing Python and uv, create an owned environment and install the local wheel with its dependencies:

~~~sh
uv --no-config venv --python /absolute/python3 /absolute/owned/venv
uv --no-config pip install --python /absolute/owned/venv/bin/python \
--default-index https://pypi.org/simple --only-binary :all: \
/absolute/owned/artifacts/jevdual-0.0.1-py3-none-any.whl
/absolute/owned/venv/bin/python -I -m jevdual.capability.cli doctor --strict
~~~

This installation fetches dependencies. Binary-only mode refuses missing wheels instead of compiling them. The [fresh-install qualification](../results/clean-installation-20261010/report.md) covers macOS arm64 with Python 3.12.6 and 3.14.7; broader Python metadata does not qualify every version or architecture. Existing Chrome, TextEdit, macOS frameworks and permissions remain prerequisites.

Doctor reports prerequisite status and blocker codes, with interpreter, architecture and installed distribution locations. Ordinary diagnostics return 0 when the report finishes. With --strict, blocked prerequisites return 1; malformed configuration returns 2. Met prerequisites do not certify a window, effect, current content or completed job. Screen Recording status is advisory here; a capture route still needs its own successful check.

Generated server and report commands use Python isolation mode (-I), preserving the environment interpreter. The detached worker uses the same mode. This excludes the host's current directory, Python environment overrides and user site packages from imports. Generated host settings also disable browser-use telemetry and cloud synchronization; they do not block all possible network access.

Optional qualification diagnostics use FOOTWORK_RUNTIME_AUDIT_DIR pointing at an existing private owned directory. Actual server/worker contexts record loaded module and SDK library origins after binding. These files are diagnostic data, separate from receipts and authority, with no field text or credential values. Normal setup requires no audit directory.

## Install offline

Use an owned environment with the declared dependencies already available. For example, with `uv` already installed:

```sh
uv pip install --offline --no-deps --python /absolute/owned/venv/bin/python /absolute/owned/artifacts/jevdual-0.0.1-py3-none-any.whl
/absolute/owned/venv/bin/python -I -m jevdual.capability.cli doctor
```

`--no-deps` installs the artifact alone. It does not prove that a fresh machine can resolve every dependency offline. A consumer needs a compatible Python environment, the declared dependencies, native macOS frameworks, an existing browser fixture and TextEdit document, and applicable Accessibility and Screen Recording permissions. `doctor` reports versions and existing permission status for its process; it never requests permissions, opens settings, starts apps or creates a journal. Run doctor from the same host or terminal that will launch the server: macOS may attribute permissions differently across hosts. Treat a bind accessibility error as authoritative even if another process's doctor reported granted. Missing prerequisites remain the caller's responsibility.

Run consumer checks outside the checkout. The console entry point shares the CLI implementation but does not enable Python isolation; `PYTHONPATH` and user site packages can still affect it. Automation and host adapters should use the generated `python -I -m jevdual.capability.cli` commands. The console entry point is for interactive use. Detached execution also uses isolation mode with that interpreter to launch the installed `jevdual.capability.execution_worker` module.

## Create one configuration per job

Choose a new absolute run directory with an existing nonsymlink parent. Supply the owned fixture's actual identifiers:

```sh
/absolute/owned/venv/bin/python -I -m jevdual.capability.cli config \
--run-dir /absolute/owned/job-run \
--source-pid 123 --source-window 456 \
--source-url http://127.0.0.1:8000/ \
--destination-pid 789 --destination-window 1011 \
--document /absolute/owned/invoice.txt
```

The identifiers above are placeholders, not a reusable registration. Configuration creates a private directory and private JSON files: `scope.json`, `host.json`, `report-schema.json` and `run.json`. Its opaque task ID is recorded in the scope. `host.json` contains a per-job MCP stdio server command using the configuring interpreter's absolute path, preserving its virtual environment. Adapt this ephemeral configuration to the host's own configuration format; nothing is registered globally.

Reuse the directory, task and journal only to reconnect or restart the same job. A new job needs a new directory. Configuration fails if its directory exists and does not create a journal or start a server. Retain the run directory as the operation history and recovery context; do not substitute an empty journal to bypass an unresolved operation.

Check a run without taking journal ownership:

```sh
/absolute/owned/venv/bin/python -I -m jevdual.capability.cli doctor \
--scope /absolute/owned/job-run/scope.json \
--journal /absolute/owned/job-run/operations.sqlite
```

Diagnostics report whether the configured document and processes exist; window identity is checked at bind. If the task already has operations, diagnostics warn that the full history belongs in its completion report. A read-only ledger check never performs startup recovery or dispatches input. Doctor uses the declared `packaging` dependency to parse the installed distribution's requirements. Missing metadata or parser makes compatibility unverified and blocks strict prerequisites.

## Enforce the completion adapter

The host must produce structured claims matching `report-schema.json`. The run manifest supplies `report_adapter`, including the durable task ID. After stopping the host, pass its final report through that command, for example:

```sh
/absolute/owned/venv/bin/python -I -m jevdual.capability.cli report \
--journal /absolute/owned/job-run/operations.sqlite \
--task task_REPLACE_WITH_GENERATED_ID \
--input /absolute/owned/job-run/host-report.json
```

Publish only the returned `trusted_summary`, including when validation rejects the host's claims. Never publish raw host prose as a fallback. `--summary` emits only that string. Exit 0 means the claims are accepted, exit 1 means they are rejected, and exit 2 means the input or journal cannot be validated. An accepted `unknown` report still means historical effects are unknown.

The summary describes the configured task's admitted operation history as of its ledger snapshot. It does not certify current GUI content, disk persistence or the host's wider goal. Merely offering the MCP `report` tool does not enforce the host's final answer; the publication adapter is required.

## References

The local chrome-devtools-mcp package verifier checks the artifact produced for publication rather than trusting a source file list. This builder applies that mechanism to its actual wheel; it copies no upstream source. The license metadata follows [PEP 639](https://peps.python.org/pep-0639/): `License-File: LICENSE` resolves under the wheel's `.dist-info/licenses/` directory.

The setup improvements use the structured prerequisite/error mechanism in local Cua, ambient-configuration isolation in Hermes, and exact emitted-command MCP discovery in Open Computer Use. Implementations are independent. Newer reference versions are not dependency upgrades; Footwork retains its pinned seams. The newer Cua doctor also performs legacy-file cleanup, so it is not used as a read-only verifier here.
56 changes: 51 additions & 5 deletions jevdual/docs/capability-slice.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# The first Footwork capability slice

This local development slice exposes four MCP tools: `bind`, `observe`, `act`, `status`.
This local development slice exposes five MCP tools: `bind`, `observe`, `act`, `status`, `report`.
An existing agent reads a value from a local browser fixture, appends it to a selected
TextEdit document, and checks exact field content. The host owns the plan. Footwork
records append intent before dispatch. An independent one-action worker retains completion
evidence across MCP process death; uncertain attempts stay blocked. `status` never repeats input.

This is a scoped macOS prototype, not a released installable package. Browser and native
This is a scoped macOS prototype. See [local packaging](capability-packaging.md) for the offline wheel and per-run host setup. Browser and native
drivers remain at the repository's pinned versions. Live agent acceptance and the raw
backend comparison have separate preregistrations in
[the notebook](notebook/2026-10-09-capability-slice.md) and
Expand All @@ -26,7 +26,14 @@ Those two fixes now pass [six fresh agent content cases](../results/capability-t
including empty and whitespace-only documents; the original C1 comparison remains unchanged.
Restart reconciliation now passes [twelve actual MCP/native process-crash cases](../results/restart-reconciliation-20261010/report.md),
including delayed execution and identical external text. This qualification uses a scripted MCP caller;
a fresh agent trial on the new receipt wording and latency remains future work.
a fresh agent trial on the new receipt wording and latency is recorded in
[A1](../results/restart-agent-acceptance-20261010/report.md). On the isolated reporting extension,
both hosts pass eight selected execution cases, but Codex omits execution uncertainty in one final
answer; combined answer acceptance is seven of eight. A2 adds an enforced journal validator:
[four fresh Codex cases](../results/operation-completion-20261010/report.md) pass execution,
structured-report validation and published-summary checks. The adapter closes that output path;
interactive host prose remains outside enforcement. Original A1, C1 and R1 results are unchanged. The reporting extension remains on the unmerged
`codex/restart-agent-acceptance` branch.
Evidence covers the scoped prototype. Arbitrary apps, saved-file persistence,
parallel hosts and a packaged public release remain unqualified.

Expand Down Expand Up @@ -68,7 +75,7 @@ uv run --no-sync python -m jevdual.capability.mcp_server \
--journal /absolute/project/run/operations.sqlite
```

Run this from `jevdual/`. A Claude Code registration can invoke the environment's Python
Run this from `jevdual/`. An ephemeral Claude Code configuration for one run can invoke the environment's Python
directly; the paths below are placeholders to replace before running:

```json
Expand All @@ -86,7 +93,10 @@ directly; the paths below are placeholders to replace before running:
}
```

The prototype does not write the host's global configuration. A temporary MCP config
Generate a fresh task ID and scope for each new host job; reuse that scope only for reconnects
and restarts of the same run. Do not register one reusable task scope globally. A deliberately
reused task reports its entire ledger, including older attempts. The prototype does not write
the host's global configuration. A temporary MCP config
under the owned run directory can be passed to a fresh host session. Experiments must
restrict the session to these tools; an agent using shell/source or other computer-use
tools has not demonstrated this interface's usability. Provider calls and host configuration
Expand Down Expand Up @@ -176,3 +186,39 @@ unreadable and ambiguous fields return no target with an unavailable reason. Off
`value_exact: true`; a bounded value can still be marked truncated. Readback uses the scoped document,
AX role and unique field frame because Cua's content-derived labels can change after an empty append.
This does not qualify disk persistence. Restart evidence has its own [preregistration](experiments/R1-restart-reconciliation.md).

## Completion reports

`report` checks a structured host claim against every operation in the configured task ledger,
including records older than status's recent list. It never drives the surface or reconciles a
receipt. Copy the exact `op_...` IDs returned by `act`/`status` into the report; descriptions of
planned actions are not IDs.

```json
{
"completion": "unknown",
"operations": [{"operation": "op_123456abcdef", "disposition": "unknown"}],
"persistence": "not_checked"
}
```

Unknown or dispatching records prevent confirmed execution; matching current text cannot cure
that uncertainty. Partial input also prevents operation completion. Refused/not_sent attempts
remain listed as confirmed no-input attempts and can coexist with a later applied operation.
At least one applied record is required. Omitted, foreign, duplicate or mismatched IDs and
unsupported saved/unsaved assertions are rejected. Honest host downgrades are retained as
uncertified judgments. Reports show the full ledger span and observation time, not a promise
about future state or the whole job.

A tool alone cannot control a model's final prose. The enforcing host adapter takes journal and
task from trusted startup configuration, validates the final JSON, and publishes **only**
`trusted_summary`. Use `footwork-capability report --journal ... --task ... --input ... --summary`
for this boundary. Exit codes are 0 for an accepted structured claim, 1 for a rejected claim
with journal evidence, and 2 for malformed/unavailable validation. Exit 0 can correctly report
unknown; it does not mean the job succeeded. Never fall back to raw host text on failure.

The report CLI reads an existing private database in SQLite read-only mode without journal
startup recovery or owner takeover. It works with a live owner and retained crash WAL. The
snapshot is bounded to 256 task records; overflow fails closed. Keep the journal and its
execution directory private, and retain unresolved operation evidence until an operator has
resolved the uncertainty.
Loading
Loading