Skip to content

feat(swarm): SP2 — real collaboration (data-flow, role-aware personas, permissions, self-healing)#13

Closed
New1Direction wants to merge 6 commits into
feat/swarm-honest-demofrom
feat/swarm-collaboration
Closed

feat(swarm): SP2 — real collaboration (data-flow, role-aware personas, permissions, self-healing)#13
New1Direction wants to merge 6 commits into
feat/swarm-honest-demofrom
feat/swarm-collaboration

Conversation

@New1Direction

Copy link
Copy Markdown
Owner

Track B SP2: make the swarm actually collaborate

The DAG already encoded the collaboration graph (benjamin depends on captain+harper; lucas on benjamin) but never passed data; permissions were plumbed yet ignored; Benjamin's worker was deliberately crash-simulated ("Implement (simulate-crash)"); and self-healing was a no-op. SP2 makes the collaboration mechanism genuinely real.

Slices (each independently reviewed)

1 — Role-aware provider + de-theater (52fbddd)

  • DeterministicProvider now recognizes all 5 personas (markers sourced 1:1 from Prompts/*.md) and emits a real role-shaped artifact each for the fixture task (captain→work_packages, harper→concerns, benjamin→applyable patch, lucas→resolutions, evaluator→passed_rubrics); honest-null for any other task — never fabricated.
  • Removed the (simulate-crash) directive from the default decomposition (the fault handler stays for explicit fault tests).

2 — Real upstream→downstream data-flow (d632657, 27f66b7)

  • dispatch_concurrent now rewrites each downstream node's payload with its DAG-upstream personas' output (Captain+Harper→Benjamin, Benjamin→Lucas) before it dispatches — carried in the existing RouteWork.payload, 8000-char capped, deterministically ordered. Verified end-to-end: the rewritten pkg.description is the load-bearing worker payload (workers.rs:200).
  • Pure apply_upstream_to_pending extracted + an integration test asserting the real rewrite (with pending-only gating).

3 — Per-persona permissions (85b90f6)

  • permissions_for: benjamin/lucas write, harper/captain/evaluator read (least-privilege default). Enforced in run_task_in_worktree via may_write — a read-only persona's mutations are not applied and files_changed is honestly 0. Proven on-disk: Benjamin's real patch is blocked under fs:read, applies under fs:write.

4 — Self-healing re-plumb (1d9a4fa)

  • Fixed the no-op + the missing re-measure: a genuine heal now re-runs real numstat and updates files_changed so the ledger count stays truthful; a clean run still fabricates no heal. Test made non-vacuous (real compile error → heal runs → files_changed==1).

Honesty boundary (stated, not hidden)

Offline real collaboration is demonstrated on fixture-class tasks via the role-aware deterministic stub; arbitrary campaign tasks honestly honest-null offline and need --provider ollama. No claim that "all personas do real work on any task."

Test plan

  • cargo test --workspace — 32 binaries, 0 failures (korg-runtime 132 lib + korg-llm 20 + keystone + run_once)
  • permissions proven on-disk (read-only persona can't mutate)
  • self-healing re-measure non-vacuous (real heal → real count)
  • data-flow integration test (downstream payload carries upstream output)
  • keystone + run-once intact; fmt + clippy clean on touched code

Honest limitation

No live multi-subprocess dispatch_concurrent campaign was run end-to-end (it spawns real worker processes + worktrees — heavy/non-deterministic). Coverage targets the deterministic seams (data-flow pure-fn, permissions on-disk, self-healing orchestrator-level); the live-loop wiring is verified by construction + trace, not an end-to-end run.

Stacked on feat/swarm-honest-demo (SP4). For review — not for merge ahead of the stack.

Spec: docs/superpowers/specs/2026-06-14-korg-swarm-sp2-collaboration-design.md

New1Direction and others added 6 commits June 14, 2026 14:41
…ake-crash from decomposition

role_marker now recognizes all 5 personas from their Prompts/*.md role titles
(Captain=Swarm Orchestrator & Planner, Harper=Adversarial Researcher & Reviewer,
Benjamin=Builder & Implementer, Lucas=Synthesizer & Reconciler,
Evaluator=Guardrail Evaluator & Critic), with persona names as fallback.

For the fixture-class task (add bug on src/lib.rs) each persona emits a real,
deterministic, role-shaped JSON artifact matching its documented schema:
captain->work_packages/acceptance_criteria, harper->concerns/risk_assessment,
benjamin->applyable mutations (unchanged), lucas->resolutions,
evaluator->passed_rubrics/recommended_action. Any non-fixture task ->
honest-null (empty role-shaped artifact + 0.2 confidence), never fabricated.

complete() stays pure/deterministic. Adds role-aware fixture + honest-null tests.

De-theater: decompose_into_persona_packages no longer bakes 'simulate-crash'
into Benjamin's package (now 'Implement: {root_task}'); the harness handler is
retained for explicit fault-injection. Adds a unit test asserting the default
benjamin package description does not contain 'simulate-crash'.
…(real data-flow)

dispatch_concurrent now makes packages_map mutable and, after each DAG level
completes, rewrites every still-pending downstream node whose dependencies
include a just-completed node so its worker payload carries the real upstream
PersonaResult.output (Captain+Harper -> Benjamin; Benjamin -> Lucas). The reverse
dependency map is read from the DAG node .dependencies; the next level's
dispatch_level reads pkg.description as the worker payload, so the flow is real
(not a no-op).

The payload rewrite lives in a pure helper compose_downstream_payload(base,
upstream) in workers.rs: upstream entries are sorted by (persona, node_id) for
determinism, and the appended upstream context is capped at
UPSTREAM_CONTEXT_BUDGET (8000 chars) with an explicit …[truncated] marker on a
UTF-8 char boundary. Adds unit tests proving the downstream payload carries
upstream content (captain plan -> benjamin, benjamin output -> lucas),
order-independence, the size cap, and the empty-upstream no-op.
…ver mutate

Add a permission policy module (permissions_for / may_write): benjamin & lucas
get fs:write:worktree; harper, captain, evaluator (and any unknown persona)
get fs:read and are analyze-only.

- session.rs: SubprocessBackend::spawn now derives RouteWork.permissions from
  permissions_for(&spec.persona) instead of hardcoding fs:write:worktree.
- harness.rs: handle_route_work stops ignoring permissions (_permissions ->
  permissions) and threads them into run_task_in_worktree, which gates
  apply_mutations on may_write(). A read-only persona that emits mutations is
  analyzed (numstat/cargo_check on the existing tree), never applied, and
  records files_changed = 0 honestly, with a one-line log note.

TDD: 8 policy tests + an apply-gate integration test proving benjamin's real
fixture patch is blocked under fs:read (file unchanged, files_changed == 0) but
applied under fs:write:worktree (src/lib.rs rewritten to a + b).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ount after heal)

run_self_healing_loop previously never re-plumbed files_changed after a heal,
so the leader's real_files_changed sum (leader.rs ~1484) missed healed changes.

- After a real heal (direct heal-success or a speculative repair that mutated
  the worktree), re-run observation::numstat on the heal target and set
  current_result.files_changed to the re-measured count. Gated on a
  healed_this_loop flag so a no-op pass never re-measures or clobbers the
  worker's reported count — no fabrication.
- Made the honest no-op explicit: when no worktree exists (clean run — the
  worker already cleaned up), return the worker's result unchanged.

TDD:
- test_self_healing_loop_success now sets up a git worktree with a committed
  missing-semicolon error so the heal path actually runs (non-vacuous), and
  asserts the re-measured files_changed == 1 (the healed src/main.rs;
  build artifacts gitignored, not counted).
- Added test_self_healing_loop_no_op_preserves_count_when_no_worktree: with no
  worktree, the worker's reported count (5) is preserved, not re-measured.

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

The Slice-2 data-flow wiring previously had only the pure compose_downstream_payload
helper under test — the per-level rewrite loop in dispatch_concurrent had no
regression test. Extract that loop into a pure, testable
workers::apply_upstream_to_pending(packages_map, node_dependencies,
completed_outputs, just_completed_ids) and call it from dispatch_concurrent
(behavior unchanged — the inline loop is now a single call).

Add an integration test on the canonical 4-node DAG that asserts the REAL
rewrite (not a tautology):
- After captain+harper complete, benjamin's payload contains BOTH 'work_packages'
  / 'Upstream from Captain (pkg-captain)' AND 'concerns' / 'Upstream from Harper',
  while lucas (dep not yet done) is untouched and L1 peers are not rewritten.
- After benjamin completes, lucas's payload contains 'mutations' / 'src/lib.rs' /
  'Upstream from Benjamin (pkg-benjamin)', and benjamin is not re-rewritten.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
New1Direction added a commit that referenced this pull request Jun 15, 2026
…ssions)

Lands the stacked branch 'feat/swarm-collaboration' (PR #13) onto main.
@New1Direction

Copy link
Copy Markdown
Owner Author

Landed on main via the full stack merge — main is now 0eaefb4. This branch's commits are fully contained in main (GitHub: 'no new commits between main and this branch'), recorded by merge commit 1701446 ("Merge PR #13"). Closing as merged-by-stack; deleting the now-redundant branch.

@New1Direction New1Direction deleted the branch feat/swarm-honest-demo June 15, 2026 00:22
@New1Direction New1Direction deleted the feat/swarm-collaboration branch June 15, 2026 00:22
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