Skip to content

refactor: remove bespoke Agent Teams machinery; expose as optional flag#240

Merged
dean0x merged 11 commits into
mainfrom
refactor/remove-agent-teams
Jun 9, 2026
Merged

refactor: remove bespoke Agent Teams machinery; expose as optional flag#240
dean0x merged 11 commits into
mainfrom
refactor/remove-agent-teams

Conversation

@dean0x

@dean0x dean0x commented Jun 8, 2026

Copy link
Copy Markdown
Owner

Summary

  • Removes the agent-teams skill, all eight *-teams.md command variants (implement, plan, debug, explore, code-review, resolve, research, release), and the entire teamsEnabled / teams: manifest field / applyTeamsConfig / stripTeamsConfig pipeline from the CLI
  • Re-exposes Claude Code experimental teammate mode as a single optional FLAG_REGISTRY entry (id: agent-teams, defaultEnabled: false) — users opt in with devflow flags --enable agent-teams rather than devflow init --teams
  • Adds stripDevflowTeammateMode cleanup helper (src/cli/utils/teammate-mode-cleanup.ts) and two migrations (purge-devflow-teammate-mode-global-v1, purge-devflow-teammate-mode-v1) to clean up the Devflow-written teammateMode:"auto" key from existing settings files

Changes

Deleted:

  • shared/skills/agent-teams/ (SKILL.md + 3 reference files)
  • 8 *-teams.md command variants across 8 plugins
  • applyTeamsConfig / stripTeamsConfig from post-install.ts
  • teamsEnabled from FileCopyOptions (installer), --teams/--no-teams from init.ts, teams: from ManifestData

Added:

  • src/cli/utils/teammate-mode-cleanup.ts — sync + async teammate mode cleanup helpers
  • agent-teams FLAG_REGISTRY entry in flags.ts targeting CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS
  • Two migrations for teammateMode:"auto" cleanup (global + per-project)
  • Full test coverage: tests/teammate-mode-cleanup.test.ts, migration tests, flag tests

Updated:

  • 9 plugin plugin.json manifests — removed agent-teams from skills arrays
  • marketplace.json — removed agent-teams keyword from devflow-debug entry
  • Tests — removed all applyTeamsConfig/stripTeamsConfig/teamsEnabled/teams-variant test cases; updated thresholds and exclusion sets for removed skill

Reviewer Focus Areas

  • src/cli/utils/teammate-mode-cleanup.ts — the sync/async helpers only remove teammateMode when value is exactly "auto" (Devflow-written); user values like "tmux" or "in-process" are preserved
  • src/cli/utils/migrations.ts — per ADR-001 exception: minimum migration only (only teammateMode:"auto" cleanup; env var and manifest field self-heal via flag machinery)
  • tests/skill-references.test.ts — the canonical-name stale-doc guards run unguarded for agent-teams (the temporary PENDING_README_REMOVAL/PENDING_DOC_REMOVAL exclusion sets were removed post-review, since the doc cleanup landed in this branch)
  • src/cli/commands/init.ts — the LEGACY_COMMAND_NAMES loop still cleans up the review/specify legacy commands; a blanket *-teams.md sweep was added post-review so the 8 orphaned workflow variants are removed on partial reinstalls too (not only full installs)

Post-Review Fixes

Follow-up commits after code review (full suite green — 1541 pass):

  • propagate teammate-mode cleanup write errors; make async — a swallowed write error previously let the migration be recorded as applied so it never retried (PF-004); errors now propagate. The file helper is async and delegates to the pure stripDevflowTeammateModeFromJson.
  • sweep orphaned *-teams.md commands on any install type — partial reinstalls (devflow init --plugin=X) no longer retain the 8 dead workflow *-teams.md commands (PF-009).
  • drop dead agent-teams doc-removal guards; cover in-process preservation — removed the now-inert PENDING_* test guards; added in-process value-preservation coverage.
  • changelog — specify the =1 value for the agent-teams env var.

Docs: All Agent Teams documentation was removed in this branch (commit 378d0a2) — CLAUDE.md, docs/, the affected plugin README.md files, and CHANGELOG.md are updated here, not deferred to a separate PR.

