mirror of
https://github.com/andrewyng/openworker.git
synced 2026-09-13 07:40:18 +00:00
The auto_approve flag (and its shadow sibling) become first-class settings
instead of hand-edited TOML, and the Auto-Approve mode entry appears in the
picker only when the flag is on.
Server:
- manager: auto_approve()/auto_approve_shadow() read prefs.json first,
falling back to the config.toml value a power user may have set; both
writers persist to prefs. Both stores are user-global, so a cloned repo
still can't enable either (the 1.5 invariant, unchanged).
- get_settings() exposes both; POST /v1/settings/auto-approve and
/auto-approve-shadow write them (same shape as context-bar).
- Session builds pass the prefs-backed values into build_engine via new
optional auto_approve/auto_approve_shadow overrides (None = config value),
so a Settings flip takes effect on the next session build with no restart.
Scheduled runs keep reading config only - they are unattended, so the
live reviewer can never fire there regardless.
GUI:
- Mode picker: the Auto-Approve entry is `gated` - shown when
getSettings().auto_approve is true, fetched on menu open. A session
already IN auto-approve always shows its own entry so the current mode
stays legible even if the flag was later turned off. This replaces the
TEST-ONLY unconditional entry.
- Settings: AutoApproveCard with the feature toggle and the nested shadow-
evaluation toggle ("records what it would have decided next to your own
choice - without changing anything").
- api.ts: ModelSettings.auto_approve/auto_approve_shadow + setters.
Verified live against the running sidecar: flag off hides the entry on an
interactive session, flag on shows it, the Settings toggles round-trip and
persist. tests/test_auto_approve_settings.py (6): defaults, REST round-
trip, restart persistence, config fallback, prefs-beats-config, and the
build_engine override. tsc clean; 111 GUI unit tests pass.
81 lines
3.3 KiB
Python
81 lines
3.3 KiB
Python
"""Settings wiring for Auto-Approve (spec §1.5 / Part 6 step 3).
|
|
|
|
Covers the prefs-backed feature flag + shadow toggle: default off, REST round-trip,
|
|
persistence across a manager restart, config.toml fallback, and the build-engine override
|
|
so a flag flip takes effect on the next session build without a config edit.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
from fastapi.testclient import TestClient
|
|
|
|
from coworker.server.app import create_app
|
|
from coworker.server.manager import SessionManager
|
|
|
|
|
|
@pytest.fixture
|
|
def client(tmp_path, monkeypatch):
|
|
# Isolate config.toml: no hand-set flag leaking in from the dev machine.
|
|
monkeypatch.setenv("COWORKER_STATE_DIR", str(tmp_path / "state"))
|
|
return TestClient(create_app(SessionManager(data_dir=tmp_path / "data")))
|
|
|
|
|
|
def test_flags_default_off(client):
|
|
s = client.get("/v1/settings").json()
|
|
assert s["auto_approve"] is False
|
|
assert s["auto_approve_shadow"] is False
|
|
|
|
|
|
def test_set_auto_approve_roundtrip(client):
|
|
r = client.post("/v1/settings/auto-approve", json={"auto_approve": True}).json()
|
|
assert r["ok"] and r["auto_approve"] is True
|
|
assert client.get("/v1/settings").json()["auto_approve"] is True
|
|
# Off again.
|
|
client.post("/v1/settings/auto-approve", json={"auto_approve": False})
|
|
assert client.get("/v1/settings").json()["auto_approve"] is False
|
|
|
|
|
|
def test_shadow_is_independent_of_the_live_flag(client):
|
|
client.post("/v1/settings/auto-approve-shadow", json={"auto_approve_shadow": True})
|
|
s = client.get("/v1/settings").json()
|
|
assert s["auto_approve"] is False # untouched
|
|
assert s["auto_approve_shadow"] is True
|
|
|
|
|
|
def test_flags_persist_across_restart(tmp_path, monkeypatch):
|
|
monkeypatch.setenv("COWORKER_STATE_DIR", str(tmp_path / "state"))
|
|
data_dir = tmp_path / "data"
|
|
c1 = TestClient(create_app(SessionManager(data_dir=data_dir)))
|
|
c1.post("/v1/settings/auto-approve", json={"auto_approve": True})
|
|
|
|
reborn = SessionManager(data_dir=data_dir)
|
|
assert reborn.auto_approve() is True
|
|
assert TestClient(create_app(reborn)).get("/v1/settings").json()["auto_approve"] is True
|
|
|
|
|
|
def test_prefs_falls_back_to_config_when_unset(tmp_path, monkeypatch):
|
|
# No prefs key set → the manager reads config.toml. Point config at a file that enables it.
|
|
state = tmp_path / "state"
|
|
state.mkdir(parents=True)
|
|
(state / "config.toml").write_text("auto_approve = true\n")
|
|
monkeypatch.setenv("COWORKER_STATE_DIR", str(state))
|
|
mgr = SessionManager(data_dir=tmp_path / "data")
|
|
assert mgr.auto_approve() is True # config value, no prefs entry
|
|
# A prefs write then wins over config.
|
|
mgr.set_auto_approve(False)
|
|
assert mgr.auto_approve() is False
|
|
|
|
|
|
def test_build_engine_override_beats_config(tmp_path, monkeypatch):
|
|
# build_engine's auto_approve arg (what the server passes) overrides the config value.
|
|
monkeypatch.setenv("COWORKER_STATE_DIR", str(tmp_path / "state"))
|
|
from coworker.agent import build_engine
|
|
from coworker.agents.chat import chat_agent
|
|
|
|
# config has it off by default; override to on → a reviewer is attached.
|
|
engine = build_engine(agent=chat_agent(), auto_approve=True, auto_approve_shadow=False)
|
|
assert engine.reviewer is not None
|
|
engine2 = build_engine(agent=chat_agent(), auto_approve=False, auto_approve_shadow=False)
|
|
assert engine2.reviewer is None
|