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
48 changes: 0 additions & 48 deletions .claude/commands/catchup.md

This file was deleted.

7 changes: 7 additions & 0 deletions .claude/deploy.json.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"__comment": "Copy to .claude/deploy.json and customize. Used by /landed.",
"environments": [
{"name": "dev", "workflow": "deploy-dev.yml"},
{"name": "staging", "workflow": "deploy-staging.yml", "health_check": "https://staging.example.com/health"}
]
}
116 changes: 116 additions & 0 deletions .claude/skills/landed/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
---
name: landed
description: Post-merge lifecycle. Verifies merge CI, optional deployment checks, cleans up branches, and prepares next phase.
allowed-tools: Bash, Read, Grep, Glob
disable-model-invocation: true
---

# Landed

Post-merge lifecycle command. Run this after a PR is merged to verify CI, check deployments, clean up branches, and identify next steps.

## Step 1: Detect Merged PR

Identify the PR that was just merged.

1. Run `git branch --show-current` to get the current branch
2. If already on master:
- Check `git reflog --oneline -20` for the previous branch name
- If no branch found, ask the user for the PR number or branch name
3. Look up the merged PR:

```bash
gh pr list --state merged --head <branch> --json number,title,mergeCommit -L 1
```

4. If no PR found: ask the user for the PR number directly
5. Display: PR number, title, merge commit SHA

**Pre-check**: Run `gh auth status` early. If not authenticated, stop and instruct the user to run `gh auth login`.

## Step 2: Verify Merge CI

Check that CI passed on the merge commit.

1. List recent runs on master:

```bash
gh run list --branch master -L 20 --json status,conclusion,databaseId,name,headSha
```

2. Filter to runs whose `headSha` matches the merge commit SHA
3. Evaluate all matched runs:
- **in_progress**: watch still-running run(s) with `gh run watch <id>`
- **success**: all matched runs must be `completed` with `conclusion=success` to proceed
- **failure**: show details via `gh run view <id> --log-failed` for each failing run
- Ask: "Is this a recurring issue or specific to this PR?"
- If recurring: suggest adding to `/done` validation or pre-merge CI
- If specific: diagnose inline from the failed log output

## Step 3: Deployment Verification (Configurable)

Check for deployment status if configured.

1. Check if `.claude/deploy.json` exists
2. If it exists:
- Read the file and iterate over configured environments
- For each environment:
- Watch the deployment workflow: `gh run list --workflow <workflow> --commit <merge-commit-sha> --json status,conclusion,databaseId`
- If `health_check` URL is configured, fetch it and verify a 200 response
- Report per-environment status (success/failure/in_progress)
3. If no config file:
- Ask the user: "Is there a deployment to verify? (skip if none)"
- If user says no or skips: mark as "skipped"

## Step 4: Branch Cleanup

Switch to master and clean up the feature branch.

1. `git checkout master && git pull --rebase`
2. Delete local branch: `git branch -d <branch>` (safe delete, will fail if unmerged)
3. Check if remote branch still exists: `git ls-remote --heads origin <branch>`
4. If remote branch exists:
- Ask the user before deleting: "Delete remote branch origin/<branch>?"
- If approved: `git push origin --delete <branch>`
- If denied: note "kept" in summary
5. If remote branch already deleted (e.g., GitHub auto-delete): note "already deleted by GitHub" in summary

**Edge case**: If already on master and the branch was already deleted locally, skip local deletion gracefully.

## Step 5: Next Phase (P-scope Only)

Check if there is more planned work.

1. Read `docs/IMPLEMENTATION_PLAN.md`
2. If the file exists, check the "Quick Status Summary" table near the top for any phase whose status is not "Complete":
- Identify the next incomplete phase
- Summarize what it covers and any noted dependencies
3. If all phases show "Complete" or no plan file exists: skip this step

## Step 6: Summary Report

Output a summary of everything that happened:

```text
# Landed

PR: #N "<title>" merged into master
CI: PASS (run #ID) | FAIL (run #ID) | WATCHING
Deploy: verified / skipped / failed

## Cleanup
- Deleted local branch: <branch>
- Deleted remote branch: <branch> [or "kept" or "already deleted by GitHub"]
- Now on: master (up to date)

## Next Steps
- [next phase summary / "Ready for new work" / "Project complete"]
```

