repo-pulse

RepoPulse — Development Workflow

This document describes how to develop RepoPulse using the SpecKit / Specification-Driven Development (SDD) workflow.


Prerequisites


Feature loop (spec → plan → tasks → implement)

One feature at a time, fully through implementation before starting the next. Do not batch specs across features.

Step 1 — Specify

/speckit.specify [P1-F01] Repo Input

Review the generated spec. It is a contract — approve it before proceeding.

Step 2 — Plan

/speckit.plan [P1-F01]

Review the plan. Verify it does not introduce any dependency that would block Phase 2 or Phase 3.

Step 3 — Tasks

/speckit.tasks [P1-F01]

Review the task list before implementation begins.

Step 4 — Implement

/speckit.implement [P1-F01]

Implement the feature per the generated tasks.

Step 5 — PR

Before opening a PR, verify the Definition of Done (constitution Section XII):

Once done, open a PR and merge before starting the next feature.


Workflow sub-agents

Three project-scoped sub-agents in .claude/agents/ encode the RepoPulse workflow rules that live in CLAUDE.md, .specify/memory/constitution.md, and this document. Two are bounded, read-heavy checks; the third (pr-test-plan-runner) executes the automatable portion of a PR’s Test plan. Running them is how the rule becomes mechanical instead of a thing you have to remember.

Agent When to invoke How to invoke
spec-reviewer After /speckit.specify, before the mandatory approval gate in CLAUDE.md. Mention spec-reviewer in a prompt, or @spec-reviewer (agent) review specs/<N>-<slug>/spec.md.
dod-verifier Before git push / PR open. Runs the Definition of Done checklist from constitution §XII against the current branch. @dod-verifier (agent) walk the DoD for this branch.
pr-test-plan-runner After PR open, before asking the user to merge. Executes automatable items in ## Test plan (unit tests, lint, typecheck, build, focused vitest/eslint/tsc runs), ticks the matching checkboxes via gh pr edit, and posts an audit comment with the per-item results. @pr-test-plan-runner (agent) run PR #<N>.

Each agent returns a structured report. spec-reviewer returns PASS / FAIL with citations into the constitution and docs/PRODUCT.md; dod-verifier returns a per-item SATISFIED / BLOCKED / REQUIRES HUMAN SIGN-OFF punch list with command output as evidence; pr-test-plan-runner returns a per-item AUTO-PASS / AUTO-FAIL / MANUAL / ALREADY-CHECKED classification plus an overall READY / BLOCKED verdict, and additionally posts a PR comment with an agent-generated disclaimer (> Automated report from pr-test-plan-runner …) so reviewers have a durable audit trail of what ran, when, and with what result.

PR merge discipline: pr-test-plan-runner’s tool allowlist is narrow — 13 patterns total: Bash(gh pr view:*), Bash(gh pr edit:*), Bash(gh pr comment:*) for PR interaction, plus ten automatable command prefixes (Bash(npm test:*), Bash(npm run lint:*), Bash(npm run typecheck:*), Bash(npm run build:*), Bash(npm run test:unit:*), Bash(npm run test:integration:*), Bash(npm run demo:*), Bash(npx vitest:*), Bash(npx eslint:*), Bash(npx tsc:*)). gh pr merge, gh pr close, gh pr ready, and gh pr review are explicitly absent from the allowlist, and the agent prompt additionally forbids extracted Test plan commands matching those prefixes, rm, git reset, git push --force, sudo, curl, wget, ssh, npm run test:e2e, and npx playwright. The CLAUDE.md rule that PR merging is a manual user action is intact — none of the three agents is permitted to merge.

Sub-agents inherit the parent session’s .claude/settings.json allowlist as a ceiling; they cannot widen it. No entry was added to settings.json for these three agents — the existing allowlist already covers every tool they need.


This section is the single source of truth for RepoPulse MCP guidance. CLAUDE.md intentionally points here instead of duplicating a server list.

Recommended shared aliases:

Alias Why it helps in this repo
github Read issues, PRs, reviews, branches, and Actions runs without copying GitHub data into chat. Most useful for the issue-driven SpecKit flow and future Phase 3 integrations.
playwright Exercise the Next.js UI, org-summary flows, auth states, and regressions through a browser tool instead of manual narration or screenshots.
vercel Inspect preview deployments, environment mismatches, and runtime failures for the Vercel-hosted app.
context7 Pull current framework and API docs (Next.js, React, Playwright, GitHub APIs) when implementation details are newer than the repo docs.

Rules:

Repo status:


Phase 1 feature order

This is the planned implementation order for Phase 1. It may differ from the feature listing order in docs/PRODUCT.md, which remains the canonical product definition.

