feat(orchestrator): W1 operator tasks, diff previews and bound commit asks

Operator tasks move from the Inbox into Tasks with the Waits on Operator filter. Review and commit asks carry the staged diff; a file whose old or new blob holds an armour line or a detection-pattern match, or every file when no pattern loads, ships only its path, status, mode and counts. The approval binds to the staged tree id from git write-tree, the encoded request body respects the hub limit, and the hub image ships every client module signed.py loads. Committed below the bar on the operator's word 2026-11-12 (no further per-bundle tests, one gate after the merge): the full hub suite and the W1 e2e never ran, two high MEDUSA rows (sandbox_paths.py FLOOR, permission_hook.py ask POST) wait for their acceptance.

🎯 Fix: 85 %
💥 Break: 12 %
💸 Cost: 30 %
⏱️ Time: 30 %
🛡️ Security: 50 %
🏆 Score: 100
🚦 Gate: ⛔ fix 85 % < 90, break 12 % > 5; security 50 % < 100: ask the operator before the commit

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kevin Veen-BirkenbachandClaude Opus 5.5 committed 2026-11-12 16:13:58 +00:00
1 parent fd8fc08603
commit d09ed61161
34 files changed
+2449 -111

No files matched your search

+6 -1
View File
@@ -6,6 +6,7 @@ from collections.abc import Callable
from http import HTTPStatus
from urllib.parse import urlsplit
from .diffs import DIFF_BODY_LIMIT
from .engines import STT_LIMIT
from .exchange import PLAIN_CSP, Exchange
@@ -56,6 +57,7 @@ BOARD_SIGN_ROUTE = "/api/board/sign"
SETTINGS_SIGN_ROUTE = "/api/settings/sign"
CHOICES_ROUTE = "/api/choices"
CHOICES_SIGN_ROUTE = "/api/choices/sign"
DIFF_ROUTE = re.compile(r"/api/diffs/(review|ask)/([0-9a-f]{16})")
WAV_POSTS = (STT_ROUTE, VOICE_ROUTE)
ACTION_POSTS = ("/api/rules", "/api/nl", SETTINGS_ROUTE, TTS_ROUTE, STT_ROUTE, SIGN_ROUTE, RULE_SIGN_ROUTE, CLEANUP_ROUTE, OFFICES_ROUTE, TASKS_ROUTE, VOICE_ROUTE, MESSAGE_SIGN_ROUTE, SETTINGS_SIGN_ROUTE, CHOICES_ROUTE, CHOICES_SIGN_ROUTE)
@@ -83,10 +85,13 @@ class Handler(Exchange):
cleanup = CLEANUP_STATE.fullmatch(path)
office = OFFICE_STATE.fullmatch(path)
voice = VOICE_ITEM.fullmatch(path)
diff = DIFF_ROUTE.fullmatch(path)
if self.host() is None:
self.send_text(HTTPStatus.FORBIDDEN, "forbidden host")
elif path == "/":
self.index()
elif diff:
self.answer(lambda: hub.diff_of(diff.group(1), diff.group(2)), HTTPStatus.OK)
elif vendored and vendored.group(1) in VENDOR:
self.vendored(*VENDOR[vendored.group(1)])
elif voice and voice.group(2):
@@ -237,7 +242,7 @@ class Handler(Exchange):
def do_POST(self) -> None:
path = urlsplit(self.path).path
length = self.body_length({UPLOADS: UPLOAD_LIMIT, STT_ROUTE: STT_LIMIT, VOICE_ROUTE: STT_LIMIT}.get(path, BODY_LIMIT))
length = self.body_length({UPLOADS: UPLOAD_LIMIT, STT_ROUTE: STT_LIMIT, VOICE_ROUTE: STT_LIMIT, "/mcp": DIFF_BODY_LIMIT, "/api/asks": DIFF_BODY_LIMIT}.get(path, BODY_LIMIT))
if length is None:
return
host = self.host()