Skip to content

feat(role-md): markdown role authoring + acc-cli compile/decompile/lint (PR-3) - #28

Merged
flg77 merged 1 commit into
mainfrom
feat/markdown-role-authoring
May 7, 2026
Merged

feat(role-md): markdown role authoring + acc-cli compile/decompile/lint (PR-3)#28
flg77 merged 1 commit into
mainfrom
feat/markdown-role-authoring

Conversation

@flg77

@flg77 flg77 commented May 6, 2026

Copy link
Copy Markdown
Owner

Summary

  • acc/role_md.py (new) — compile / decompile / lint a role.md source ↔ canonical role.yaml + system_prompt.md. Round-trip stable. Forward-compat for unknown sections. No new runtime deps.
  • acc/cli/role_cmd.py — three new subcommands: 'role compile', 'role decompile', 'role lint'. Exit codes: 0 clean, 1 dirty.
  • 18 new tests — synthetic full-section coverage, required-section enforcement, risk-level validation, round-trip identity for the bundled coding_agent role, file-write round-trip.

Why

Step 3 of docs/PLAN_subagent_clustering.md. Lets operators author roles in markdown — including PR-2's estimator block — without learning the strict YAML schema. Round-trip identity guarantees migrations of existing roles are lossless.

Independence

This PR is branched off main and is independent of PR-1 (#26) and PR-2 (#27). Can land in any order.

Test plan

  • pytest tests/test_role_markdown.py — 18 passed
  • Verify on acc1 once SSH is back

🤖 Generated with Claude Code

…nt (PR-3)

Operators author roles in a friendly markdown source (role.md) with H1
+ front-matter + H2 sections; the compiler emits the strict YAML
schema RoleDefinitionConfig consumes.

acc/role_md.py (new):
* compile_markdown() — parses role.md → canonical role dict +
  separately-extracted system_prompt.  Required-section enforcement
  raises RoleMarkdownError with line numbers; risk-level validation
  rejects out-of-set values with usable messages.
* decompile_to_markdown() — reverse direction, round-trip stable.
* lint_markdown() — exhaustive diagnostics (no short-circuit).
* compile_file() — disk wrapper: writes role.yaml +
  system_prompt.md beside the source.
* decompile_dir() — render an existing roles/<name>/ to markdown.
* Forward-compat: unknown sections preserved on extras carryover so
  partial-knowledge tools never lose data.
* No new runtime deps — markdown-it-py was already transitively
  pulled in by Textual but the parser is line-oriented for round-trip
  determinism.
* Edge case: System Prompt is the terminal section.  Inner ## H2
  inside the prompt body is treated as raw markdown so a typical
  '## Task types' inside system_prompt.md doesn't clobber the
  outer Task Types section.

acc/cli/role_cmd.py:
* New subcommands: 'role compile <path>', 'role decompile <name>',
  'role lint <path>'.  Exit codes: 0 clean, 1 dirty/missing.

18 new tests in tests/test_role_markdown.py:
* Synthetic role with every supported section parses correctly.
* Required-section enforcement (Purpose H1, role name).
* Invalid risk-level rejection.
* Round-trip identity for synthetic + bundled coding_agent role.
* compile_file() writes correct role.yaml + system_prompt.md.
* Unknown sections preserved on extras.

Independent of PR-1 / PR-2 — branches off main directly.  No wire-protocol
changes, no behavioural changes to existing call sites.  Lets PR-2's
new estimator block be authored in the friendly source format.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@flg77
flg77 merged commit 5ff6132 into main May 7, 2026
@flg77
flg77 deleted the feat/markdown-role-authoring branch May 7, 2026 12:21
flg77 added a commit that referenced this pull request May 7, 2026
acc-cli plan submit previously called json.loads() directly, which
rejected the YAML scenario plans documented under
examples/coding_split_skills/. YAML is the natural pair for the role.md
authoring format (PR #28); plan files should not require an
extra json-conversion step.

acc/cli/plan_cmd.py:
* New _parse_plan_text(raw, path_hint) helper.
* Files ending .yaml / .yml parse via PyYAML; YAML errors are loud
  (a yaml-extension file should always be valid YAML).
* Other paths attempt JSON first and fall back to YAML on
  JSONDecodeError so stdin can carry either dialect.
* PyYAML is already a project dep (used everywhere else in acc/);
  no new requirement.

7 new tests in tests/test_cli_plan_yaml.py:
* JSON plan parses (back-compat).
* YAML plan parses (.yaml + .yml extensions).
* Broken .yaml fails loudly with 'invalid YAML' in stderr.
* Stdin-style YAML body parses via JSON-fallback.
* Malformed input returns None with 'invalid JSON' diagnostic.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
flg77 added a commit that referenced this pull request May 7, 2026
…reopened) (#40)

* feat(arbiter): wire role_resolver + skill_resolver into PlanExecutor (D1)

PR #26-#30 delivered the wire protocol + estimator + TUI panel + slash
commands for sub-agent clustering, but the arbiter agent itself did
NOT pass role_resolver / skill_resolver into PlanExecutor. Without
those callbacks, every PLAN step fell back to legacy single-agent
dispatch — the cluster fan-out path was unreachable in production.

acc/agent.py:
* New _build_cluster_resolvers() helper returns (role_resolver,
  skill_resolver) for the arbiter's PlanExecutor.
* role_resolver: RoleLoader against $ACC_ROLES_ROOT (or ./roles).
  Swallows + logs exceptions so a malformed role.yaml never crashes
  dispatch — executor falls back to single-agent in that case.
* skill_resolver: intersection of role.allowed_skills and the live
  SkillRegistry.list_skill_ids() — operator-visible list, not the
  registry total. A whitelisted-but-not-loaded skill cannot leak to
  the cluster panel's skill_in_use column.
* Falls back to role.allowed_skills directly when the registry isn't
  yet initialised. The eventual A-017 invocation gate still enforces
  real-skill-only at dispatch time.

Tests — tests/test_arbiter_cluster_dispatch.py (6 cases):
* role_resolver returns RoleDefinitionConfig for a known role.
* role_resolver returns None for unknown role (executor falls back).
* skill_resolver intersection — non-allowed registry skills do not leak.
* skill_resolver fail-closed when role has no allowed_skills.
* skill_resolver falls back to role.allowed_skills when registry None.
* role_resolver swallows loader exceptions (does not raise).

88 passed across PR-26..30 + new module on the local sweep.

Foundation for the persona+skill-cluster showcase tracked in
the approved plan.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(cli): plan submit accepts JSON and YAML (D2)

acc-cli plan submit previously called json.loads() directly, which
rejected the YAML scenario plans documented under
examples/coding_split_skills/. YAML is the natural pair for the role.md
authoring format (PR #28); plan files should not require an
extra json-conversion step.

acc/cli/plan_cmd.py:
* New _parse_plan_text(raw, path_hint) helper.
* Files ending .yaml / .yml parse via PyYAML; YAML errors are loud
  (a yaml-extension file should always be valid YAML).
* Other paths attempt JSON first and fall back to YAML on
  JSONDecodeError so stdin can carry either dialect.
* PyYAML is already a project dep (used everywhere else in acc/);
  no new requirement.

7 new tests in tests/test_cli_plan_yaml.py:
* JSON plan parses (back-compat).
* YAML plan parses (.yaml + .yml extensions).
* Broken .yaml fails loudly with 'invalid YAML' in stderr.
* Stdin-style YAML body parses via JSON-fallback.
* Malformed input returns None with 'invalid JSON' diagnostic.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(skills): six stub coding-cluster skills (D4)

The five coding-agent personas (D3 follow-up) reference six skills
that did not exist on disk: code_review, code_generation,
test_generation, test_execution, security_scan, dependency_audit.

Each ships:
* skills/<name>/skill.yaml — LOW-risk manifest, adapter_class
  StubCodingSkill, domain_id software_engineering or security_audit
  (per the receptor model in docs/SUBAGENT_COMMUNICATION.md).
* skills/<name>/adapter.py — pass-through StubCodingSkill that
  round-trips the LLM-supplied text and tags it with the skill_id
  for audit attribution.

The skills are governance-only stubs.  The LLM still does the
actual work via its natural-language output; the skill registry
contributes:
* Cat-A A-017 enforcement (skill ceiling + allow-list).
* Audit anchor on TASK_COMPLETE.invocations.
* skill_in_use column on the cluster panel (PR #29).

Replacing each adapter with a real linter / static-analysis /
pytest backend is a separate hardening track.

Tests — tests/test_stub_skills.py (10 cases):
* All six manifests load via SkillRegistry.load_from.
* All six are LOW risk (default MEDIUM ceiling accepts them).
* Adapter round-trips input text per skill_id (parametrised).
* Per-skill module isolation — each adapter resolves to its own
  manifest's skill_id.
* Domain-id alignment matches the receptor-filter expectations.

110 passed across PR-26..30 + new module on the local sweep.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(roles): five coding-agent personas (D3)

Specialist personas for cluster fan-out — each is a narrowed
coding_agent with distinct system prompt, default skill set,
estimator config, and eval rubric.  Designs from
docs/CODING_AGENT_SUBROLES.md.

New role directories under roles/:
* coding_agent_architect — single-instance interface designer.
  Estimator: fixed count=1.  Default skill: code_review.
  Pattern B (knowledge-share fan-in) — publishes draft_interface.
* coding_agent_implementer — multi-instance code writer.
  Estimator: heuristic base=1, per_n_tokens=1500, cap=4 +
  difficulty bumps for 'concurrency' and 'refactor'.  Default
  skill: code_generation.
* coding_agent_reviewer — single-instance verdict author.
  Estimator: fixed count=1.  Default skills: code_review +
  security_scan.  Carries security_audit receptor.
* coding_agent_tester — multi-instance test author + runner.
  Estimator: heuristic base=1, per_n_tokens=3000, cap=3 +
  security difficulty bump.  Default skills: test_generation,
  test_execution.
* coding_agent_dependency — single-instance CVE / license auditor.
  Estimator: fixed count=1.  Default skills: dependency_audit,
  security_scan.  Carries security_audit receptor.

Each persona carries:
* role.md — operator-facing markdown source (lints clean).
* role.yaml — canonical compiled YAML.
* system_prompt.md — distinct prompt that includes the persona's
  cancellation behaviour.
* eval_rubric.yaml — weights sum to 1.0, security ≥ 10% (mirrors
  the schema invariant pinned for the bare coding_agent).

Tests — tests/test_coding_agent_personas.py (32 cases — 5 personas
× 6 invariants each + 2 cross-persona):
* role.md lints clean (5).
* role.yaml loads via RoleLoader with the right estimator
  block + max_parallel_tasks (5).
* default_skills ⊆ allowed_skills (5).
* default_skills resolve in the live skill registry — D4
  prerequisite (5).
* Rubric weights sum to 1.0 (5).
* Rubric security weight ≥ 10% (5).
* Reviewer + dependency_auditor carry security_audit receptor (1).

131 passed across the related test sweep.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(example+docs): runnable coding-split-skills showcase + doc enhancements (D5+D6)

Stacked atop PRs #34-#37 — combines the arbiter resolver wiring,
CLI YAML support, stub skills, and personas into a fully runnable
end-to-end scenario.

examples/coding_split_skills/ (the deliverable):
* .env.example — every operator-configurable variable in one
  commented file.  Three LLM-backend options preconfigured
  (Anthropic, Ollama, OpenShift AI vLLM-compat).
* run.sh — one-command runner.  Sources .env, lints every
  persona's role.md, brings the stack up via acc-deploy.sh,
  submits the plan via acc-cli plan submit.
* verify.sh — programmatic post-run check.  Subscribes to
  acc.{cid}.> for ACC_VERIFY_DURATION_S seconds, parses
  cluster_id values out of the bus, exits 0 when ≥
  ACC_VERIFY_MIN_CLUSTERS distinct clusters were observed.
  CI-friendly.
* clean.sh — teardown + scratchpad eviction.
* plan.json — JSON copy of plan.yaml for environments without
  YAML support (D2 makes both work via acc-cli plan submit).
* README.md — rewritten with the live "one command" flow and
  troubleshooting matrix.

Docs landed alongside the runnable example:
* docs/IMPLEMENTATION_subagent_clustering.md — wire-protocol +
  module reference for PRs #26-#30 (was pending commit).
* docs/SUBAGENT_COMMUNICATION.md — Patterns A-E catalogue.
* docs/INDEX_subagent_clustering.md — cross-reference matrix.
* docs/ROADMAP_subagent_clustering.md — short / medium / long
  forecasts; "won't do" list.
* docs/CODING_AGENT_SUBROLES.md — caveat dropped (the personas
  are LIVE as of PR #37); rubric summary table + cancellation
  behaviour summary appended; "How the showcase actually runs"
  section linked to examples/coding_split_skills/.
* docs/DEMO_TUI_subagent_clustering.md — Phase 1.5 (env prep) +
  Phase 4.5 (verify.sh) added; Phase 3 substitutes the synthetic
  acc-cli plan submit with the actual run.sh; Phase 5 lists the
  five personas as concrete artefacts; Known-gaps list updated
  to reflect the merged state.

179 passed across the related test sweep on this branch (combined
PR-26..30 + D1 + D2 + D3 + D4 work).

End-to-end usage:
    cp examples/coding_split_skills/.env.example examples/coding_split_skills/.env
    $EDITOR examples/coding_split_skills/.env       # set LLM creds
    ./examples/coding_split_skills/run.sh
    acc-tui                                          # press 7
    ./examples/coding_split_skills/verify.sh         # exit 0 = ok
    ./examples/coding_split_skills/clean.sh          # tear down

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
flg77 added a commit that referenced this pull request Jun 5, 2026
…I variant

End-to-end runner that walks the operator through the five-phase
acc1 K8s hub smoke after PRs #20-#28 land.  Hermetic CI variant
exercises the same chain in-process against a file-mode catalog
so PR-time tests prove the wiring without a running cluster.

What ships:

  * tools/smoke-acc1-hub.sh (NEW):
    - Phase 0 preflight — checks cosign / kubectl / python / jq /
      curl / acc-pkg on PATH.
    - Phase 1 — applies gitops/acc-hub/ if not present; waits for
      rollout; curls /index.json.
    - Phase 2 — generates pilot cosign keypair via
      tools/cosign-pilot-keygen.sh if not on disk.
    - Phase 3 — builds pilot pkg, signs with cosign sign-blob,
      publishes via gitops/acc-hub/publish-to-hub.sh; verifies the
      hub now advertises the package via jq on the live index.
    - Phase 4 — downloads tarball + sig from live hub, runs
      acc-pkg install into a tmp sandbox, exercises cosign verify.
    - Phase 5 — RoleLoader resolves coding_agent from the
      installed-package path (proves the dual-source loader chain
      from PRs #21-#23).
    - Coloured logging + idempotent steps + smoke-specific exit
      codes (7 = hub deploy fail, 8 = roundtrip verification fail).

  * tests/pkg/test_live_smoke_hermetic.py (NEW):
    - Mirrors the bash script's Phase 3-5 in-process against a
      file-mode catalog with mocked cosign so CI exercises the
      chain without acc1 reachability.
    - 7 tests: build determinism, end-to-end install + load,
      idempotent re-install, signing-floor refusal, --allow-unsigned
      bypass, PROPOSE_INFUSE shares the same fetch_and_install seam,
      and smoke script wiring sanity (script references the right
      helpers).

  * tools/SMOKE.md (NEW):
    - Operator runbook: prerequisites, run command, what each
      phase does, exit codes, troubleshooting matrix.

Test growth: 2979/37 (PR #27 baseline) -> 422/1 pkg suite (this PR
adds +7 hermetic tests on top of the operator-only script).  Full
sweep impact is +7 (since #28 was Go-only, no Python tests).

Stage 1 close-out — every code path the eight sub-slices ship is
now exercised by a single hermetic test that proves they compose
correctly:

  Build (#20) -> Sign (#27) -> Publish (#27) ->
  Catalog resolve (#20) -> Verify (#20 + #26) ->
  Install (#20) -> Registry (#20) -> RoleLoader (#21) ->
  PROPOSE_INFUSE dispatch (#24) all hit the same code path.

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

Activates the CRDs landed in PR #28.  Two Go reconcilers + one
Python CLI subcommand that bridges them to the existing Stage 1
install seam.

What ships:

  * operator/internal/controller/acccatalog_controller.go (NEW):
    - Watches every AccCatalog in a namespace.
    - Renders the merged catalog list (sorted by priority desc, id
      asc) into a single ConfigMap `acc-catalogs` containing
      `catalogs.yaml` matching acc.pkg.catalog.CatalogFile.
    - Patches status.LastRenderedAt + Reconciled condition on each
      AccCatalog after each render.
    - Map-func collapses every AccCatalog event into one
      namespace-sentinel reconcile so the full set re-renders, not
      just the changed entry.

  * operator/internal/controller/accpackageinstall_controller.go (NEW):
    - Watches AccPackageInstall.
    - Finds a Ready ACC pod (label acc.redhat.io/corpus=<name>) and
      exec's `acc-cli collective pkg-install-direct @scope/name@constraint --json`.
    - Parses the JSON; populates status.Phase
      (Pending|Installing|Installed|Failed), InstalledVersion,
      InstallPath, LastInstalledAt, Conditions.
    - Honours Spec.AllowUnsigned (audit-logged at WARN).
    - Idempotent: Stage 0's content-hash match returns no-op; the
      controller just refreshes LastInstalledAt.
    - PollInterval requeue (5 min default) for status refresh.

  * acc/cli/collective_cmd.py:
    - New `pkg-install-direct <@scope/name@constraint>` subcommand
      consumed by the reconciler exec'd above.  Output JSON shape
      matches `pkg-install` so the Go parser is one struct.

  * operator/cmd/main.go:
    - Registers both reconcilers; constructs a kubernetes.Interface
      from mgr.GetConfig() for the AccPackageInstall pod-exec path.

  * operator/config/rbac/role.yaml:
    - Adds acccatalogs / accpackageinstalls verbs + status.
    - Adds pods/exec create (for the AccPackageInstall reconciler).

Tests: 7 new Python tests (tests/pkg/test_pkg_install_direct.py)
covering output shape, --allow-unsigned propagation, fetch-error +
malformed-spec exit codes, idempotent re-install reporting.
Pkg suite 443/1; full sweep impact +7 since the Go work doesn't
touch Python.

Operator-side: this PR requires `make generate` (controller-gen) +
`make manifests` (kubebuilder RBAC) before OLM bundle update, then
`go build ./...` + envtest in the operator dir.  Go toolchain not
on this checkout so the Go compile is unverified — CI's
operator-build step is the gate.

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