Files
rune/scripts/admit_wave_consent_bridge.py

206 lines
8.6 KiB
Python

"""Admission dispatch for wave wave-consent-bridge: the chat→task consent
card, phase 1 (owner-approved queue item 2a — the dichotomy's y/n card).
classify + activate + admit (the headless scaffolding entry law,
scripts/admit_wave_k27_composer_depth.py exemplar), admitted through the
research gate on the consent bridge's own round chain
research/factory-consent-bridge/07-qualify.json (registry entry
factory-consent-bridge, verdict PROCEED).
Artifacts this wave admits: tui/consent (the phase-1 deterministic
bridge — tier-0 declared promote language via the promote SA-D3 ladder's
explicit class, tier-1 structural suppression gate in the
construction_intent style, the one-live-card lifecycle with the
STALE_GAP_TURNS=2 lapse law, the BriefRecord consent-time snapshot, the
gold y/n strip render with default focus No), the card's wiring into the
shell (observe on chat sends/replies, y → enqueue through the
task-queue admission carrying the brief, n/Esc → dismiss demoting to the
promote hold queue), and the red-first frozen batteries. Tier-2 (model
self-report work_intent envelope) is NOT built — the synthesis phases it
as phase 2 behind kill tests K1-K3 and owner decision OD-B.
Still wiring (main session lands): the core-side widening of the task
envelope/ledger Brief slot beyond the enqueue surface's text field.
"""
import json
import os
import subprocess
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src"))
from rune import construction_activation as cact # noqa: E402
from rune import construction_admission as cadm # noqa: E402
from rune import construction_mode as cmode # noqa: E402
from rune import scaffold as sc # noqa: E402
ROOT = os.path.join(os.path.dirname(__file__), "..")
SLUG = "wave-consent-bridge"
RESEARCH_SLUG = "factory-consent-bridge"
OBJECTIVE = (
"construct the consent bridge phase 1 (owner-approved queue item 2a; "
"openspec add-native-rune-tui task 2.2's importance-aware promotion, "
"suggested-task accept/dismiss leg): when a chat message is really a "
"work request, Rune proposes starting it as a task via one live y/n "
"card at the chat pane's bottom; on y the task starts immediately "
"carrying the conversation's context. Detection tiers 0-1 "
"deterministic only: tier-0 declared promote language (the SA-D3 "
"explicit class — idiom prefixes stripped, imperative verb, not "
"keyword spam) and tier-1 structural suppression (interrogative + "
"analysis markers suppress the card; everything ambiguous passes — "
"recall-preserving, in the construction_intent resolver's style). "
"The card: one live card per chat channel, non-modal, composer keeps "
"focus, y/n keys bind only while the composer is empty, DEFAULT "
"FOCUS NO, keyboard-first with click equivalents; a newer proposal "
"supersedes; lapse after 2 recorded turns (STALE_GAP_TURNS) demotes "
"to the promote hold queue — a proposal never dies silently. Consent "
"captures the BriefRecord snapshot AT CONSENT TIME (triggering msg "
"id + verbatim text, brief statement, evidence refs, channel) and "
"the enqueue carries it through the task-queue admission (never "
"preempting a running turn). Dismissal demotes to the promote hold "
"queue with a receipt. Tier-2 model self-report is NOT built (phase "
"2, kill tests K1-K3 + owner decision OD-B). Frozen tests: "
"detection table, lapse state machine, BriefRecord verbatim "
"immutability, card render exact-width + button geometry, key "
"universe (typing never answers a card). Red-first."
)
AFFECTED = [
"tui/consent/consent.go",
"tui/consent/render.go",
"tui/consent/consent_test.go",
"tui/promote/promote.go",
"tui/promote/promote_hold_test.go",
"tui/tasks/face.go",
"tui/cmd/rune-tui/main.go",
"tui/consent/README.md",
]
def record_research_gate(marker: str) -> int:
store_dir = os.path.join(ROOT, "store", "scaffold")
scaffold = sc.Scaffold(store_dir, "rune")
doc = scaffold.load()
c = scaffold.contract(doc, f"ADMIT-{SLUG}")
if c is None:
print("contract ADMIT-<slug> not found — nothing to record")
return 1
ext = c.setdefault("ext", {})
rg = ext.get("research_gate") or {}
rg.update({
"chain": "research/factory-consent-bridge/07-qualify.json",
"verdict": "PROCEED",
"marker": marker,
"note": "consent bridge phase 1 (queue item 2a): tiers 0-1 + "
"card + snapshot + hold-queue demotion; tier-2 stays "
"phase 2 behind kill tests K1-K3 and OD-B",
})
ext["research_gate"] = rg
scaffold.commit(doc, what=f"record research_gate evidence for {SLUG}")
print("research_gate record committed on ADMIT-" + SLUG)
return 0
def main() -> int:
gate = subprocess.run(
[sys.executable, os.path.join("scripts", "check_research_gate.py"),
"admit", "--slug", SLUG, "--objective", OBJECTIVE,
"--research-slug", RESEARCH_SLUG],
capture_output=True, text=True, cwd=ROOT,
)
print(gate.stdout.strip())
if gate.returncode != 0:
print("research gate REFUSED — the law holds")
return 1
marker = ""
for line in gate.stdout.splitlines():
if line.startswith("research-gate:ALLOW") or line.startswith("exempt:research-gate:"):
marker = line.strip()
break
if not marker:
print("no ALLOW marker in gate output — refusing to mint unrecorded")
return 1
store = os.path.join(ROOT, "store", "scaffold")
scaffold = sc.Scaffold(store, "rune")
md = cmode.classify_mode(
OBJECTIVE,
target_project="rune",
project_exists=True,
affected_surface=AFFECTED,
)
print("classified mode:", md.mode)
need = {
"need_id": f"need-{SLUG}",
"objective": OBJECTIVE,
"origin": "worker",
"project": "rune",
"task_ref": SLUG,
"affected_surface": AFFECTED,
"evidence_refs": [
marker,
"research/factory-consent-bridge/06-synthesis.json",
"research/factory-consent-bridge/07-qualify.json",
"docs/openspec/changes/add-native-rune-tui/specs/dual-lane-interface/spec.md",
"docs/openspec/changes/add-native-rune-tui/tasks.md#2.2",
"src/rune/construction_intent.py",
"tui/promote/promote.go",
"tui/approvals/approvals.go",
"tui/runtime/lib/command.go",
"tui/vendor/rune/task-state/ledger/ledger.go",
],
}
act = cact.activate(md, need=need, scaffold=scaffold,
builder=SLUG)
print("activated:", act.get("activated"), act.get("mode"))
env = cadm.admit(
act,
objective=OBJECTIVE,
builder=SLUG,
affected_surface=AFFECTED,
evidence_refs=need["evidence_refs"],
origin="worker",
task_ref=SLUG,
expected_new_entities=[
"tui/consent consent bridge phase 1: tier-0/tier-1 detector "
"(declared promote language + structural suppression), "
"one-live-card Hub with STALE_GAP_TURNS lapse law, "
"BriefRecord consent-time snapshot, gold y/n strip render "
"(default focus No)",
"promote.Promote.Hold (the visible-hold demotion path the "
"card's dismissal and lapse ride)",
"shell wiring: observe on chat sends/replies, y → enqueue "
"carrying the brief, n/Esc → hold-queue demotion",
],
expected_runtime_effects=[
"phase-1 deterministic only: no model changes, no prompt "
"overhead, tier-2 work_intent envelope NOT built (kill "
"tests K1-K3 + OD-B gate it); the card never creates a "
"task silently — only consent does; a running turn is "
"never preempted (enqueue, never interrupt); no commits",
],
verification_obligations=[
"go test ./consent/ ./promote/ -count=1 green in tui/ "
"(frozen groups: detection table, lapse law, snapshot "
"immutability, render geometry, key universe, hold "
"demotion)",
"gofmt -l and go vet clean on tui/consent",
"go build ./... green in tui/",
"python scripts/check_research_gate.py sweep GREEN",
],
approval_ref="user:40-scale queue approval 2026-10-19",
contract_id=f"ADMIT-{SLUG}",
project_store=store,
project_id="rune",
)
print(json.dumps(env, indent=2, default=str)[:2400])
rc = 0 if env.get("success") else 1
if record_research_gate(marker) != 0:
return 1
return rc
if __name__ == "__main__":
sys.exit(main())