dean0x added 4 commits June 8, 2026 23:37
…e as optional flag

Deletes the agent-teams skill, all eight teams-variant command files, and
every teamsEnabled / teams manifest field / applyTeamsConfig / stripTeamsConfig
touch-point across the CLI. The Claude Code experimental teammate mode is
re-exposed as a single optional flag in the existing FLAG_REGISTRY
(defaultEnabled: false), giving users devflow flags --enable agent-teams
to opt in without a bespoke pipeline.

Two migrations clean up stale teammateMode:"auto" written by prior Devflow
installs: a global migration for ~/.claude/settings.json and a per-project
migration for the project .claude/settings.json. Helpers extracted into
src/cli/utils/teammate-mode-cleanup.ts. Applies ADR-001 exception (minimum
migration only). Per PF-009, full grep verification performed after changes.

BREAKING: devflow init --teams/--no-teams options removed.
Use devflow flags --enable agent-teams instead.
- installer.ts: collapse dead -teams.md filter to install every .md unconditionally (rename baseCommands → commandFiles); no teams variants exist anymore (AC13/AC17, plan §D) - teammate-mode-cleanup.ts: delete redundant async file-path variant (zero production call sites); migrations use the sync wrapper, uninstall uses the string→string primitive — collapses to one coherent surface per 'one async pattern' - teammate-mode-cleanup.test.ts: drop the obsolete async describe block + import - uninstall.ts / post-install.ts: route teammateMode strip through the shared stripDevflowTeammateModeFromJson primitive (DRY)
/debug no longer uses the Agent Teams subsystem — it runs competing-hypothesis
investigation via regular parallel subagents. Update the description in all
three locations (plugins.ts, marketplace.json, plugin.json) to reflect this.
… Remove all bespoke Agent Teams documentation and update references across CLAUDE.md, cli-reference, skills-architecture, file-organization, and three plugin READMEs. Add agent-teams as an optional Claude Code flag (default OFF) accessible via devflow flags --enable agent-teams. Drop the --teams/--no-teams init flags from cli-reference. Note the breaking change in CHANGELOG under Unreleased. Co-Authored-By: Claude <noreply@anthropic.com>
@dean0x

dean0x commented Jun 9, 2026

Copy link
Copy Markdown
Owner Author

src/cli/utils/teammate-mode-cleanup.ts:57-61 — Swallowed write error breaks the documented retry (MEDIUM, ~85%)

The catch {} swallows writeFileSync failures, but the migration runner then records the migration as applied, so the comment "migration will retry on next init" can never happen (PF-004 idempotency trap).

Bounded/benign impact — the orphaned teammateMode:"auto" key is inert without the default-OFF env var.

Fix: Let the write error propagate (matches MIGRATION_PURGE_STALE_MEMORY_MARKERS at migrations.ts:882-884) or correct the misleading comment. Distinguish "no change needed" from "write failed".

@dean0x

dean0x commented Jun 9, 2026

Copy link
Copy Markdown
Owner Author

tests/skill-references.test.ts:570 & :602 — Dead PENDING_README_REMOVAL/PENDING_DOC_REMOVAL guards mask regression guards (MEDIUM, ~90%)

The sets claim agent-teams doc cleanup is "pending / separate PR," but commit 378d0a2 already removed every agent-teams doc reference in this same branch (verified: zero matches in READMEs and skills-architecture.md). The guards are dead code with factually-wrong comments and they permanently mask the canonical-name stale-doc guard (PF-009).

Fix: Delete both sets and their two if(...) continue; skip lines so the assertions run unguarded (suite still passes).

@dean0x

dean0x commented Jun 9, 2026

Copy link
Copy Markdown
Owner Author

src/cli/commands/init.ts:991 — -teams.md cleanup never sweeps the 8 workflow variants on partial reinstall (MEDIUM, ~90%)

The kept loop iterates LEGACY_COMMAND_NAMES = ['review','specify','specify-teams'], so it removes review[-teams].md/specify[-teams].md but NOT code-review-teams.md, implement-teams.md, plan-teams.md, debug-teams.md, explore-teams.md, resolve-teams.md, research-teams.md, release-teams.md.