## Edge Cases

- **Already on master**: check `git reflog` for previous branch, or ask the user
- **PR not found via branch name**: ask the user for the PR number
- **Remote branch already deleted**: GitHub auto-delete is common; handle gracefully
- **gh not authenticated**: check `gh auth status` early and stop with instructions
- **No CI runs found**: report "no CI runs found for merge commit" and proceed
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ Thumbs.db

# Claude Code local overrides
.claude/settings.local.json
.claude/deploy.json
.claude/hooks/*.log
CLAUDE.local.md

Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Development Process

Use `/sync` before starting work, `/design` to formalize a plan, and `/done` when finished. `/design` estimates scope (Q/S/P) during planning; `/done` auto-detects actual scope at completion based on workspace signals. Before creating any plan, read `docs/DEVELOPMENT_PROCESS.md` first.
Use `/sync` before starting work, `/design` to formalize a plan, `/done` when finished, and `/landed` after the PR merges. `/design` estimates scope (Q/S/P) during planning; `/done` auto-detects actual scope at completion based on workspace signals. Before creating any plan, read `docs/DEVELOPMENT_PROCESS.md` first.

## Security

Expand Down
6 changes: 5 additions & 1 deletion docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]

### Added
- `/landed` skill for post-merge lifecycle -- verifies merge CI, optionally checks deployments (via `.claude/deploy.json`), cleans up feature branches, and identifies the next phase for P-scope work
- `.claude/deploy.json.example` template for configuring deployment verification in `/landed`
- Chain-of-Verification (CoVe) commands (`/cove`, `/cove-isolated`) for high-stakes accuracy -- 4-step self-verification process based on Meta's CoVe paper, with an isolated variant that runs verification in a separate agent to prevent confirmation bias
- Template sync workflow (`.github/workflows/template-sync.yml`) for downstream projects to auto-sync upstream template improvements -- runs weekly or on manual trigger, creates PRs with changed template-managed files while preserving project-specific code
- Python-specific SOLID checklist in `refactoring-specialist` agent -- checks for mutable default arguments, ABC/Protocol misuse, missing dependency injection, god classes, `@property` overuse, and circular imports
Expand All @@ -18,7 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Workflow skill `/done` auto-detects scope (Q/S/P) and runs the full validate-ship-document pipeline, including the former `/ship` checklist
- Three graduated permission tiers (Assisted, Autonomous, Full Trust) for devcontainer environments -- container isolation (firewall, non-root, hooks) enables safely expanding Claude Code permissions, reducing unnecessary prompts from dozens per session to zero in Tier 2/3 while blocking tool installation, package publishing, and container escape vectors via curated deny lists and a policy-enforcement hook
- 5 hook scripts in `.claude/hooks/` run automatically during Claude Code sessions -- 3 security hooks block destructive commands, secret leaks, and invisible Unicode attacks in real time; 2 productivity hooks auto-format Python files and auto-run associated tests after every edit
- 4 slash commands (`/catchup`, `/cove`, `/cove-isolated`, `/security-audit`) provide context restoration, chain-of-verification for accuracy, and a 6-phase security posture scan with A-F grading
- 3 slash commands (`/cove`, `/cove-isolated`, `/security-audit`) provide chain-of-verification for accuracy and a 6-phase security posture scan with A-F grading
- 3 new specialized agents: `security-auditor` (OWASP-based vulnerability analysis, read-only), `refactoring-specialist` (SOLID/code smell detection, read-only), `output-evaluator` (LLM-as-Judge quality scoring for automated pipelines)
- 4 review rules in `.claude/rules/` auto-loaded as project context -- cover architecture, code quality, performance, and test quality concerns that linters cannot catch
- AI-powered PR review via GitHub Actions (`claude-code-review.yml`) using `anthropics/claude-code-action@v1` -- automatically reviews PRs with read-only tools on open/sync/ready_for_review
Expand All @@ -36,6 +38,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

- Development workflow expanded from sync-design-done to sync-design-done-landed, closing the post-merge gap
- QSP scope classification is now auto-detected by `/done` based on branch, diff size, and IMPLEMENTATION_PLAN.md state -- users no longer classify manually before starting work
- PCC shorthand now triggers `/done` instead of manually executing S.5-S.7
- Setup script now makes `.claude/hooks/*.sh` files executable after placeholder substitution -- hook scripts work immediately after project setup without manual `chmod`
Expand All @@ -50,6 +53,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Removed

- `/catchup` command -- its context restoration role overlaps with `/sync`, which already covers pre-flight workspace state
- `/ship` slash command -- its 3-tier validation checklist (Blockers, High Priority, Recommended) is preserved in `/done` Phase 2
- Shell Command Style and Allowed Operations sections from CLAUDE.md -- absolute path preferences and read-only command lists are now handled by settings.json permission rules rather than prose instructions

Expand Down
10 changes: 10 additions & 0 deletions docs/DECISIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,16 @@ When a decision is superseded or obsolete, delete it (git history preserves the
- `/sync` and `/done` have `disable-model-invocation: true` (side effects: git fetch, git commit/push, PR creation); `/design` is intentionally model-invocable so Claude can suggest it during brainstorming
- QSP paths (Q/S/P) and their step descriptions preserved in DEVELOPMENT_PROCESS.md -- skills orchestrate the paths, they don't replace them

## 2026-03-10: Post-merge /landed Skill

**Request**: Close the post-merge gap in the sync-design-done workflow. After `/done` creates a PR and it merges, nothing verifies merge CI, checks deployments, cleans up branches, or identifies the next phase.

**Decisions**:
- New `/landed` skill (not command) -- follows same pattern as `/sync` and `/done` with `disable-model-invocation: true`
- `/catchup` removed -- its context restoration overlaps with `/sync` which already covers pre-flight state
- Optional deployment verification via `.claude/deploy.json` (gitignored) -- not all projects have deployments, so it's opt-in with an example file
- Phase detection uses "Quick Status Summary" table in IMPLEMENTATION_PLAN.md, not `- [ ]` checkboxes -- matches actual file structure

## 2026-03-10: Template Integration CI Pipeline

**Request**: Create a CI pipeline that applies the template in various settings to catch template bugs before merge.
Expand Down
14 changes: 11 additions & 3 deletions docs/DEVELOPMENT_PROCESS.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,14 @@ All agents use `subagent_type: "general-purpose"`. Do NOT use `feature-dev:code-

---

## Post-merge

Run `/landed` after a PR is merged. It verifies merge CI, optionally checks
deployments (via `.claude/deploy.json`), cleans up branches, and identifies the
next phase for P-scope work.

---

## P. Project Path

**P.1 Analyze**
Expand Down Expand Up @@ -161,11 +169,10 @@ All hooks require `jq` for JSON parsing and degrade gracefully if jq is missing.

## Commands

4 slash commands in `.claude/commands/`:
3 slash commands in `.claude/commands/`:

| Command | Purpose |
|---------|---------|
| `/catchup` | Context restoration after `/clear`. Reads IMPLEMENTATION_PLAN.md, CHANGELOG.md, git history; recommends next steps. |
| `/cove` | Chain-of-Verification (CoVe) for high-stakes accuracy. 4-step process: generate baseline, plan verifications, verify from source, produce corrected response. |
| `/cove-isolated` | Isolated CoVe variant. Verification step runs in a separate agent that cannot see the baseline response, preventing confirmation bias. |
| `/security-audit` | 6-phase Python security scan (deps, secrets, code patterns, input validation, config, scoring). Outputs A-F grade. |
Expand All @@ -174,13 +181,14 @@ All hooks require `jq` for JSON parsing and degrade gracefully if jq is missing.

## Skills

4 skills in `.claude/skills/`:
5 skills in `.claude/skills/`:

| Skill | Purpose |
|-------|---------|
| `/sync` | Pre-flight workspace sync. Fetches remote, reports branch state, dirty files, ahead/behind, recent commits. |
| `/design` | Crystallize brainstorming into a structured plan. Reads DECISIONS.md for conflicts, auto-classifies scope, outputs actionable plan. |
| `/done` | Universal completion. Auto-detects scope (Q/S/P), validates (3-tier checklist), ships/lands/delivers, updates docs. Absorbs former `/ship`. |
| `/landed` | Post-merge lifecycle. Verifies merge CI, optional deployment checks, cleans up branches, prepares next phase. |
| `/edit-permissions` | Manage Claude Code permission rules in settings.json. Pattern syntax reference and safety guardrails. |

---
Expand Down
13 changes: 0 additions & 13 deletions tests/test_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
COMMANDS_DIR = Path(__file__).parent.parent / ".claude" / "commands"

ALL_COMMANDS = [
"catchup.md",
"cove.md",
"cove-isolated.md",
"security-audit.md",
Expand Down Expand Up @@ -71,18 +70,6 @@ def test_command_has_markdown_heading(self, command_name: str) -> None:
class TestCommandContent:
"""Verify specific command content."""

def test_catchup_reads_implementation_plan(self) -> None:
content = (COMMANDS_DIR / "catchup.md").read_text(encoding="utf-8")
assert "IMPLEMENTATION_PLAN" in content, "catchup should reference IMPLEMENTATION_PLAN.md"

def test_catchup_reads_changelog(self) -> None:
content = (COMMANDS_DIR / "catchup.md").read_text(encoding="utf-8")
assert "CHANGELOG" in content, "catchup should reference CHANGELOG.md"

def test_catchup_checks_git(self) -> None:
content = (COMMANDS_DIR / "catchup.md").read_text(encoding="utf-8")
assert "git log" in content, "catchup should analyze git history"

def test_security_audit_has_scoring(self) -> None:
content = (COMMANDS_DIR / "security-audit.md").read_text(encoding="utf-8")
assert "Grade" in content or "grade" in content, "security-audit should include grading"
Expand Down
28 changes: 27 additions & 1 deletion tests/test_skills.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"sync",
"design",
"done",
"landed",
]


Expand Down Expand Up @@ -80,7 +81,7 @@ def test_skill_has_markdown_heading(self, skill_name: str) -> None:
class TestSkillSideEffects:
"""Verify side-effect declarations are correct."""

@pytest.mark.parametrize("skill_name", ["sync", "done"])
@pytest.mark.parametrize("skill_name", ["sync", "done", "landed"])
def test_side_effect_skills_disable_model_invocation(self, skill_name: str) -> None:
content = (SKILLS_DIR / skill_name / "SKILL.md").read_text(encoding="utf-8")
parts = content.split("---", 2)
Expand Down Expand Up @@ -181,3 +182,28 @@ def test_done_has_scope_detection(self) -> None:
assert "ship" in content.lower(), "done should describe Q=ship"
assert "land" in content.lower(), "done should describe S=land"
assert "deliver" in content.lower(), "done should describe P=deliver"

# /landed
def test_landed_detects_merged_pr(self) -> None:
content = (SKILLS_DIR / "landed" / "SKILL.md").read_text(encoding="utf-8")
assert "gh pr list" in content, "landed should detect merged PR"

def test_landed_verifies_ci(self) -> None:
content = (SKILLS_DIR / "landed" / "SKILL.md").read_text(encoding="utf-8")
assert "gh run" in content, "landed should verify CI runs"

def test_landed_cleans_branches(self) -> None:
content = (SKILLS_DIR / "landed" / "SKILL.md").read_text(encoding="utf-8")
assert "git branch -d" in content, "landed should clean up branches"

def test_landed_checks_deployment(self) -> None:
content = (SKILLS_DIR / "landed" / "SKILL.md").read_text(encoding="utf-8")
assert "deploy.json" in content, "landed should check deployment config"

def test_landed_checks_next_phase(self) -> None:
content = (SKILLS_DIR / "landed" / "SKILL.md").read_text(encoding="utf-8")
assert "IMPLEMENTATION_PLAN" in content, "landed should check for next phase"

def test_landed_produces_summary(self) -> None:
content = (SKILLS_DIR / "landed" / "SKILL.md").read_text(encoding="utf-8")
assert "# Landed" in content, "landed should produce a summary report"
Loading