EDPA Setup — V2 Project Initialization
What this does
Bootstraps a local-first EDPA project. Everything EDPA-related lives
under .edpa/; the engine is vendored as .edpa/engine/ so CI and
non-Claude-Code tools can run scripts directly without a per-run
install.
Resulting layout:
<project>/
├── .edpa/
│ ├── engine/ ← vendored from ${CLAUDE_PLUGIN_ROOT}/edpa/
│ │ ├── scripts/ ← Python: backlog.py, engine.py, mcp_server.py, …
│ │ ├── schemas/ ← JSON Schemas
│ │ └── templates/ ← yaml + workflow templates
│ ├── config/
│ │ ├── edpa.yaml ← project.name + governance metadata
│ │ ├── people.yaml ← team registry
│ │ └── id_counters.yaml ← local-first ID allocator state
│ ├── backlog/ ← per-item .md files
│ │ ├── initiatives/ epics/ features/ stories/ defects/ events/ risks/
│ ├── iterations/ ← per-iteration .yaml
│ ├── reports/ ← timesheet + engine results
│ └── snapshots/ ← frozen audit snapshots
└── .github/workflows/
└── edpa-contribution-sync.yml ← PR signal materialization (optional)
Arguments
$ARGUMENTS — optional flags:
--with-ci— copyedpa-contribution-sync.ymlto.github/workflows/. Optional enhancement that materialises pr_reviewer / issue_comment signals (PR-thread-only events) from GitHub Actions intoevidence[]. Local commit_author signals flow regardless via--with-hooks.--with-hooks— install the full git-hook stack into.git/hooks/:- pre-commit: ID-safety validator (filename≡frontmatter id, counter monotonicity, HEAD collisions) — Layer 5 of collision defense
- commit-msg: require EDPA item reference in commit subject/body
(or
chore(no-ticket):escape) — catches "did work, forgot to attribute" - post-commit:
local_evidence.pyemits commit_author and/contributesignals into the referenced item'sevidence[] - pre-push: upstream ID collision check (
validate_ids.py --pre-push) — Layer 6 of collision defense; blocks push when local ID already exists onorigin/main. Recover viarenumber_collisions.py --apply. Full guide:docs/dev-collisions.md. - NOTE: Layer 7 (CI workflow
edpa-collision-check.yml) is a separate manual step — copy from.edpa/engine/templates/github-workflows/to.github/workflows/after running setup. - Registration is robust + lefthook-aware:
- Under lefthook (
lefthook.ymlpresent, which owns.git/hooks/), EDPA does not write.git/hooks/. It instead wires one line into your lefthook config —extends: [.edpa/engine/lefthook-edpa.yml]— and that entry is the only thing it ever changes there: nothing removed, reordered or reformatted, and a stale hand-pasted EDPA block is left in place on purpose (the fragment wins the name collision, with identicalrun:lines). Runlefthook installafterwards. Because the fragment is re-vendored every session, hook fixes then arrive with/plugin updateinstead of needing a re-paste — repos still on a pasted block are migrated ontoextends:automatically at the next update. Alefthook.toml/.jsoncannot be line-edited safely, so those get the full paste-in snippet printed instead (withuse_stdin: trueon pre-push, or the push hangs). - A foreign hook already occupying a slot is never clobbered — EDPA skips it and prints the exact line to chain itself in by hand.
- Idempotent + self-refreshing: re-running
--with-hooks(or--refresh-hooks) refreshes EDPA-owned hooks to the current version; the SessionStart auto-update re-registers them after a plugin update. - Verify:
python3 .edpa/engine/scripts/project_setup.py --check-hooks(read-only doctor — active / missing / foreign / lefthook).
- Under lefthook (
--with-rules— copyplugin/rules/*.mdto.claude/rules/. Claude Code auto-loads these into every agent session, so AI assistants in this repo follow the same ticket-first workflow as humans.
All three flags are recommended for any team workflow.
Steps
1. Run the bootstrap script
python3 ${CLAUDE_PLUGIN_ROOT}/edpa/scripts/project_setup.py \
--with-ci --with-hooks --with-rules
The script first vendors the engine (scripts + schemas +
templates + VERSION) into .edpa/engine/ from
${CLAUDE_PLUGIN_ROOT}, then seeds the configs + id_counters.yaml. It
is idempotent — safe to re-run when adding hooks/CI after the initial
setup; re-running also refreshes EDPA-owned git hooks to the current
version (see --check-hooks to verify, --refresh-hooks to register only).
2. Edit the seeded configs
.edpa/config/edpa.yaml— setproject.name,project.registration(optional grant ID),project.organization,project.program..edpa/config/people.yaml— replace the example team with your members. Each entry:{id, name, role, team, fte, capacity}.
3. Create the first item
python3 .edpa/engine/scripts/backlog.py add \
--type Initiative --title "Project Apollo"
4. (Optional) Enable PR signal materialization later
If you skipped --with-ci initially:
python3 ${CLAUDE_PLUGIN_ROOT}/edpa/scripts/project_setup.py --with-ci
Then push the new workflow to main. After merge of any PR
referencing an EDPA item, the workflow runs sync_pr_contributions.py
which writes evidence[] into the item's YAML. The engine reads this
on the next /edpa:engine run.
Migrating from V2.0 (where the block was named ci_signals[]):
python3 .edpa/engine/scripts/migrate_evidence_rename.py
(idempotent; --dry-run for preview)
Tuning weights — .edpa/config/cw_heuristics.yaml
project_setup.py seeds this file with sensible defaults. Three
sections matter:
signals:— per-signal-type weight (commit_author,pr_reviewer,issue_comment, …). Higher = more influence on a person'scwshare for an item. The seeded file itself carries the current calibrated defaults (plus the calibration metadata that produced them). Recalibrate via/edpa:autocalibafter collecting ≥20 ground-truth records.gate_weights:— fires when a Feature/Epic/Initiative status transitions inside an iteration window. Splits the parent'sjob_sizeacross its lifecycle (Funnel→Analyzing…Releasing→Done) so prep + delivery + acceptance work all gets credited. Stories flow through the regular Done-only path; they don't generate gate events.yaml_edit_weights:— credits structural changes to.edpa/backlog/*.mdYAML (new block added, list item added, scalar changed, …). Captures work that doesn't go through a commit attribution flow.
When gate transitions fire (multi-iteration Feature/Epic work),
strategic roles (PM, Arch, BO) get credit via gate events that would
otherwise be invisible. For Story-only sprints, gate_weights is dead
weight — engine still works, just produces empty gate_events: [].
Edit + commit cw_heuristics.yaml like any other YAML in .edpa/;
no special migration needed.
What NOT to do
- Never modify
.edpa/engine/by hand. It's a vendored copy of the plugin; the SessionStart hook auto-resyncs when the plugin version changes. Hand edits will be silently overwritten. - Never set
EDPA_USE_GH=1in production. It's a local-debug escape hatch that re-enables runtime gh calls in the engine — bypassing the deterministic CI materialization layer. Used in production it creates the V1 drift problem all over again. - Don't create
.github/ISSUE_TEMPLATE/files. V2 doesn't use GitHub Issues for item creation — items are created locally viabacklog.py addand synced into git, not into GH.
V1 migration
If you're coming from EDPA V1 (sync.py / issue_map.yaml in place), run the migration script first:
python3 .edpa/engine/scripts/migrate_v1_to_v2.py --dry-run # preview
python3 .edpa/engine/scripts/migrate_v1_to_v2.py # apply
It seeds the counter from existing IDs, backfills timestamps from git
log, archives issue_map.yaml, and strips the sync: block from
edpa.yaml. After migration, run project_setup.py --with-ci --with-hooks to opt into the CI workflow + git hooks (verify with
--check-hooks).