installer.ts:164 wipes commands/devflow/ only on FULL install, so on devflow init --plugin=X those 8 orphaned files survive as dead slash commands pointing at removed workflows (PF-009).

Fix: Unconditionally sweep *-teams.md from commands/devflow/ during cleanup regardless of install type.

@dean0x

dean0x commented Jun 9, 2026

Copy link
Copy Markdown
Owner Author

Summary: PR #240 Approved with Conditions

This is a high-quality, well-executed removal refactor (−4131/+601 lines). tsc compiles with zero errors, all 395 tests pass, no orphaned references survive, and the flag re-exposure is idiomatic.

Overall Recommendation

APPROVE with three should-fix conditions. No merge-blocking issues. Reviewer scores 8–10/10 across all dimensions.

Should-Fix Items (≥80% confidence)

1. HIGH: -teams.md cleanup gap (Architecture)

  • The LEGACY_COMMAND_NAMES loop only covers ['review','specify','specify-teams'], leaving 8 workflow variants orphaned on partial reinstall (code-review-teams.md, implement-teams.md, plan-teams.md, debug-teams.md, explore-teams.md, resolve-teams.md, research-teams.md, release-teams.md).
  • Impact: Full install is safe (wipes commands/devflow/ completely), but partial reinstall leaves dead slash commands (PF-009).

2. HIGH: Dead test guards masking regression (Consistency/Testing)

  • PENDING_README_REMOVAL and PENDING_DOC_REMOVAL sets claim doc cleanup is "pending," but commit 378d0a2 already removed all agent-teams references in this branch (verified: zero stale matches).
  • These guards permanently suppress the canonical-name test for any future re-introduction (PF-009).

3. MEDIUM: Swallowed write error defeats retry (Reliability)

  • stripDevflowTeammateMode swallows write failures with a misleading comment claiming "migration will retry on next init." Actually, the migration is marked applied, so it never retries (PF-004 failure mode).
  • Impact: Bounded/benign — the orphaned key is inert without the env var. Fix: propagate the error or correct the comment.

Documentation Corrections (MEDIUM, ~95%)

(a) PR description vs commit state: The PR body says markdown docs are "intentionally left for a separate docs-only PR," but commit 378d0a2 already removed all agent-teams references. Fix: Update the description to state docs are complete in this branch.

(b) Helper name mismatch: The PR body names the helpers stripDevflowTeammateModeSync/stripDevflowTeammateMode, but the code ships stripDevflowTeammateModeFromJson/stripDevflowTeammateMode (the shipped names are better and consistent). Fix: Correct the PR description.

CHANGELOG (LOW, ~80%)

The new flag entry says it "sets CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS" but omits the value (=1), while cli-reference.md and flags.ts use the explicit =1 form. Optional fix: Append "to 1" for consistency.

Reconciled Non-Blocking Findings

Async conversion (sync I/O in async pool): The review flagged stripDevflowTeammateMode uses sync I/O in an async concurrency pool. This is currently a LOW blocking item per the review summary, but it's actually a convention-preservation finding, not a bug—every other settings.json writer in the repo uses the same in-place pattern. Recommendation: Optional hardening (async conversion + atomic temp+mv), not a merge condition. Note as a repo-wide opportunity.

Test coverage gap: The per-project migration test covers "tmux" but not "in-process" value preservation. The gap is real but small (unit test covers it). Optional fix: Add one assertion.

Positive Findings

Complete & coherent removal: tsc clean, zero residual references, 395/395 tests pass, all upgrade paths verified
Well-typed: No any types, removal is complete, flag follows FLAG_REGISTRY pattern idiomatic
Documentation thorough: Tree-wide sweep confirmed zero stale references (commit 378d0a2 did the work)
Manifest backward-compat: Old teams:true parses harmlessly
Migration framework sound: Idempotency, ordering, scope all verified


Status: READY TO FIX → APPROVED (after applying the three should-fix items above)
Cycle: 1 (first review; no prior false positives)