# Feature ID Feature Status
1 P1-F01 Repo Input ✅ Done
2 P1-F02 Authentication ✅ Done
3 P1-F04 Data Fetching ✅ Done
4 P1-F15 Results Shell ✅ Done
5 P1-F05 Ecosystem Map ✅ Done
6 P1-F03 Deployment ✅ Done
7 P1-F07 Metric Cards ✅ Done
8 P1-F09 Contributors ✅ Done
9 P1-F08 Activity ✅ Done
10 P1-F10 Responsiveness ✅ Done
11 P1-F11 Health Ratios ❌ Deprecated (tab removed, ratios live in domain tabs + Comparison)
12 P1-F16 Org-Level Repo Inventory ✅ Done
13 P1-F06 Repo Comparison ✅ Done
14 P1-F14 GitHub OAuth Authentication ✅ Done
15 P1-F13 Export ✅ Done
16 P1-F12 Missing Data & Accuracy ✅ Done

Testing

Run these checks before opening a PR:

npm test
npm run test:e2e
npm run lint
npm run build

Multi-worktree local development (DEV_GITHUB_PAT)

The GitHub OAuth App registers a single callback URL (http://localhost:3000/api/auth/callback). Running multiple worktrees concurrently means only the worktree on port 3000 can complete OAuth.

To work around this in next dev (only — never production), set a GitHub PAT in .env.local:

# .env.local
DEV_GITHUB_PAT=ghp_your_personal_access_token

Required scope: public_repo (read only).

When set, clicking “Sign in with GitHub” short-circuits the OAuth round-trip and grants a session using the PAT directly. Unset the variable (or leave it blank) to restore the normal OAuth flow.

Safety layers:

See issue #207 for the full rationale and constitution discussion.

Spawning worktrees with agentctl

Install agentctl before use: see the install guide.

agentctl automates the parallel-worktree workflow: it provisions an isolated git worktree per issue, picks a free dev-server port, copies .env.local (so DEV_GITHUB_PAT flows through), starts next dev, and launches a coding agent with a kickoff prompt pointing at the issue.

Run agentctl --help for the canonical usage reference.

Spawn:

# interactive — slug auto-derived from the GitHub issue title
agentctl spawn 207

# headless — agent runs in background, log -> agent.log
agentctl spawn --headless 207

# batch
for i in 210 211 212; do agentctl spawn --headless "$i"; done

agentctl spawn creates ../repo-pulse-<issue>-<slug>/ on a new branch named <issue>-<slug>, picks the next free port in 3010–3100, runs npm install, starts next dev in the background (log: dev.log), writes a .agent key=value state file, and launches the agent with a prompt that runs the SpecKit lifecycle and opens a PR (never merges — see CLAUDE.md).

Agent selection:

The --agent <name> flag selects which coding agent adapter to use.

agentctl spawn 207                          # uses claude adapter (default)
agentctl spawn --agent copilot 207          # uses copilot adapter
agentctl spawn --headless --agent copilot 207

.agent state file:

agentctl writes a single key=value .agent file in the worktree root:

agent=claude
session-id=abc-def-12345678
dev-pid=67890
agent-pid=12345

The core writes agent, session-id, and dev-pid after the dev server starts. The adapter appends agent-pid once the agent process is running.

Numbering rule

The branch, worktree, and spec directory for a feature always share the same numeric prefix. Two paths, one convention:

Flow Worktree path Branch Spec directory
Worktree-driven (issue) ../repo-pulse-249-align-numbering 249-align-numbering specs/249-align-numbering/
Manual sequential (none) 230-some-refactor specs/230-some-refactor/
Timestamp (opt-in) (none) 20260416-143022-refactor specs/20260416-143022-refactor/

When agentctl spawn <N> pre-creates the branch <N>-<slug>, /speckit.specify inside the worktree detects the ^[0-9]+- prefix on the current HEAD and reuses it verbatim — it does not scan specs/ for the next free sequential number. Outside the worktree flow (a manual /speckit.specify on main), the sequential-scan fallback applies unchanged.

The rare collision — manual sequential claims slot N just before issue #N is filed and worktree-spawned — surfaces as a loud error naming the conflicting branch or spec directory. Resolution: rename/remove the colliding entity, or pick a different issue number. /speckit.specify never silently renumbers.

Mandatory pause after /speckit.specify. Both interactive and --headless spawns halt after /speckit.specify and wait for your explicit approval before continuing to /speckit.plan. The kickoff prompt tells the agent to report the generated spec path and wait for one of the phrases "proceed", "approved", or "go to plan". Spec revisions re-enter the paused state; only an approval phrase releases it. This exists because the spec is the highest-leverage artifact — revisions applied after plan/tasks are generated force the agent to re-derive everything downstream.

Permission model for headless spawns. The repo commits a project-scoped Claude Code permission policy at .claude/settings.json. Every session launched inside this repo or any git worktree of it — interactive or claude -p — inherits that allowlist. This is what unblocks headless spawns from freezing on the first tool-approval prompt (issue #238).

The allowlist is intentionally narrow:

Explicitly not allowed: Bash(rm:*), Bash(curl:*), Bash(wget:*), Bash(sudo:*), Bash(ssh:*), unapproved project-scoped MCP servers, any blanket wildcard, and bypassPermissions. A tool outside the allowlist falls through to the normal prompt path; in a headless session, that still blocks — which is the signal to extend the allowlist by PR, not to bypass it.

Extending the allowlist: edit .claude/settings.json in a PR. The PR description must name the SpecKit helper, git subcommand, npm script, gh subcommand, or project-scoped MCP alias that needs the new entry. No secrets, no blanket wildcards, no destructive shell commands.

Releasing a paused headless session. For --headless spawns, use the fire-and-forget release commands:

# Approve the generated spec and run Stage 2 (plan → tasks → implement → PR) in the background:
agentctl approve-spec <issue>

# Or send revision feedback to the paused session; it edits the spec in place and re-enters the pause:
agentctl revise-spec <issue> "Add an acceptance scenario for empty input."

Both commands resolve the worktree for <issue>, read the agent key from <worktree>/.agent to load the correct adapter, then call agent_resume as a detached nohup process appending to agent.log. They return control to your shell in under 5 seconds — you do not need to keep the invoking terminal open.

Preconditions (both commands exit non-zero with a clear error if unmet):

Additional rule for revise-spec: empty feedback is rejected. Repeated revise-spec rounds accumulate — each round edits the spec as it stands after the previous round.

Manual fallback (still supported): cd ../repo-pulse-<issue>-<slug> && claude --resume opens an interactive session if you want to review and revise the spec conversationally. This attaches your terminal; approve-spec / revise-spec do not.

The pause is per-worktree: in a batch spawn (for i in 210 211 212; do agentctl spawn --headless "$i"; done) you review and release each worktree independently, but the release itself is fire-and-forget, so for i in 210 211 212; do agentctl approve-spec "$i"; done walks away with three PRs on the way.

Cleanup:

# Post-merge, from the main repo: pull main, kill processes, remove the
# worktree, delete the local+remote branch (refuses if unmerged).
agentctl cleanup-merged 207

# Discard unmerged work (unrecoverable: kills processes, removes worktree,
# deletes local + remote branch — prompts for YES confirmation).
agentctl discard 207
# From inside the worktree (e.g. the same shell the agent session ran in),
# the issue number is inferred from the current branch's `^[0-9]+-` prefix:
cd ../repo-pulse-207-<slug>
agentctl cleanup-merged    # no arg needed

When invoked with no argument from a linked worktree, the command chdirs to the main repo, auto-checks out main if the primary worktree is on another branch (refuses on dirty state — never force-discards), then runs the standard cleanup flow. On success, if the caller’s current working directory was the removed worktree, the command prints a final-line notice of the form note: your shell's previous CWD (...) no longer exists — run \cd \` to continue` so the stranded shell knows where to go. Inference only fires from inside a linked worktree — the no-argument form from the main repo clone still prints the existing usage error and takes no destructive action.

cleanup-merged verifies merge status by querying the associated PR’s state via gh pr view <branch> --json statenot by local ancestry. This matters because squash-merges and rebase-merges on GitHub produce a merge commit that is not an ancestor of the local feature branch, so the older ancestry check (git branch -d) would silently refuse even after a real merge. When the PR state is MERGED, the local branch is force-deleted, then git push origin --delete <branch> removes the remote ref. If the remote was already removed (e.g. GitHub’s “Automatically delete head branches” setting fired at merge time), the step prints Remote branch <branch> already removed and exits 0. Any other remote-delete failure (network, auth, protected branch) surfaces a warning and exits non-zero — the worktree and local branch are already gone, so only a manual git push origin --delete <branch> remains. When the PR is OPEN, CLOSED without merge, missing, or gh cannot reach GitHub, the command refuses and points to discard. Use discard as the escape hatch for unmerged branches; it also deletes the local and remote branch.

cleanup-merged is self-healing around two common failure modes. First, after killing the dev-server PID it waits up to 5 s for the process to exit before calling git worktree remove, because next dev can still hold file descriptors under .next/ when kill returns and macOS rejects the rmdir with Directory not empty. Second, if git worktree remove --force still fails but git has already unregistered the worktree admin dir, the command falls back to rm -rf <dir> and continues — the partial success is treated as a full cleanup rather than a hard failure. Re-invoking the command after an earlier mid-sequence failure also works: the command detects an orphaned dir / local branch / remote branch for <issue> by matching the ^<issue>-* branch prefix, and finishes whatever remains. The MERGED PR-state guard is still enforced on the recovery path.

If something gets stuck:

git worktree list                 # what's still registered
git worktree prune                # drop stale entries for deleted paths
lsof -iTCP:3010-3100 -sTCP:LISTEN # any dev servers still bound?

Phase 2 feature order

Phase 2 adds new scoring buckets to the health score. Requirements specs live in the linked GitHub issues — not in PRODUCT.md. See Spec Ownership for the rationale.

# Feature ID Feature Issues Status
1 P2-F01a Documentation scoring (basic) #66 ✅ Done
2 P2-F02 Licensing & Compliance #115 ✅ Done
3 P2-F03 Inclusive naming #107 ✅ Done
4 P2-F07 Security scoring #68 ✅ Done
5 P2-F04 Governance & Transparency #116 ✅ Done
6 P2-F05 Community scoring #70 ✅ Done
7 P2-F06a CNCF foundation support #398 (#399, #400, #157, #210, #211) ✅ Done
8 P2-F06b Foundation-aware recommendations (Apache, LF, others) #119  
9 P2-F08 Accessibility & Onboarding #117 ✅ Done
10 P2-F09 Release health scoring #69 ✅ Done
11 P2-F10 Development cadence #73 ✅ Done
12 P2-F11 Project maturity #74 ✅ Done
13 P2-F12 Ecosystem Reach #118  
14 P2-F01b Documentation scoring (advanced) #110, #67  
15 P2-F13 Org governance audit — stale admin detection, 2FA enforcement #287, #286 (children of #285) ✅ Done
16 P2-F14 Org governance audit — member permission distribution #288 ✅ Done
17 P2-F15 Foundation input mode — top-level tab (repos + org) #413 ✅ Done
18 P2-F16 Foundation input mode — Projects board sub-mode #411  
19 P2-F17 Repo card progressive disclosure (UX density) #483 ✅ Done

Phase 3 feature order

Phase 3 delivers the OSS Health Score through additional channels, wrapping the shared analyzer module without duplicating logic.

# Feature ID Feature Issues Status
1 P3-F01 Public REST API #120  
2 P3-F02 GitHub Action  
3 P3-F03 MCP Server  
4 P3-F04 Embeddable badge #72  
5 P3-F05 CLI tool #82  
6 P3-F06 PR comment bot #83  
7 P3-F07 VS Code extension #84  
8 P3-F08 Webhook receiver #85  

Phase 4

Phase 4 ports the application to support GitLab in addition to GitHub. It introduces a provider abstraction layer (GitHub and GitLab implement the same interface) so the analyzer, delivery phases, and UI remain unchanged. Phase 4 begins only after Phase 3 is complete.

Feature development flow (Phase 2+)

For Phase 2 and beyond, the requirements spec for each feature lives in its GitHub issue. The SpecKit workflow reads the issue as input:

GitHub Issue (requirements spec)
  │  Acceptance criteria, signals, scoring approach, open questions
  │
  ▼
/speckit.specify (reads issue as input)
  │  Generates specs/NNN-feature-name/ with TypeScript contracts
  │
  ▼
/speckit.plan → /speckit.tasks → /speckit.implement
  │  Plan, break down, build
  │
  ▼
PR with test plan → merge
  │  Mark feature as ✅ Done in the table above

Key distinction: The GitHub issue defines what and why (requirements). SpecKit generates how (TypeScript interfaces, view props, data flow contracts). They are not duplicates — the issue is the input, the spec file is the output.

This replaces the Phase 1 pattern where PRODUCT.md contained inline acceptance criteria for every feature. Phase 1 specs are frozen in PRODUCT.md as a historical record.


Adding an org governance signal — demo fixture rule

Any PR that adds a new governance panel or signal (i.e. a new /api/org/ route that feeds OrgFixture.governance in app/demo/organization/page.tsx) must do all three of the following in the same PR:

  1. Add the new field to the governance object in scripts/generate-demo-fixtures.ts.
  2. Regenerate the fixtures with npm run demo:fixtures and commit the updated fixtures/demo/org-ossf.json.
  3. Confirm npm run demo:check-parity exits 0.

Why: hand-authoring values in fixtures/demo/ violates the accuracy policy (constitution §II). Issue #385 documented a real incident where memberPermission was committed with fabricated counts that were only caught during manual review. The CI workflow .github/workflows/demo-fixture-parity.yml enforces generator parity automatically on every PR that touches governance-related files.


Adding a new scoring signal — integration checklist

There is no central signal registry. When adding a new signal to an existing lens (e.g. community, licensing, docs), update every touchpoint below or the signal will be invisible on one of the surfaces:


Notes