feat(prompt): slash commands in prompt input (PR-5 #30 reopened) - #33
Merged
Conversation
…ustering) New acc/cluster.py module: * ClusterPlan dataclass with field invariants (subagent_count >= 1, difficulty in [0, 1]). * In-memory registry (register/lookup/unregister/list) with optional Redis mirror via redis_compat — edge-friendly: works without Redis. * new_cluster_id() emits c-prefixed UUIDs to discriminate from task_id (plan-…) and agent_id (<role>-<hex>) in log lines / dashboards. * fetch_cluster_async() backfills from Redis on local-cache miss. Wire-protocol propagation: * acc/plan.py:_publish_task_assign accepts optional cluster_id + target_agent_id kwargs; both attached only when supplied so legacy single-agent payloads stay byte-identical. * acc/agent.py:_handle_task echoes inbound cluster_id on every outbound TASK_PROGRESS and TASK_COMPLETE so cluster fan-in aggregators see a complete event stream per cluster. TUI fan-out: * NATSObserver gains register_cluster_listener / unregister_cluster_listener + internal _fan_out_cluster helper. Every cluster-tagged TASK_PROGRESS / TASK_COMPLETE fans out to per-cluster_id callbacks with per-callback exception isolation (one buggy listener cannot starve others). Payloads without cluster_id are silently ignored. 21 new tests in tests/test_cluster_propagation.py covering: dataclass invariants, registry round-trip, sync/async lookup miss behaviour, TASK_ASSIGN cluster_id presence/absence, listener fan-out, unregister idempotency, multi-listener support, exception isolation. Foundation for PR-2 (estimator + sub-cluster spawn) and PR-4 (TUI cluster panel). No user-visible change in this PR. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…-out (PR-2)
New acc/estimator.py:
* TaskComplexity dataclass — narrow input surface (tokens, task_type,
required_skills, has_external_io).
* Estimator Protocol — pure callable, easy to fuzz + custom-implement.
* default_estimator: token-budget heuristic (base + ceil(tokens/per_n))
with keyword-driven difficulty bumps. Output clamped to
[1, min(cap, role.max_parallel_tasks)] — defence in depth on top of
Cat-A A-019.
* build_estimator() dispatcher: 'heuristic' (default) | 'fixed' |
'module:dotted.path'. Unknown / failing strategies log + fall back
to heuristic — arbiter NEVER crashes on a buggy operator config.
* slice_skill_mix() round-robins skills across N sub-agents so no one
member loads every skill prompt.
* derive_complexity() turns a raw step payload into TaskComplexity
(token estimate via len(text)//4, [SKILL: ...] hint extraction).
acc/config.py:
* RoleDefinitionConfig gains max_parallel_tasks: int = 1 (legacy: no
parallelisation) and estimator: dict[str, Any] = {}. Schema is a
free-form dict on purpose so 'module:' strategies don't need
schema bumps.
acc/plan.py:
* PlanExecutor.__init__ accepts optional role_resolver / skill_resolver
callbacks. Without them, dispatch is byte-identical to PR-1.
* _maybe_build_cluster: consults the estimator, returns ClusterPlan or
None for single-agent fallback. All exceptions logged + downgraded
to single-agent dispatch.
* _dispatch_cluster: fans one PLAN step out as N TASK_ASSIGN payloads
sharing one cluster_id (PR-1 wire shape).
* on_task_complete: cluster aggregation — step transitions only after
all members report. COMPLETE if every member ok, FAILED if any one
blocked. Cluster auto-unregistered on transition.
regulatory_layer/category_a/constitutional_rhoai.rego:
* Bumped 0.4.0 → 0.5.0.
* Two new rules:
- deny_cluster_oversize: subagent_count > role.max_parallel_tasks
- deny_cluster_nonpositive: subagent_count < 1
Both gate action='CLUSTER_SPAWN' so external (Gatekeeper) admission
enforces the same invariant as the in-process clamp.
26 new tests in tests/test_estimator.py — heuristic shape, role-cap
clamp, [0,1] difficulty bound, skill_mix precedence, fixed strategy,
module: import + import-failure fallback, unknown-strategy fallback,
slice_skill_mix round-robin, derive_complexity SKILL-hint extraction,
PlanExecutor single-agent fallback, fan-out wire-shape, A-019 in-process
clamp, estimator-failure → single-agent fallback, cluster aggregation
all-members-must-report, any-blocked-fails-step.
47 passed across PR-1 + PR-2 test modules; 104 across all related
modules.
Foundation complete for PR-4 (TUI cluster panel) which subscribes via
the PR-1 register_cluster_listener and PR-3 (markdown role authoring,
independent of this PR).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ustering) acc/tui/widgets/cluster_panel.py (new): * ClusterPanel widget — collapsible header + per-cluster body. * Render-only: takes a snapshot dict, emits Rich-marked-up text. * Driven manually via render_now(); no reactive watcher (Textual >=0.80 watcher path can re-enter layout, breaks Pilot tests). * 30 s grace window after a cluster finishes — operator gets to read the final state before the row disappears. * Skill name extracted heuristically from "Calling skill:<name>" step labels (capability_dispatch convention from PR #20). acc/tui/models.py: * CollectiveSnapshot.cluster_topology dict — keyed by cluster_id, populated from cluster-tagged TASK_PROGRESS / TASK_COMPLETE. acc/tui/client.py: * NATSObserver._fan_out_cluster also folds events into the snapshot via _update_cluster_topology — keeps panel rendering snapshot-driven without forcing every screen to register listeners. * Member status transitions: running → complete | blocked. * subagent_count tracked as running max of witnessed members. * finished_at stamped when every observed member has reported, so the panel's grace-window filter can hide finished clusters. acc/tui/screens/prompt.py: * ClusterPanel mounted between target row and transcript. * watch_snapshot pushes cluster_topology + calls panel.render_now() from a non-layout context. 11 new tests in tests/test_cluster_panel.py — aggregator fold-shape (progress creates row, complete marks member done, finished_at stamped), back-compat (no cluster_id → no row), skill_in_use extraction for skill: + mcp:, panel render header counts + total members, expanded member rows, grace-window filtering. 72 passed across PR-1/2/4 + streaming; 121 across all related modules. Foundation for PR-5 (slash commands incl. /cluster show + /cluster kill) which leverages the same registry + listener path. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ustering)
Operator types '/<verb> <args>' in the prompt textarea; recognised
commands trigger an action without an LLM round-trip. Non-slash
input flows through the legacy prompt-dispatch path unchanged
(full back-compat — every existing keystroke pattern still works).
acc/slash_commands.py (new):
* Pure-function parser — returns SlashIntent(kind, args, error).
* Verbs: /help, /cancel <task_id|cluster_id>, /cluster show|kill,
/role list, /skills, /oversight pending|approve|reject.
* /cancel <c-…> auto-routes to KIND_CLUSTER_KILL via the c- prefix
so operators can type either '/cancel <id>' or '/cluster kill <id>'.
* Unknown verbs return KIND_UNKNOWN with a helpful message — never
raise. Operators learn from typos in the transcript.
* HELP_TEXT documented + the test harness pins that every verb
appears in it (no future verb additions will silently lack help).
acc/signals.py:
* SIG_TASK_CANCEL = "TASK_CANCEL".
* subject_task_cancel(cid) → "acc.{cid}.task.cancel" — distinct from
subject_task so cancel handlers can subscribe cheaply.
acc/tui/screens/prompt.py:
* action_send branches on '/' prefix → _dispatch_slash().
* /cancel + /cluster kill publish TASK_CANCEL via the active
observer (fire-and-forget; the agent's TASK_COMPLETE with
blocked=True, block_reason='cancelled' is what the operator
ultimately observes).
* /cluster show renders the current snapshot in the transcript.
* /role list + /skills query the local registry and append a
system entry — no NATS round-trip.
* /oversight verbs are accepted by the parser and stubbed in the
dispatch with a 'use Compliance screen' hint (full wiring lands
in a follow-up).
22 new tests in tests/test_slash_commands.py — every verb routed,
required-arg enforcement (cancel without target, cluster kill without
id, oversight reject without reason), c-prefix routing, unknown verb
helpful message, HELP_TEXT covers every accepted verb, signal subject
format pinned.
82 passed across PR-1/2/4/5; 121 across all related modules.
The agent-side TASK_CANCEL subscriber + cooperative checkpoint inside
CognitiveCore.process_task land in a separate small follow-up so this
PR stays focused on the operator surface.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Reopened — original PR #30 was auto-closed when its base branch was deleted on merge. All commits + tests unchanged. Verified on acc1: 22/22 targeted, 100/100 across PR 26-30 + PR-3 modules. See test/results/05_PR30_prompt_slash_commands.md.