dean0x and others added 4 commits June 9, 2026 10:47
Rewrites stripDevflowTeammateMode to be async (fs/promises) and delegate
parse/strip logic to the existing pure stripDevflowTeammateModeFromJson.
Write errors now propagate so a failed migration stays unapplied and retries
on next init (avoids PF-004). Removes the synchronous readFileSync/writeFileSync
imports and the swallowed write-error catch. The per-project migration's pooled()
concurrency now provides real parallelism instead of blocking the event loop.

Both call sites in migrations.ts updated to await the now-async helper.
Tests updated to await all stripDevflowTeammateMode calls; write-error
propagation test added using a read-only file.

Co-Authored-By: Claude <noreply@anthropic.com>
…ervation

Remove PENDING_README_REMOVAL and PENDING_DOC_REMOVAL exclusion sets from
skill-references.test.ts — the agent-teams doc cleanup landed in commit
378d0a2, so these guards were masking the canonical-name stale-doc checks
(PF-009 failure mode) permanently.

Add in-process teammateMode preservation assertion to the per-project
teammate-mode migration test, matching the existing tmux test style.

Co-Authored-By: Claude <noreply@anthropic.com>
The LEGACY_COMMAND_NAMES loop only removes the 3 known legacy names
(review, specify, specify-teams) plus their -teams variants, but misses
the 8 workflow-variant files produced by the Agent Teams refactor
(code-review-teams.md, implement-teams.md, plan-teams.md, etc.).

On partial installs (devflow init --plugin=X) the full-install dir wipe
does not run, so those 8 dead commands survive pointing at the removed
agent-teams skill (PF-009).

Add an unconditional readdir sweep that removes every *-teams.md file
from the commands/devflow dir regardless of install type. No *-teams.md
command is ever legitimately installed anymore so the blanket sweep is safe.

Co-Authored-By: Claude <noreply@anthropic.com>
Matches flags.ts (value: '1'); clarifies the exact env-var assignment.
@dean0x

dean0x commented Jun 9, 2026

Copy link
Copy Markdown
Owner Author

Blocking: JSON-null input causes uncaught TypeError

File: src/cli/utils/teammate-mode-cleanup.ts:14-21
Confidence: 90% (flagged by both reliability and typescript reviewers)

Issue

The try/catch at lines 16-20 wraps only JSON.parse, not the subsequent property access at line 21. When the file content is the literal JSON null, it parses successfully and returns null, but then settings['teammateMode'] throws:

TypeError: Cannot read properties of null (reading 'teammateMode')

The as Record<string, unknown> cast is compile-time only and performs no runtime narrowing.

Impact

This uncaught error propagates out of the migration, marking it as failed. Since the migration runner never marks it applied on failure, it retries on every devflow init run forever — the file is never rewritten, so the same null input re-throws indefinitely (exactly the PF-004 never-converging trap).

Other JSON primitives (42, true, "auto", []) safely coerce to undefined from the bracket read and return unchanged. Only null throws.

Suggested Fix

Add a guard before the property access to handle non-object results:

export function stripDevflowTeammateModeFromJson(settingsJson: string): string {
  let parsed: unknown;
  try {
    parsed = JSON.parse(settingsJson);
  } catch {
    return settingsJson; // Malformed JSON — leave untouched
  }
  // Tolerant: only object roots can carry the key; primitives/null/arrays are no-ops.
  if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
    return settingsJson;
  }
  const settings = parsed as Record<string, unknown>;
  if (settings['teammateMode'] !== 'auto') return settingsJson;
  delete settings['teammateMode'];
  return JSON.stringify(settings, null, 2) + '\n';
}

Add a regression test for the 'null' input alongside the existing malformed-JSON case.


Claude Code

@dean0x

dean0x commented Jun 9, 2026

Copy link
Copy Markdown
Owner Author

Should-Fix: Stale "team" prose in devflow-debug README

File: plugins/devflow-debug/README.md:26, :57, :58
Confidence: 92% (flagged by both consistency and documentation reviewers)

Issue

This PR edited the debug README (removed Prerequisites, rewrote the marketplace description) but three references to the now-removed Agent Teams machinery remain in unchanged lines:

  • L26: 2. **Team Spawning** - Creates one investigator agent per hypothesis — contradicts the marketplace description updated in this PR ("via parallel subagents"). The base /debug command does not use a "team".
  • L57: - [devflow-code-review](...) - Code review with team-based debate — describes the removed code-review-teams.md variant (deleted in this PR).
  • L58: - [devflow-implement](...) - Implementation with team exploration — describes the removed implement-teams.md variant (deleted in this PR).

