Conversation
Includes Gource visualization, ACPX spawn backend/transport, and git context layer (conflicts, context). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Keep original README content including Korean docs link. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ed718c9f68
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| shell_cmd = f"{cmd_str}; {exit_hook}" | ||
|
|
||
| process = subprocess.Popen( | ||
| shell_cmd, | ||
| shell=True, |
There was a problem hiding this comment.
Do not wrap async ACPX spawns with the normal on-exit hook
When acpx is launched in --no-wait mode, the CLI can return as soon as the turn is queued. Because this wrapper always falls through to clawteam lifecycle on-exit, ClawTeam will treat that still-running ACPX session as exited, and the stored PID will already be dead. That breaks the liveness checks used by TaskStore._acquire_lock()/release_stale_locks() and TeamWaiter._check_dead_agents(), so active ACPX agents can have locks released or in_progress tasks reset while they are still working.
Useful? React with 👍 / 👎.
| session_name = f"clawteam-{self.team_name}-{recipient}" | ||
|
|
||
| # Try to deliver via ACPX session | ||
| try: | ||
| # Parse the message to extract content for the ACPX prompt |
There was a problem hiding this comment.
Normalize inbox names before deriving ACPX session IDs
Here recipient is the mailbox inbox name, not always the spawned agent name. TeamManager.inbox_name_for() and get_leader_inbox() prefix members as user_agent when member.user is set, but AcpxBackend.spawn() creates sessions as clawteam-{team}-{agent_name}. On multi-user teams, direct sends to the leader or any member will target a non-existent ACPX session and silently fall back to file delivery, so the live ACPX push path never works for those members.
Useful? React with 👍 / 👎.
| try: | ||
| from clawteam.team.mailbox import MailboxManager | ||
| from clawteam.workspace.conflicts import auto_notify | ||
| mailbox = MailboxManager(team_name) | ||
| auto_notify(team_name, mailbox) |
There was a problem hiding this comment.
Deduplicate overlap warnings before auto-notifying again
While clawteam board watch is open, this branch calls auto_notify() every 5 refresh cycles, and auto_notify() always enqueues a fresh message for every current overlap. At the default 2-second refresh interval that means agents receive the same warning about every 10 seconds until someone closes the board, which quickly floods inboxes and buries real coordination messages.
Useful? React with 👍 / 👎.
| count = int(count) | ||
| else: | ||
| start = int(hunk) | ||
| count = 1 | ||
| lines.update(range(start, start + count)) |
There was a problem hiding this comment.
Treat zero-length diff hunks as real line changes
Deletion hunks from git diff -U0 are reported as +N,0. In that case range(start, start + count) is empty, so _changed_lines() records no lines for pure deletions. Two agents deleting the same region, or one deleting lines that another edits, will therefore be downgraded to medium/Different lines modified even though Git will produce a real merge conflict.
Useful? React with 👍 / 👎.
…it/exemption/symlink/concurrent RED gate for Plan 02-05. 10 test functions covering: - freeze/unfreeze persistence to freeze.json (SPRINT-02, QUALITY-06) - is_frozen exact + path-prefix match - Pitfall HKUDS#5 exemption: paths under get_data_dir() never vetoed - T-02-02 mitigation: symlinks + .. segments canonicalized - FrozenPathError message shape (D-12) - freeze_audit.jsonl append-only (SAFETY-04, mirrors exit_journal.py) - Pause/resume rehydration via get_freeze_registry singleton - 8-thread concurrent writer (file_locked serialization) All 10 tests fail with ModuleNotFoundError — Task 2 ships the implementation.
…pend-only audit GREEN gate for Plan 02-05. Ships the sprint-scoped path write-lock registry that Plan 02-10's BeforeFileWrite / BeforeToolCall subscribers will consult to veto agent writes. Implementation: - FreezeRegistry singleton mirrors PhaseRegistry shape (§02-CONTEXT lesson HKUDS#3). - freeze/unfreeze persist to freeze.json via file_locked + atomic_write_text (QUALITY-06); concurrent writers serialize with no torn state (8-thread test). - Path canonicalization via Path.resolve(strict=False) on both store and compare (T-02-02 mitigation): symlinks resolve to real paths, .. segments collapse, relative-path bypass defeated. - Pitfall HKUDS#5 exemption: is_frozen returns (False, "") for any path under get_data_dir() so sprint pause/resume never deadlocks writing its own state.json / freeze.json / answers/ after a user types /freeze /. - freeze_audit.jsonl is append-only (SAFETY-04), mirroring exit_journal.py: one json.dumps(entry) + newline per freeze / unfreeze / freeze_glob. - FrozenPathError subclasses ValueError so mcp.helpers.translate_error auto-wraps it into an MCPToolError (D-12). Message shape: "<path> is /freeze-locked by <who>; unfreeze via /unfreeze <path>" Tests: 10/10 passing (test_freeze_registry.py). Regression matrix 12/12 green. Phase 1 peer tests (sprint_state, interaction_gate, phase_registry) all green. Plan 02-10 subscribes a handler that raises FrozenPathError when is_frozen matches; this plan ships only the registry state-holder. Requirements: SAFETY-02, SAFETY-04, QUALITY-06.
SUMMARY.md documents: - Class + method line ranges (per plan <output> requirement) - Pitfall HKUDS#5 exemption confirmation via test_is_frozen_returns_false_for_paths_under_data_dir - T-02-02 symlink mitigation confirmation via test_is_frozen_canonicalizes_relative_paths_and_symlinks - JSONL append-only invariant proof (5 lines after 5 actions) - Forward contracts for Plan 02-10 / 02-11 / 02-12 / Phase 3 - 1 deviation: Rule 3 (blocking) test-fixture split for work_root vs data_root so Pitfall HKUDS#5 exemption and T-02-02 symlink check don't mask each other RED commit: e0c01e6 GREEN commit: 268e423 Requirements completed: SAFETY-02, SAFETY-04, QUALITY-06
- 8 tests for Task 1: EventBus subscribers + /careful regex blacklist - 6 tests for Task 3: clawteam guard CLI sub-app (freeze/unfreeze/guard/unguard) - All fail with ImportError on CAREFUL_BLACKLIST + register_safety_subscribers (RED) - Pitfall HKUDS#5 exemption re-verified at subscriber level (test 8) - CLI tests use typer.testing.CliRunner + --json envelope assertions
Task 1 GREEN. Extends FreezeRegistry module with opt-in EventBus subscribers that consult the registry on BeforeFileWrite + BeforeToolCall events. - CAREFUL_BLACKLIST regex matches 5 destructive command patterns (D-13): rm -rf, git reset --hard, git push --force, DROP TABLE, DELETE FROM ... WHERE - _on_before_file_write: veto writes to frozen paths (delegates to is_frozen which already honors the Pitfall HKUDS#5 data-dir exemption) - _on_before_tool_call: shallow arg-scan for frozen path values - _on_careful: warn-only by default; veto-mode via set_careful_veto_mode (flipped by clawteam guard guard composite) - register_safety_subscribers(bus): idempotent opt-in registration called by SprintConductor.__init__ in Plan 02-11; Phase 0 templates never call this so their EventBus stays unsubscribed (Pitfall HKUDS#8 BC hinge) - reset_safety_subscribers(): test helper - 9 tests in tests/test_safety_rails.py now GREEN (Task 1 tests 1-8 plus one pass-through sanity check)
- 4 tests covering sprint lifecycle across Phase 2 primitives (UX-02/03/04/05/09, CORE-05/07, SPRINT-01/02, SKILL-09, SAFETY-02, QUALITY-01/08/11) - Exercises CLI start/status/show/list/pause/resume via typer.CliRunner + JSON envelope shape - Verifies FreezeRegistry data-dir exemption (Pitfall HKUDS#5) - Confirms advance_phase gate chain does not crash on empty artifacts/turn-counters - Validates AMBIGUOUS_SPRINT prefix error envelope (D-25)
…nswer/stop) The 28-command CLI surface is overwhelming for a solo founder. This adds 4 task-oriented top-level commands that wrap the full happy path: clawteam go "<goal>" — create team + start sprint + launch agents clawteam status — dashboard: team/sprint/questions/cost, no tmux clawteam answer — interactive question picker + $EDITOR launch clawteam stop — kill tmux + cleanup team data All four read/write an "active team" pointer at ~/.clawteam/active_team so the user rarely passes --team explicitly. The full 28-command surface (team, sprint, launch, attend, inbox, ...) is unchanged for power users. Rationale: - v1.0 UAT surfaced that the documented flow is `team spawn` + `sprint start` + `launch` (3 commands, easy to forget order) + `attend` + `attend pick <qid>` (requires knowing qid) + `team cleanup X --force` + `tmux kill-session -t clawteam-X` (two ways to stop). - A solo founder shouldn't need to learn the full harness vocabulary. - User explicitly asked for "不要做的太command heavy / 交互流程的简单 易用 / 状态观察的直觉性" (don't make it command-heavy, prioritize easy interaction + intuitive state observation). Design: - `go`: 24-second one-shot on gstack template. Auto-names team `solo-<hash>` unless --name given. Shows 3-step progress (create / sprint / launch) + final rich Panel with next-step hints. - `status`: single-screen view — agent count, sprint ID (truncated), goal, phase progress bar [█░░░░░░] think (1/7), top-5 pending questions (title not qid), cost panel with honest "$0.00 — 999.001 unwired" caveat, tmux session liveness indicator (● green / ○ red). - `answer`: numbered list of pending questions, prompt for choice, open question.md in $EDITOR to read, then offer to open answer.md for writing. Closes the loop for the HUMAN-UAT HKUDS#5 scenario. - `stop`: interactive confirm (--force to skip), kill tmux, run team cleanup, clear active pointer. --keep-data variant preserves team data for post-mortem. Tests: 9 unit tests covering active-team pointer roundtrip, empty-state messaging for all 3 read commands, stale-pointer detection, stop clearing the pointer, --team flag override, dashboard field rendering. E2E verified on CachyOS/fish: clawteam go "improve README" → 24.5s, 11 tmux windows, rich Panel clawteam status → dashboard renders correctly clawteam stop --force → tmux killed, data removed, pointer cleared Addresses v1.0 UAT finding: the primary user flow requires 10+ command invocations spanning team/sprint/launch/attend/tmux. After this change, 90% of the flow is 4 commands. Note: no version bump — folded into v1.0 UAT validation scope per user decision 2026-04-22. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
User request: "用tmux hook 把prompt注入进每一轮对话 包括身份和需要用的
skill 或者用tmux hook去做一些别的事情 总之tmux hook会是一个很好的技术".
Concept: agents drift over long sessions. CEO forgets to delegate (user
just hit this bug), engineer forgets which `clawteam` subcommands are
available, shipper forgets that `/ship` lives in the framework. A
periodic re-injection of "you are X; here's what you should use"
keeps them anchored.
Implementation:
1. `clawteam nudge <pane_id>` CLI command (new clawteam/nudge.py):
- Looks up pane_id → session → team → agent via the persisted
tmux_pane_map.json
- Debounces (60s cooldown per pane) so repeated silence-hook firings
don't spam the claude TUI
- Reads the latest sprint state (current_phase + goal) for context
- Builds a role-specific reminder text from _ROLE_NUDGES table
(each role gets 2-3 lines: what you ARE + what commands you CAN
run now). Appends "Goal: X · Phase: Y" when available.
- Injects via `tmux send-keys -l <text>` + `send-keys Enter` so
claude processes it as a user turn.
- Silent-fail on any error (a noisy hook drowns the user's tmux).
2. tmux hook wiring (tmux_backend.py::_configure_nudge_hook):
- `monitor-silence = 45s` on each window
- `alert-silence` hook fires `clawteam nudge #{pane_id}` in the
background (`run-shell -b`)
- Opt-out via `CLAWTEAM_NUDGE_SECONDS=0` env var
- Called from both `TmuxBackend.tile_panes()` (happy path) and
`TmuxBackend.enable_mouse()` (the --windows non-tiled path) so
every team gets nudge wiring regardless of layout choice.
3. pane_map normalization (tmux_backend.py::tile_panes):
- Post-layout readback of pane_title returned claude's dynamic
status strings like "⠐ CEO" / "✢ ENG-MGR" (spinner glyphs prefix).
- Normalize by matching uppercase title content against the
pre-merge window roster (roster_upper dict). Any title containing
a known role (e.g. "⠐ CEO" contains "CEO") maps to the canonical
lowercase role name.
- pane_map.json now reliably stores {"0": "ceo", "1": "pm", ...}
regardless of what claude's currently showing.
Real-env verified:
clawteam go --no-attach --no-window "test"
cat ~/.clawteam/teams/<team>/tmux_pane_map.json → clean lowercase roster
clawteam status → Panes: 0=ceo 1=pm ...
# wait for claude to boot
clawteam nudge --force <pane_id> → injects role reminder
tmux capture-pane → sees "[nudge] Reminder:
you DELEGATE, never
implement. Decompose..."
Future uses of this hook infrastructure (brainstormed but not impl):
- HKUDS#3 pane-died → log death reason, optional respawn
- HKUDS#5 alert-activity → append pane output to activity.jsonl timeline
- HKUDS#6 session-created → broadcast phase+goal to all panes
- HKUDS#7 send-keys filter → intercept dangerous commands (rm -rf, force push)
- HKUDS#10 pipe-pane → validate agent envelope JSON against schema
Tests: 30 solo/spawn_cli still green (nudge unit tests not added yet
— next commit candidate).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
No description provided.