Files
rune/scripts/admit_wave_dispatch_classifier.py
Nick 76f7b535a1
CI Pipeline / documentation (push) Canceled after 0s
CI Pipeline / canary (push) Canceled after 10s
CI Pipeline / type-check (push) Canceled after 0s
CI Pipeline / gitea-offline (push) Canceled after 0s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Canceled after 0s
CI Pipeline / required-checks (push) Canceled after 0s
CI Pipeline / unit-tests (push) Canceled after 11s
CI Pipeline / go-surface (push) Canceled after 11s
CI Pipeline / rust-surface (push) Canceled after 11s
CI Pipeline / best-practices (push) Canceled after 10s
Auto-version and Release / release (push) Canceled after 30s
CI Pipeline / lint (push) Canceled after 19s
deploy / deploy (push) Canceled after 33s
feat(dispatch-classifier): the DISPATCH CLASSIFIER — solo vs parallel vs queue decided, never specified (red-first)
Stdlib-only Go module deciding each wave's lane so the owner stops
specifying: hot-state registry ~/.rune/hot/dispatch_registry.json
(lock/unlock/show, 4h STALE + --prune), classify with the one
data-driven keyword->surface map, prefix-overlap queue-behind (owner
-> PRIORITY-QUEUE with landing-boundary yield), owner-origin solo
latency, disjoint-surface parallel split suggestion, --apply lock take
atomic and fail-loud on locked/stale/changed; scripts/dispatch_check.sh
wraps the pre-spawn discipline. go test -race green, vet + gofmt clean.
Contract ADMIT-wave-dispatch-classifier.
2026-10-22 18:32:53 +01:00

166 lines
8.1 KiB
Python