This is the PF-009 straggler pattern: a removal pass that fixes source and primary docs misses narrative prose in README sections.

Suggested Fix

  • L26: 2. **Hypothesis Investigation** - Spawns one investigator agent per hypothesis
  • L57: - [devflow-code-review](../devflow-code-review) - Comprehensive code review
  • L58: - [devflow-implement](../devflow-implement) - Implementation with quality gates

Claude Code

@dean0x

dean0x commented Jun 9, 2026

Copy link
Copy Markdown
Owner Author

Should-Fix: Write-error test fragile under root (chmod no-op)

File: tests/teammate-mode-cleanup.test.ts:123
Confidence: 85%

Issue

The write-error-propagation test uses fs.chmod(settingsPath, 0o444) to force a write failure. However, when the test runs as root (common in CI Docker), root bypasses DAC permission bits and the write succeeds — causing the test to fail (or silently false-pass if logic changed).

This is the highest-value PF-004 regression guard in this PR (a swallowed write error reintroduces the "falsely applied migration" pitfall), so a guard that flips behavior under root is fragile exactly where it matters most.

Suggested Fix

Use a uid-independent failure method. Minimal variant: make the parent a directory (which rejects writes as EISDIR even by root):

it('propagates write errors (avoids PF-004)', async () => {
  // Make the file a directory so writeFile rejects (EISDIR) even as root.
  const dirAsFile = path.join(tmpDir, 'settings-dir.json');
  await fs.mkdir(dirAsFile);
  // ...point the cleanup at dirAsFile, or inject a writer stub that rejects.
  // EISDIR is uid-independent.
});

Or, cleanest: dependency-inject the writeFile function and assert rejection with a stub that always throws (removes the filesystem-permission dependency entirely).


Claude Code

@dean0x

dean0x commented Jun 9, 2026

Copy link
Copy Markdown
Owner Author

Should-Fix: Redundant -teams.md suffix in legacy loop

File: src/cli/commands/init.ts:992
Confidence: 88%

Issue

The loop at lines 991-1001 iterates LEGACY_COMMAND_NAMES × ['.md', '-teams.md'] to remove each {legacy}-teams.md file. However, the new blanket sweep at lines 1005-1012 then re-scans the entire commandsDir and removes every *-teams.md file — a strict superset of what the loop's -teams.md suffix targets.

The '-teams.md' entry in the suffix array is now redundant: any file it would match is also matched by the blanket readdir sweep. Two code paths now express the same intent ("remove orphaned teams command variants"), increasing maintenance burden.

