Skip to content

feat(tui): cluster topology panel (PR-4 #29 reopened) - #32

Merged
flg77 merged 4 commits into
mainfrom
feat/cluster-tui-surface
May 7, 2026
Merged

feat(tui): cluster topology panel (PR-4 #29 reopened)#32
flg77 merged 4 commits into
mainfrom
feat/cluster-tui-surface

Conversation

@flg77

@flg77 flg77 commented May 7, 2026

Copy link
Copy Markdown
Owner

Reopened — original PR #29 was auto-closed when its base branch was deleted on merge. All commits + tests unchanged. Verified on acc1: 72/72 targeted, 86/86 wider. See test/results/04_PR29_cluster_tui_surface.md.

flg77 and others added 4 commits May 6, 2026 22:35
…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>
@flg77
flg77 merged commit b66e275 into main May 7, 2026
@flg77
flg77 deleted the feat/cluster-tui-surface branch May 7, 2026 12:23
flg77 added a commit that referenced this pull request Jun 5, 2026
…dapters

Two formal proposals scoping the multi-week / multi-repo work
remaining after Stage 1; plus a partial implementation of Stage
2.4 (Marketplace + Catalog admin) as data-only adapters so the
eventual TUI/WebGUI panes are pure presentation.

Proposals:

  * openspec/changes/20260606-acc-ecosystem-hub-and-scale/ (Stage 2):
    Six sub-slices — flg77/acc-ecosystem repo bootstrap, family
    extractions (4 packages from 44 in-tree dirs, two-release
    deprecation), public hub at acc-roles.dev, Marketplace + Catalog
    admin TUI/WebGUI surfaces, flg77/acc-podman-desktop, docs.
    Recommended order: 2.4 → 2.6 → 2.1 → 2.3 → 2.2 → 2.5.  Four
    open strategic decisions called out (hub authority, VP pricing,
    family boundaries, CLA mechanism).

  * openspec/changes/20260607-acc-pkg-edge-bootc/ (Stage 3):
    Three sub-slices — Containerfile.bootc.j2 + per-base configs
    (hummingbird / rhel-bootc / microshift), acc/pkg/bundle.py
    bundler core, air-gap --offline support.  All work lives in
    this repo (no external repo coordination).  Three open
    decisions (default base, builder binary, MicroShift size
    warning).

Stage 2.4 partial — data adapters:

  * acc/marketplace.py (NEW):
    Pure-Python module powering Marketplace surfaces.
    - MarketplaceRow dataclass: name, version, tier, tier_badge,
      catalog_id, catalog_mode, signer, install_marker.
    - render_rows(name_filter=, workspace=) walks all layered
      catalogs and returns rendered rows; stable sort (name asc,
      version asc).
    - list_versions(name) returns all versions of a package,
      newest first (powers the version-picker dropdown).
    - stage_install(row, constraint=) produces the canonical
      PROPOSE_INFUSE marker text that the Compliance pane Package
      Proposals queue (PR #32) approves.
    - Compliance pane is the install surface; this is the
      discovery surface — they compose.

  * acc/catalog_admin.py (NEW):
    Read + mutate <workspace>/.acc/catalogs.yaml with validation
    against acc.pkg.catalog.Catalog Pydantic model.
    - load / save (atomic_write_text + flock).
    - add / remove / set_priority — mutations with duplicate-id
      refusal.
    - parse_form() — builds a Catalog from individual form
      fields; surfaces ValidationError to the form-submit
      handler for inline per-field errors.

What's NOT in scope (deferred to Stage 2.4 TUI follow-up):

  * acc/tui/screens/marketplace.py — Textual pane consuming
    acc.marketplace.
  * acc/tui/screens/catalogs.py — Textual pane consuming
    acc.catalog_admin.
  * acc/webgui/routes_roles.py — REST API parity.
  * React surfaces.

Each becomes pure presentation work since this PR proves the
data shape end-to-end.

Tests: 29 new (14 marketplace + 15 catalog admin); pkg suite
472/1.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
flg77 added a commit that referenced this pull request Jun 5, 2026
…2.4/2.6/2.2)

Three of Stage 2's six sub-slices land here — the ones that don't
need operator-side `gh repo create` or DNS decisions.

Stage 2.4 — Marketplace + Catalog admin TUI/WebGUI surfaces:

  * acc/tui/screens/marketplace.py (NEW):
    - Package discovery from layered catalogs.
    - Filter input (/ prefix match), DataTable with tier badge +
      signer, install staging via _StageInstall message bubbled to
      the app — which posts the canonical PROPOSE_INFUSE marker for
      the Compliance pane Package Proposals queue (PR #32).
    - Discovery surface; Compliance pane is the install surface.

  * acc/tui/screens/catalogs.py (NEW):
    - Catalog admin pane: list / add / remove / +-priority.
    - Form with Pydantic ValidationError surfacing per-field errors
      in the status line.
    - Mutations go through acc.catalog_admin (PR #34) with atomic
      flock writes to <workspace>/.acc/catalogs.yaml.

  * acc/tui/app.py: register both screens in SCREENS dict.

  * acc/webgui/routes_roles.py (NEW):
    - GET /api/roles/available — list packages from layered catalogs
      (auth: viewer).
    - POST /api/roles/install — stage PROPOSE_INFUSE marker for
      Compliance queue (auth: operator).
    - GET/POST/DELETE/PATCH /api/catalogs[/{id}] — CRUD wrapping
      acc.catalog_admin (auth: viewer/operator).
    - 400 on invalid form, 404 on missing, 409 on duplicate id.

  * acc/webgui/app.py: mount routes_roles router.

Stage 2.6 — Operator + contributor docs:

  * docs/CONTRIBUTING-ROLE.md (NEW):
    First-time community publisher walkthrough — scaffold ->
    author role + evals -> build deterministic .accpkg -> GitHub
    Actions OIDC keyless signing -> verify on acc-roles.dev.  Cites
    the EC policy's eval_pass requirement; documents the tier
    classification operators see.

  * docs/MIGRATING-FROM-INTREE.md (NEW):
    Operator runbook for the Stage 2 release N -> N+1 cycle.  Five-
    minute migration: add `required_packages:` to collective.yaml,
    re-apply, verify dual-source loader picks installed-package
    source.  Includes `git checkout` recovery for missed windows +
    air-gap local-catalog setup.

Stage 2.2 partial — family extraction tooling:

  * tools/build_family_pkg.py (NEW):
    Multi-role family builder.  Four default manifests:
      @acc/workspace-roles  (coding_agent family + analyst + synthesizer)
      @acc/research-roles   (research_* — 6 roles)
      @acc/business-roles   (30 business roles)
      @acc/devops-roles     (data/devops/ml/security engineers)
    --manifest <yaml> accepts an arbitrary custom family.  Dedupes
    skills + MCPs across the family's roles; refuses unclassified
    refs; reuses acc.pkg.build for the same deterministic output
    the pilot tool produces.
    End-to-end smoke proven: built dist/acc-research-roles-1.0.0.accpkg
    (6 roles + 6 skills + 3 MCPs, 6.4 KB) against the real roles/
    tree.

What's NOT in this PR (Stage 2 deferred — operator-side):

  * 2.1 flg77/acc-ecosystem repo creation (`gh repo create`)
  * 2.3 acc-roles.dev DNS + hosting authority decision
  * 2.5 flg77/acc-podman-desktop + Podman Desktop marketplace
        registration

Tests: 35 new (9 TUI + 15 WebGUI + 11 family builder); full sweep
3071/37 (vs 3036/37 baseline — exact +35 delta, no regressions).

Stage 2 implementation complete for everything that can ship from
this repo.  Stage 3 (edge-bootc) remains as a proposal.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant