Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ src/canopy/
│ ├── bot_resolutions.py # M3: persistent log of bot comments addressed via `commit --address`
│ ├── bot_status.py # M3: per-feature bot-comment rollup
│ ├── augments.py # M2: per-workspace augment resolver (preflight_cmd, review_bots, ...)
│ ├── historian.py # M4: cross-session feature memory at .canopy/memory/<feature>.md
│ ├── preflight_state.py # records preflight result for state machine
│ ├── reads.py # 4 alias-aware read primitives
│ ├── realign.py # internal helper used by switch (deprecated from CLI/MCP in Wave 2.9)
Expand All @@ -53,7 +54,7 @@ src/canopy/
│ ├── github.py # GitHub PR + comments (MCP or gh CLI fallback)
│ └── precommit.py # detect + run pre-commit hooks
└── mcp/
├── server.py # MCP server — 49 tools, stdio transport
├── server.py # MCP server — 54 tools, stdio transport
└── client.py # MCP client — stdio + HTTP+OAuth transports
```

Expand Down Expand Up @@ -98,7 +99,7 @@ For integration testing against real services, see `~/projects/canopy-test/` (me
- **Action contract:** `actions/protocol.py` (planned) will formalize the per-repo `{status, before, after, reason?}` shape. For now, each action returns it ad-hoc.
- **Skill bundling:** Bundled skills live at `src/canopy/agent_setup/skills/<name>/SKILL.md`. `canopy setup-agent` copies them to `~/.claude/skills/<name>/SKILL.md`. The default `using-canopy` skill always installs; opt-in extras (e.g. `augment-canopy`) install via `--skill <name>` (repeatable). Foreign skills with the same path are not overwritten without `--reinstall`. The `_SKILL_SOURCE` constant remains as a backward-compat alias pointing at `using-canopy`'s source.

## MCP Server (49 tools)
## MCP Server (54 tools)

Grouped by topic. Run with `canopy-mcp` (entry point) or `python -m canopy.mcp.server`.

Expand Down
2 changes: 1 addition & 1 deletion docs/agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ How AI coding agents (Claude Code primarily; others by analogy) integrate with c

Three pieces, all installed in one step by `canopy init`:

1. **Canopy MCP server** (`canopy-mcp` binary) — 49 tools exposing every canopy operation. Registered in `<workspace>/.mcp.json`.
1. **Canopy MCP server** (`canopy-mcp` binary) — 54 tools exposing every canopy operation. Registered in `<workspace>/.mcp.json`.
2. **`using-canopy` skill** at `~/.claude/skills/using-canopy/SKILL.md` — tells the agent *when* to prefer canopy MCP over raw bash.
3. **Per-workspace MCP config** in `<workspace>/.mcp.json` with `CANOPY_ROOT` set so the server scopes to the right workspace.

Expand Down
2 changes: 2 additions & 0 deletions docs/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ Write actions and execution.
| `canopy sync` | Pull default branch + rebase feature branches across repos. |
| `canopy commit -m <msg> [--feature <f>] [--repo <r,...>] [--paths <p ...>] [--no-hooks] [--amend] [--address <id>]` | **Wave 2.3 + M3.** Commit across every repo in the canonical (or named) feature with a single message. Pre-flight refuses with `BlockerError(code='wrong_branch')` if any in-scope repo has drifted; per-repo hook failures don't cancel the others (status: `hooks_failed`). `--address <comment-id>` (numeric id or GitHub URL) auto-suffixes the message with the bot comment's title + URL and records the resolution in `.canopy/state/bot_resolutions.json`. Non-bot comments raise `BlockerError(code='not_a_bot_comment')`. |
| `canopy bot-status [--feature <f>] [--unresolved-only]` | **M3.** Per-feature rollup of bot review comments — total / resolved / unresolved per repo + an `all_resolved` flag. Bot vs human classification respects `[augments] review_bots` in canopy.toml. |
| `canopy historian show [<feature>]` | **M4.** Print the rendered memory file for a feature (3 sections: resolutions log, PR context, sessions). Returns empty when no memory has been recorded yet. |
| `canopy historian compact [<feature>] [--keep-sessions <n>]` | **M4.** Trim the Sessions section to the most-recent N (default 5). Resolutions log + PR context are preserved regardless. v1 is mechanical (no LLM); future iterations will summarize. |
| `canopy push [--feature <f>] [--repo <r,...>] [--set-upstream] [--force-with-lease] [--dry-run]` | **Wave 2.3.** Push the feature branch in every in-scope repo. Pre-flight raises `BlockerError(code='no_upstream')` if any repo lacks an upstream and `--set-upstream` was not passed; the fix-action carries the same args + `--set-upstream` so an agent retries mechanically. Per-repo statuses: `ok`, `up_to_date`, `rejected`, `failed`. |

## Verify
Expand Down
10 changes: 10 additions & 0 deletions docs/concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,16 @@ The dashboard's CTA is whichever node you're sitting on. Drift always wins — i

For **worktree-backed** features, the drift detection runs against the worktree path (not main), so a worktree-backed feature is only `drifted` if someone manually `git checkout`'d to a different branch *inside the worktree*. The fix is `switch` (re-establishes the feature context), not `realign` (which would touch main and undo the protection worktrees were supposed to provide).

### Cross-session memory (M4)

`canopy switch` returns a `memory: <markdown>` field rendered from `<workspace>/.canopy/memory/<feature>.md` — a per-feature persistent log of decisions, comment activity, PR context, and session entries. Agents read it on switch instead of re-deriving "where was I, what's resolved, what's blocked." The memory is append-only (concurrent agents on the same feature flock-serialize), with three top-level sections:

- **Resolutions log** — per-comment outcomes (✓ resolved, ⊙ likely-resolved by classifier, ⊘ deferred). Never compacted.
- **PR context** — one block per PR with rationale + chronological updates. Never compacted.
- **Sessions** — newest-first per-session entries (decisions, pauses, events). Trimmed by `historian_compact`.

Auto-capture wires existing canopy actions: `commit --address` mirrors the bot resolution into memory; `github_get_pr_comments` records each actionable thread + the temporal classifier's likely-resolved batch (deduped per session). Explicit `historian_decide` / `historian_pause` cover the agent's narrative side. See [docs/plans/historian.md](plans/historian.md) for the full design.

## 4. The canonical-slot model

Every feature in canopy lives in exactly one of three states:
Expand Down
5 changes: 5 additions & 0 deletions docs/mcp.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ Grouped by topic. Every tool is alias-aware where it accepts a feature input.
| `feature_link_linear` | Attach a Linear issue to a feature |
| `feature_state` | **Dashboard backend.** Returns `{state, summary, next_actions, warnings}`. State ∈ `{drifted, needs_work, in_progress, ready_to_commit, ready_to_push, awaiting_bot_resolution, awaiting_review, approved, no_prs}`. The `summary` carries split `actionable_human_count` + `actionable_bot_count` (M3). See [concepts.md](concepts.md#3-the-9-state-machine). |
| `bot_comments_status` | **M3.** Per-feature rollup of bot review comments — `{feature, repos: {<repo>: {pr_number, total, resolved, unresolved, threads}}, all_resolved, any_bot_comments}`. Resolutions come from the persistent log written by `commit --address`. |
| `historian_decide` | **M4.** Record one or more decisions in the feature's memory file. Accepts `decisions: [{title, rationale}, ...]`. Deduped per-session by title. |
| `historian_pause` | **M4.** Record why the agent stopped — what's blocked, what's needed next. |
| `historian_defer_comment` | **M4.** Mark a review comment as intentionally deferred with a reason. |
| `feature_memory` | **M4.** Read the rendered memory file as markdown — `{feature, memory: <markdown or "">}`. |
| `historian_compact` | **M4.** Trim the Sessions section to the most-recent N (default 5). Resolutions log + PR context are always preserved. |

#### Action (Wave 2)

Expand Down
4 changes: 2 additions & 2 deletions docs/plans/INDEX.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Live status of canopy's planned work. Update this file as milestones progress; each plan's frontmatter is the per-plan source of truth, this doc is the rolled-up dashboard.

**Last updated:** 2026-05-02 (M0, M1, M2, M3, M5 shipped)
**Last updated:** 2026-05-02 (M0, M1, M2, M3, M5 shipped; M4 in-progress)
**Roadmap:** [roadmap.md](roadmap.md) — full architecture context, cross-cutting decisions, sequencing rationale

## Status legend
Expand All @@ -28,7 +28,7 @@ Live status of canopy's planned work. Update this file as milestones progress; e
Per-workspace `[augments]` block in canopy.toml + opt-in `augment-canopy` skill. Wires `preflight_cmd`; reserves `review_bots` (M3) and `test_cmd` (future).
- [x] ✅ **M3 — Bot-comment tracking** — [archive/bot-tracking.md](archive/bot-tracking.md) · shipped 2026-05-02
Bot vs human comment classification, `commit --address <id>`, `awaiting_bot_resolution` state, `bot-status` rollup.
- [ ] 🟦 **M4 — Historian** — [historian.md](historian.md) · P1 · ~5-6d · depends on M3
- [ ] 🟨 **M4 — Historian** — [historian.md](historian.md) · P1 · ~5-6d · depends on M3
Cross-session feature memory at `.canopy/memory/<feature>.md`. Auto-read on `canopy switch`.
- [x] ✅ **M5 — Issue-provider scaffold** — [archive/issue-providers.md](archive/issue-providers.md) · shipped 2026-04-27
Linear refactored into the contract; GitHub Issues backend. New `issue_get` / `issue_list_my_issues` MCP tools; old `linear_*` retained as deprecated aliases. Closes [#5](https://github.com/ashmitb95/canopy/issues/5).
Expand Down
2 changes: 1 addition & 1 deletion docs/plans/historian.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
status: queued
status: in-progress
priority: P1
effort: ~5-6d
depends_on: ["bot-tracking.md"]
Expand Down
15 changes: 15 additions & 0 deletions src/canopy/actions/commit.py
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,21 @@ def commit(
comment_title=addressed_info["title"],
comment_url=addressed_info["url"],
)
# Mirror the resolution into historian (M4) so the per-feature
# memory file's Resolutions log stays current. Non-fatal if
# the historian write fails — the canonical state is still in
# bot_resolutions.json.
try:
from . import historian
historian.record_comment_resolved(
workspace.config.root, feature_name,
comment_id=addressed_info["comment_id"],
commit_sha=sha,
gist=addressed_info["title"],
url=addressed_info["url"],
)
except Exception:
pass
addressed_info["sha"] = sha
addressed_info["recorded"] = True
else:
Expand Down
Loading
Loading