"""Admission dispatch for the DISPATCH CLASSIFIER wave (the owner's
mechanism-not-law directive: "can we have something that intelligently
decides that so i dont have to specify").
Builds src/go/dispatch-classifier — a zero-dependency (stdlib-only) Go
module that decides solo-attention vs parallel-factory vs queue for
each incoming wave so the owner never specifies: (1) REGISTRY —
~/.rune/hot/dispatch_registry.json hot-state entries {agent_id,
surfaces, kind, since, owner_origin} with lock/unlock/show CLI
subcommands and 4h STALE flagging + --prune; (2) CLASSIFY —
`classify --brief <file> [--paths p1,p2] [--owner-origin] [--apply]`:
extract candidate surface prefixes (explicit paths win, else brief
paths, else keyword→surface heuristics kept in ONE data-driven map),
compute prefix overlap vs the registry's locked surfaces, and emit the
verdict QUEUE-BEHIND (PRIORITY-QUEUE when owner-origin; lock-holder
yields at next landing boundary) / SOLO (owner watching, latency wins)
/ PARALLEL (multiple disjoint surfaces with a suggested split) / SOLO;
--apply takes the lock atomically, fail-loud on stale/locked; (3)
TESTS — red-first per rule: overlap detection, owner-origin priority,
multi-surface split suggestion, lock/unlock lifecycle, stale-lock
handling, prefix-vs-exact matching; go test -race green, vet/gofmt
clean; (4) DISCIPLINE WIRING — scripts/dispatch_check.sh, the one-liner
wrapper the session agent runs before spawning anything, documented in
the module README in plain English.
approval_ref 'user:dispatch classifier directive 2026-10-22'.
"""
import json
import os
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__), "..")
OBJECTIVE = (
"Build the DISPATCH CLASSIFIER: create the self-contained stdlib-only "
"Go module src/go/dispatch-classifier that decides solo-attention vs "
"parallel-factory vs queue for each incoming wave so the owner never "
"specifies — implement the hot-state registry "
"~/.rune/hot/dispatch_registry.json with lock/unlock/show subcommands "
"(entries {agent_id, surfaces, kind, since, owner_origin}; entries "
"older than 4h flagged STALE in show with a --prune flag), implement "
"classify --brief <file> [--paths p1,p2] [--owner-origin] [--apply] "
"that extracts candidate surface prefixes (explicit paths win, else "
"brief-named repo-relative paths, else keyword→surface heuristics "
"kept in one data-driven map), computes prefix overlap against the "
"registry's locked surfaces, and emits the JSON verdict "
"{decision, reason, surfaces, locks_to_take, queue_behind} — "
"QUEUE-BEHIND naming the agent and surface when any candidate "
"surface is locked by an in-flight agent (PRIORITY-QUEUE on "
"owner-origin, noting the lock-holder yields at its next landing "
"boundary), SOLO when owner-origin (full-attention lane, latency "
"wins), PARALLEL with a suggested split when the brief names "
"multiple disjoint surfaces, else SOLO — with --apply taking the "
"lock atomically and failing loud on stale/locked; add red-first "
"tests per rule (overlap detection, owner-origin priority, "
"multi-surface split suggestion, lock/unlock lifecycle, stale-lock "
"handling, prefix-vs-exact matching) green under go test -race with "
"vet and gofmt clean; add scripts/dispatch_check.sh, the one-liner "
"wrapper the session agent runs before spawning anything (reads the "
"brief, runs classify, prints the verdict, locks with --apply), and "
"document usage in the module README in plain English"
)
AFFECTED = [
"src/go/dispatch-classifier/",
"scripts/admit_wave_dispatch_classifier.py",
"scripts/dispatch_check.sh",
]
def main() -> int:
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": "need-dispatch-classifier",
"objective": OBJECTIVE,
"origin": "worker",
"project": "rune",
"task_ref": "wave-dispatch-classifier",
"affected_surface": AFFECTED,
"evidence_refs": [
"src/go/failure-filer (stdlib-only module pattern: go.mod, "
"main.go, flat package, *_test.go)",
"~/.rune/hot/ hot-state file convention (task_state.json in "
"src/go/webfeed/internal/webfeed/server.go:390)",
"scripts/check_landing.py rule-5 scope matcher (_scope_covers "
"prefix law — reused for registry surface overlap)",
"scripts/admit_wave_presentation.py (this admission pattern)",
"scripts/dispatch_check.sh discipline wrapper (new)",
],
}
act = cact.activate(md, need=need, scaffold=scaffold,
builder="wave-dispatch-classifier")
print("activated:", act.get("activated"), act.get("mode"))
env = cadm.admit(
act,
objective=OBJECTIVE,
builder="wave-dispatch-classifier",
affected_surface=AFFECTED,
evidence_refs=need["evidence_refs"],
origin="worker",
task_ref="wave-dispatch-classifier",
expected_new_entities=[
"src/go/dispatch-classifier (stdlib-only Go module: go.mod, "
"main.go, registry.go, classify.go, README.md)",
"src/go/dispatch-classifier (hot-state registry "
"~/.rune/hot/dispatch_registry.json: lock/unlock/show, "
"atomic writes, 4h STALE flagging + --prune)",
"src/go/dispatch-classifier (classify: surface extraction — "
"explicit paths, brief parsing, one data-driven keyword map; "
"prefix overlap vs locked surfaces; QUEUE-BEHIND / "
"PRIORITY-QUEUE / SOLO / PARALLEL verdicts with suggested "
"split; --apply takes the lock atomically, fail-loud)",
"src/go/dispatch-classifier (red-first tests per rule: "
"overlap, owner-origin priority, split suggestion, "
"lock/unlock lifecycle, stale handling, prefix-vs-exact; "
"go test -race green, vet + gofmt clean)",
"scripts/dispatch_check.sh (session-agent pre-spawn "
"discipline wrapper: read brief, classify, print verdict, "
"--apply locks)",
"tests: red-first suites per rule, -race green",
],
expected_runtime_effects=[
"an agent can lock/unlock its surfaces in the hot registry "
"and show prints the table with STALE flags for entries "
"older than 4h; --prune removes stale entries",
"classify reads a brief and returns the verdict JSON "
"without the owner specifying the mode; overlap with an "
"in-flight lock queues behind the named agent; owner-origin "
"wins latency (SOLO) or jumps the queue (PRIORITY-QUEUE); "
"disjoint multi-surface briefs suggest a parallel split",
"the session agent runs scripts/dispatch_check.sh before "
"spawning anything and follows the printed verdict",
],
verification_obligations=[
"cd src/go/dispatch-classifier && go test ./... -race green; "
"go vet ./... clean; gofmt -l . empty",
"CLI proofs: lock/unlock/show lifecycle, stale flagging + "
"prune, classify verdicts for overlap/owner-origin/split "
"cases, --apply lock take + fail-loud on stale/locked",
"scripts/dispatch_check.sh round-trip on a real brief file",
],
approval_ref="user:dispatch classifier directive 2026-10-22",
contract_id="ADMIT-wave-dispatch-classifier",
project_store=store,
project_id="rune",
)
print(json.dumps(env, indent=2, default=str)[:2400])
if __name__ == "__main__":
raise SystemExit(main())