Behavior is correct (no double-count: the loop removes the file first, so readdir won't re-see it), so this is maintainability-only.

Suggested Fix

Drop -teams.md from the suffix array, leaving the loop focused on plain {legacy}.md removal:

for (const legacy of LEGACY_COMMAND_NAMES) {
  const legacyPath = path.join(commandsDir, `${legacy}.md`);
  try {
    await fs.rm(legacyPath);
    staleCommandsRemoved++;
  } catch {
    // expected for most entries
  }
}

The blanket sweep (lines 1005-1012) becomes the single owner of *-teams.md cleanup (which already cites PF-009 correctly).


Claude Code

@dean0x

dean0x commented Jun 9, 2026

Copy link
Copy Markdown
Owner Author

Pre-existing: [Unreleased] CHANGELOG references removed Teams variants

File: CHANGELOG.md:21
Confidence: 85% (pre-existing, not introduced in this PR)

Issue

The ### Changed entry in the [Unreleased] section (line 21) reads:

.../resolve, /plan, /self-review, /code-review, and /debug (plus their Teams variants and ambient orch equivalents...)

However, the same [Unreleased] section's ### Removed block (line 14) eliminates Agent Teams entirely. When this release ships, "their Teams variants" will reference command files that no longer exist — a self-contradiction.

Note: Historical CHANGELOG entries under shipped releases (e.g., ## [1.3.2]) are correct and immutable per Keep a Changelog convention — only the [Unreleased] parenthetical is a candidate for cleanup.

Suggested Fix (Report Only)

Drop the "plus their Teams variants and" clause so line 21 reads:

.../resolve, /plan, /self-review, /code-review, and /debug (plus ambient orch equivalents resolve:orch, plan:orch, ...)


Claude Code

@dean0x

dean0x commented Jun 9, 2026

Copy link
Copy Markdown
Owner Author

Summary: Inline Comments Created (Cycle 2)

Blocking & Should-Fix Issues Reported

5 inline comments created, 0 skipped (no pre-existing comments at these locations):

  1. BLOCKING (90%) — JSON-null TypeError in teammate-mode-cleanup.ts:14-21: migration retry storm trap
  2. SHOULD-FIX (92%) — Stale "team" prose in devflow-debug/README.md:26,57,58: PF-009 stragglers
  3. SHOULD-FIX (85%) — Root-fragile chmod test in teammate-mode-cleanup.test.ts:123: uid-dependent guard
  4. SHOULD-FIX (88%) — Redundant -teams.md suffix in init.ts:992: maintainability cleanup
  5. PRE-EXISTING (85%) — CHANGELOG L21 self-contradiction: "Teams variants" removed in same section

Lower-Confidence Suggestions (Summary Comment)

Architecture (65%): A cross-reference comment in flags.ts linking to teammate-mode-cleanup.ts would clarify that one feature's cleanup logic is split across two modules (the flag registry and the cleanup helper).

Overall Verdict

Clean ~4,100-line removal with one real blocking bug (the JSON-null TypeError). 7 of 10 reviewers APPROVED; 2 CHANGES_REQUESTED both stem from finding #1. The blocking issue is well-scoped (a guard clause + regression test). Other findings are maintainability/documentation polishes (not blocking merge, but recommended before release ships).


Claude Code Review Agent

dean0x and others added 3 commits June 9, 2026 16:15
…l root

JSON.parse("null") returns null (not thrown), escaping the catch block.
The subsequent `settings['teammateMode']` evaluation then threw TypeError on
null — uncaught by either try/catch — causing the migration runner to record
a failure and retry on every devflow init (avoids PF-004).

Fix: narrow parsed value to a non-null plain object before key access;
null/arrays/primitives are tolerated as no-ops consistent with the
malformed-JSON branch. Also rewords the docstring "in-place" to "from a
freshly parsed copy" for accuracy.

Test hardening: replace uid-dependent chmod(0o444) write-failure test with a
vi.mock on fs/promises that wraps writeFile as vi.fn, allowing
mockRejectedValueOnce to simulate EACCES deterministically even as root.
Add regression test asserting 'null', '[]', and '42' inputs return unchanged
and do not throw (covers the exact null-root bug path).

Co-Authored-By: Claude <noreply@anthropic.com>
…engthen migration test - init.ts: remove the -teams.md entry from LEGACY_COMMAND_NAMES suffix array; the blanket readdir sweep (avoids PF-009) is already the single owner of those files - flags.ts: add a one-line cross-reference next to the agent-teams FLAG_REGISTRY entry pointing readers at teammate-mode-cleanup.ts for the legacy teammateMode key cleanup (applies ADR-001) - migrations.test.ts: replace tautological Array.isArray assertions in the purge-devflow-teammate-mode-v1 test with .toEqual([]) to match the style used by the global migration test Co-Authored-By: Claude <noreply@anthropic.com>
…changelog

README — reword "Team Spawning"→"Investigation Spawning", "Adversarial Debate / agents challenge each other"→orchestrator-mediated "Adversarial Evaluation", and fix the Related Plugins lines that described removed team-based variants.

CHANGELOG — drop the "their Teams variants and" clause from the [Unreleased] knowledge-index entry.

Avoids PF-009.

Co-Authored-By: Claude <noreply@anthropic.com>
@dean0x dean0x merged commit 4228fa0 into main Jun 9, 2026
4 checks passed
@dean0x dean0x deleted the refactor/remove-agent-teams branch June 9, 2026 13:51
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