Prerequisites

Tools

ToolMinimum versionCheck
Python3.10+python3 --version
pyyaml + openpyxl + ruamel.yamlanypip install pyyaml openpyxl ruamel.yaml
Git2.30+git --version
GitHub CLI (gh)2.40+gh --versionoptional

EDPA V2 runs purely locally on top of git. gh is needed only if you want the optional PR-signal sync (the edpa-contribution-sync.yml workflow) — see 2.3 Optional GitHub PR-signal sync.

Optional: GitHub CLI scopes for PR-signal sync

If you enable the optional PR-signal sync, the ordinary repo scope is enough:

gh auth login        # ordinary repo scope is enough
gh auth status
Org-level scopes (admin:org, project) from EDPA V1 are no longer needed — GitHub Projects and org Issue Types were removed in 2.0.0.

Team and backlog

Before you start, you need:

  • A defined team: names, roles (Arch, Dev, DevSecOps, PM, QA, BO), FTE, capacities
  • At least a basic backlog (1 Epic, 2-3 Features, 5-10 Stories)
  • A Git repository (a GitHub remote is optional)

role: in people.yaml is a free-form label used for reports (timesheet column, Excel exports) and as input to the synthetic calibration corpus. Engine signal weights apply uniformly regardless of a person's role — commit_author=4.00, pr_reviewer=2.17, issue_comment=1.46, etc. are the same for everyone.

Phase 1: Infrastructure (Day 1, ~1 hour)

Path A: Claude Code (recommended)

In a terminal with Claude Code installed:

/edpa:setup --with-ci --with-hooks --with-rules

Claude Code (the /edpa:setup skill) runs steps 1.1-1.4 automatically — it vendors the engine into .edpa/engine/, seeds the configuration and id_counters.yaml, and optionally installs git hooks, the PR-signal CI workflow, and .claude/rules/. Idempotent — re-running breaks nothing.

Path B: Manual CLI

The steps below describe the manual procedure without Claude Code.

1.1 Install EDPA and vendor the engine

Variant A: Shell installer (recommended)

cd my-project
curl -fsSL https://edpa.technomaton.com/install.sh | sh

Variant B: project_setup.py

project_setup.py vendors the engine + seeds the configs + id_counters.yaml. Extra flags install git hooks, the contribution-sync CI workflow, and .claude/rules/. No --org/--repo — no GitHub provisioning.

python3 .edpa/engine/scripts/project_setup.py --with-ci --with-hooks --with-rules

Resulting structure:

my-project/
  .edpa/
    config/
      edpa.yaml          # Project + governance (source of methodology version)
      people.yaml        # Team and capacities (teams, people)
      cw_heuristics.yaml # CW signal weights
      id_counters.yaml   # ID counters (S-1, E-1, F-1, ...)
    backlog/
      initiatives/       # 1 file = 1 initiative (.md with YAML frontmatter)
      epics/             # 1 file = 1 epic
      features/          # 1 file = 1 feature
      stories/           # 1 file = 1 story
    iterations/          # PI and iterations (PI-2026-1.yaml, PI-2026-1.1.yaml, ...)
    reports/             # Generated reports + edpa_results.json per iteration
    snapshots/           # Frozen iteration snapshots
    data/                # Ground truth for calibration (post-PI)
    engine/              # VENDORED engine (installed by project_setup.py)
      VERSION
      scripts/
        engine.py            # EDPA computational core
        backlog.py           # Backlog management (add, tree, show, wsjf, validate)
        detect_contributors.py  # evidence[] -> contributors[] (cw)
        calibrate_signals.py    # Signal-weights calibration (MAD)
        reports.py           # Timesheets + PI summary + xlsx
        board.py             # Local HTML Kanban board
        capacity_override.py # Per-person/iteration capacity override
        sync_pr_contributions.py  # Materialize PR-thread signals (optional CI)
        ...                  # other helper scripts + hooks/
      templates/
        people.yaml.tmpl
        cw_heuristics.yaml.tmpl
        edpa.yaml.tmpl
  .github/workflows/         # ONLY with --with-ci
    edpa-contribution-sync.yml  # After PR merge: PR-thread signals -> evidence[]
  .claude/rules/             # ONLY with --with-rules (architectural rules)
  docs/                      # Documentation
V2 paths: the user-facing engine is vendored under .edpa/engine/scripts/*.py (NOT .claude/edpa/scripts/, NOT plugin/edpa/scripts/). Templates live in .edpa/engine/templates/.

1.2 Configure the team and capacities (people.yaml)

project_setup.py already seeds .edpa/config/people.yaml from the template. Or manually:

cp .edpa/engine/templates/people.yaml.tmpl .edpa/config/people.yaml

Edit .edpa/config/people.yaml. This is where teams + people live — no separate registry. Set cadence (iteration_weeks / pi_iterations) per-PI in the pi: block of .edpa/iterations/PI-*.yaml:

teams:
  - id: "My Team"
    planning_factor: 0.8               # Plan to 80% of capacity

people:
  - id: alice
    name: "Alice Smith"
    role: Arch                          # Arch, Dev, DevSecOps, PM, QA, BO
    team: "My Team"
    fte: 0.5
    capacity_per_iteration: 20          # hours: FTE x 40 for a 1-week iter.
                                        # (40 for a 2-week iter.)
    email: "alice@example.com"
    availability: confirmed             # confirmed, partial, unavailable

  - id: bob
    name: "Bob Johnson"
    role: Dev
    team: "My Team"
    fte: 1.0
    capacity_per_iteration: 40          # FTE x 40 for a 1-week iter.
    email: "bob@example.com"
    availability: confirmed

  # Add more team members...
Important: planning_factor is a planning heuristic (plan to 80%). The EDPA engine always calculates against 100% of capacity — the buffer absorbs unplanned work. A person's github: field is optional — it is used only by the optional PR-signal sync to map a GH login to people[].id.
V2 note: there is no separate registry/capacity file. The whole team and its capacities live in people.yaml.
Per-iteration capacity: the baseline is capacity_per_iteration in people.yaml. To adjust it for a single iteration (PTO, overtime, ramp) use the /edpa:capacity <iteration> --add --person <id> --hours 20|+8|-12 --note "…" command (also --list / --remove). The override is stored in the iteration YAML people: block and shows in the report as capacity / capacity_baseline / capacity_override. Closed iterations reject overrides — set them before closing (Stage 1 of /edpa:close-iteration).

1.3 Configure the project + governance (edpa.yaml)

project_setup.py seeds .edpa/config/edpa.yaml from the template (NOT project.yaml — that does not exist in V2). Or manually:

cp .edpa/engine/templates/edpa.yaml.tmpl .edpa/config/edpa.yaml

Edit .edpa/config/edpa.yaml:

project:
  name: "My Project"                    # ← Display name (the only required field)
  description: ""
  domain: "myproject.com"
  # Optional: funding (grants) + organizations (for audit/billing)

governance:
  # Auto-stamped to the plugin version by the installer.
  methodology: "EDPA v2.11.1"
  # Single computation path since v1.14 (no simple/full/gates mode selector,
  # no audit_mode — snapshots always carry the full signals[] audit trail).

naming:
  pi_pattern: "PI-{year}-{pi_num}"                    # PI-2026-1
  iteration_pattern: "PI-{year}-{pi_num}.{iter_num}"  # PI-2026-1.3
  branch_pattern: "{type}/{item_id}-{description}"    # feature/S-200-omop-parser
  item_prefixes:
    initiative: "I"
    epic: "E"
    feature: "F"
    story: "S"
    task: "T"
    defect: "D"
    event: "EV"
Removed in 2.0.0: the calculation_mode (simple/full) and audit_mode fields. The engine has a single computation path (JS × CW) and snapshots always carry the full audit trail.

1.4 Configure CW signal weights (cw_heuristics.yaml)

project_setup.py seeds .edpa/config/cw_heuristics.yaml (NOT heuristics.yaml). Or manually:

cp .edpa/engine/templates/cw_heuristics.yaml.tmpl .edpa/config/cw_heuristics.yaml

Default weights are calibrated by Monte Carlo simulation. For most teams the defaults are fine to start with. Calibration against your own data is done after the first PI (see Phase 3).

EDPA V2 is evidence-driven: cw[person, item] = contribution_score / Σ contribution_score, where contribution_score = Σ signal_weight. The key default signal weights:

SignalWeightMeaning
commit_author4.00Commit with the ID in branch, title, or message
pr_reviewer2.17PR review submitted (excluding self)
issue_comment1.46Issue/PR comment (excluding bots)

Contributor role weights (--contributor PERSON:ROLE:CW) — owner 1.0 / key 0.6 / reviewer 0.25 / consulted 0.15; evidence_threshold 1.0. cw_heuristics.yaml additionally carries gate_weights for Feature/Epic/Initiative — a status transition on the parent distributes its Job Size across the lifecycle (sum = 1.0 per type).

1.5 Configure iterations (iterations/)

PI and iterations live in .edpa/iterations/ as separate files:

# .edpa/iterations/PI-2026-1.yaml — PI-level metadata
pi:
  id: PI-2026-1
  status: active
  iteration_weeks: 1                      # AI-native default
  pi_iterations: 5                         # 4 delivery + 1 IP
  start_date: 2026-04-01
  end_date: 2026-05-05
# .edpa/iterations/PI-2026-1.1.yaml — per-iteration data
iteration:
  id: PI-2026-1.1
  pi: PI-2026-1
  start_date: 2026-04-01
  end_date: 2026-04-07
  weeks: 1
  status: planned
# planning / delivery / stories_detail follow as the iteration runs

Create PI-2026-1.2.yaml through PI-2026-1.5.yaml the same way; the last gets type: IP (Innovation & Planning). Continuity (no gaps / overlaps, weeks × 7 ≈ date span) is enforced by validate_iterations.py and the automatic PostToolUse hook.

Removed in 2.0.0: the sync block (github_org, github_project_number, sync_interval) — V2 is local-first, no GitHub Project is provisioned.

1.6 Populate the backlog

EDPA uses a file-per-item structure — every work item is a separate .md file with YAML frontmatter (in V2 NOT .yaml):

.edpa/
  config/
    edpa.yaml                    # project + governance
    people.yaml                  # team and capacities
    cw_heuristics.yaml           # CW signal weights
    id_counters.yaml             # ID counters
  backlog/
    initiatives/
      I-1.md                     # 1 file = 1 initiative
    epics/
      E-1.md                     # 1 file = 1 epic
    features/
      F-1.md                     # 1 file = 1 feature
    stories/
      S-1.md                     # 1 file = 1 story
  iterations/
    PI-2026-1.1.yaml             # iteration plan

Example story file (.edpa/backlog/stories/S-1.md — YAML frontmatter + optional body):

---
id: S-1
type: Story
title: "Parser implementation"
status: Backlog
parent: F-1
js: 5
assignee: bob
iteration: PI-2026-1.1
---
Optional description / acceptance criteria in Markdown.

Adding a new item (LOCAL — no GitHub calls):

# Claude Code:
/edpa:add    # interactively adds one item (Initiative/Epic/Feature/Story/Defect/Event/Risk)

# CLI:
python3 .edpa/engine/scripts/backlog.py add --type Story --parent F-1 \
  --title "..." --js 5 --assignee bob --iteration PI-2026-1.1
python3 .edpa/engine/scripts/backlog.py add --type Epic --parent I-1 \
  --title "..." --js 13 --bv 13 --tc 8 --rr-oe 5

backlog.py add is purely local in V2 (no gh): the ID is allocated from id_counters.yaml (S-42, E-15, I-3), the parent hierarchy is validated, the YAML is written under .edpa/backlog/, and the change is auto-committed as feat(<ID>):. PR-derived signals arrive later via the optional contribution-sync workflow.

Removed in 2.0.0: the "GH-first" mode of add (creating a GH issue, sub-issue API, rewriting the title to "{ID}: {title}"). In V2 add is local-only — the V1 "strictly GH-first" claim NO LONGER holds.

Contributors can be assigned right at creation time (per-item CW share):

python3 .edpa/engine/scripts/backlog.py add --type Story --parent F-1 --title "..." --js 5 \
  --contributor bob:owner:0.7 --contributor carol:reviewer:0.3
# Format PERSON:ROLE:CW, ROLE ∈ {owner,key,reviewer,consulted}, CW ∈ [0,1]

Verify integrity:

python3 .edpa/engine/scripts/backlog.py validate

Show the hierarchy:

python3 .edpa/engine/scripts/backlog.py tree
python3 .edpa/engine/scripts/backlog.py tree --level epic

Show WSJF prioritization:

python3 .edpa/engine/scripts/backlog.py wsjf
python3 .edpa/engine/scripts/backlog.py wsjf --level feature

Show item detail:

python3 .edpa/engine/scripts/backlog.py show S-1
python3 .edpa/engine/scripts/backlog.py show E-1

1.7 Verify setup

# Demo EDPA engine (no real data)
python3 .edpa/engine/scripts/engine.py --demo

# Backlog validation
python3 .edpa/engine/scripts/backlog.py validate

# Project status
python3 .edpa/engine/scripts/backlog.py status

# Visual HTML board (local-first replacement for the Projects board)
python3 .edpa/engine/scripts/board.py --output .edpa/board.html

1.8 Commit the base configuration

backlog.py add commits items automatically. Commit the initial config and the vendored engine:

git add .edpa/
git commit -m "feat(edpa): initial EDPA V2 setup for my-project"
git push origin main   # optional, if you have a GitHub remote
Removed in 2.0.0: §1.6 Issue Types (issue_types.py setup --org), §1.8 GitHub Project (project_setup.py --org/--repo/--project-title, custom fields, sub-issues), §1.9 Project views (project_views.py, create_project_views.py, Playwright). V2 replaces the board view with the local board.py / /edpa:board.

Phase 2: First iteration (Week 1)

2.1 Iteration Planning

  1. Capacity confirmation — team confirms availability for the iteration
  2. Story selection — from the backlog by WSJF order, up to 80% of capacity
# WSJF ranking features
python3 .edpa/engine/scripts/backlog.py wsjf --level feature

# Tree for a specific iteration
python3 .edpa/engine/scripts/backlog.py tree --iteration PI-2026-1.1
  1. Assignee allocation — every story must have an assignee
  2. Backlog updateiteration: PI-2026-1.1 on the selected stories

2.2 Daily work

The branch-naming convention {type}/{ITEM}-{description} is in V2 enforced by git hooks (--with-hooks), no longer by CI:

# Format: {type}/{ITEM_ID}-{description}
git checkout -b feature/S-200-omop-parser
git checkout -b feature/F-102-anon-engine
git checkout -b bugfix/S-215-upload-validation
git checkout -b chore/T-050-ci-pipeline

Allowed types: feature, bugfix, hotfix, chore

Allowed prefixes: S (Story), F (Feature), E (Epic), T (Task), D (Defect), I (Initiative), EV (Event)

Git hooks (--with-hooks) materialize evidence locally, without GitHub:

HookWhat it does
pre-commitID safety — reference check
commit-msgRequires an item reference or no-ticket:
post-commitMaterializes commit_author, yaml_edit and state_transition into evidence[] (local_evidence.py)
pre-pushChecks for ID collisions
Materializing evidence: the automatic post-commit hook writes signals after every commit. To idempotently backfill history (dedup by ref) run /edpa:materialize (the MCP tool edpa_materialize) or the CLI python3 .edpa/engine/scripts/local_evidence.py --materialize [--iteration PI-2026-1.1 | --all-iterations]. The EDPA_NO_LOCAL_EVIDENCE=1 variable disables only the automatic hook — --materialize ignores it.

Hook registration is idempotent and self-refreshing (the SessionStart auto-update re-registers them after a plugin update). Foreign (non-EDPA) hooks are never clobbered — they are skipped with a warning. Check hook state (active / missing / foreign / lefthook) anytime with the read-only doctor:

python3 .edpa/engine/scripts/project_setup.py --check-hooks
python3 .edpa/engine/scripts/project_setup.py --refresh-hooks   # register only
lefthook: if the project uses lefthook.yml (lefthook owns .git/hooks/), EDPA does not write .git/hooks/ — instead it prints a paste-ready snippet to add to lefthook.yml, then run lefthook install. Note: pre-push must have use_stdin: true or lefthook hangs.
pre-commit:
  commands:
    edpa-id-safety:
      run: sh .edpa/engine/scripts/hooks/pre-commit-id-safety
commit-msg:
  commands:
    edpa-ticket-attached:
      run: sh .edpa/engine/scripts/hooks/commit-msg-ticket-attached {1}
post-commit:
  commands:
    edpa-evidence:
      run: sh .edpa/engine/scripts/hooks/post-commit-evidence
pre-push:
  commands:
    edpa-id-safety:
      run: sh .edpa/engine/scripts/hooks/pre-push-id-safety {1} {2}
      use_stdin: true

Commit convention:

git commit -m "feat(S-200): implement OMOP CDM parser"
git commit -m "fix(S-215): validate upload file size"
git commit -m "test(S-201): add unit tests for parser"
git commit -m "docs(E-10): update epic hypothesis"

The EDPA engine recognizes S-XXX, F-XXX, E-XXX references in commits (and optionally in PRs) as evidence.

Pull Request workflow (optional, only with a GitHub remote):

git push origin feature/S-200-omop-parser
gh pr create --title "S-200: OMOP CDM parser implementation" \
  --body "Closes #42

## Changes
- Implemented OMOP CDM parser
- Added schema validation

## Testing
- Unit tests: 15 passing
"

PR review = a signal for EDPA. For PR-thread signals (pr_reviewer, issue_comment) to land in evidence[], the optional contribution-sync workflow is required — see 2.3.

2.3 Optional GitHub PR-signal sync

EDPA V2 works purely locally — the post-commit hook and /edpa:materialize write commit_author, yaml_edit and state_transition into evidence[]; the engine is a pure reader. GitHub is optional and serves only to materialize PR-thread signals.

With --with-ci, the single V2 workflow .github/workflows/edpa-contribution-sync.yml is installed. After a PR merge it runs sync_pr_contributions.py, which extracts signals (pr_reviewer, issue_comment) from the PR thread and writes them into the relevant items' evidence[]. It requires the EDPA_TOKEN secret (see docs/edpa-token-setup.md). Flow metrics can be read via the MCP tool edpa_flow_metrics.

Removed in 2.0.0: bidirectional sync sync.py (pull/push/diff/status/conflicts/--mock) — it does not exist in V2. Likewise the edpa-sync-projects-to-git.yml and edpa-sync-git-to-projects.yml workflows. No GitHub Project is propagated. For a board, use the local /edpa:board.

2.4 Iteration Close

At the end of each iteration (1 week AI-native / 2 weeks classic):

Claude Code (recommended):

/edpa:close-iteration PI-2026-1.1

Claude Code (the close-iteration skill) prepares capacity, runs the EDPA engine, and generates the reports automatically.

Manual CLI:

python3 .edpa/engine/scripts/engine.py --edpa-root .edpa --iteration PI-2026-1.1
# optional: --output path/edpa_results.json

Outputs:

.edpa/reports/iteration-PI-2026-1.1/
  edpa_results.json        # Complete calculation (JSON)
  timesheet-alice.md       # Timesheet per person (DerivedHours > 0)
  timesheet-team.md        # Aggregated team rollup
  edpa-results.xlsx        # Team Summary + Item Costs (Excel)
.edpa/snapshots/
  PI-2026-1.1.json         # Frozen snapshot (audit trail)
Note: per-person files are timesheet-<id>.md (in V2 NO LONGER vykaz-*.md).

Calculation (the single path since v1.14): score = JS × CW, hours = (score / Σ score) × capacity. No --mode simple|full and no Role Strength.

Score        = JobSize × CW
DerivedHours = (Score / ΣScores) × Capacity

CW is a per-item normalized share (Σ across persons on an item = 1.0), computed in detect_contributors.py from evidence (commit author, PR reviewer, issue comment, /contribute directives). The engine consumes CW verbatim.

Invariants (engine checks automatically):

  • Sum of a person's DerivedHours == their capacity (exactly)
  • Sum of ratios == 1.0
  • No negative hours
  • If an invariant fails, the engine reports FAIL

2.5 Update the iteration plan

After closing an iteration, create or update .edpa/iterations/PI-2026-1.1.yaml:

iteration:
  id: PI-2026-1.1
  pi: PI-2026-1
  start_date: 2026-04-01
  end_date: 2026-04-14
  weeks: 2
  status: closed

planning:
  capacity: 380
  planning_factor: 0.80
  planned_sp: 24
  stories:
    - S-200
    - S-201
    - S-202

delivery:
  delivered_sp: 24
  predictability: "100%"
  velocity: 24
  spillover: []
  unplanned: []
  notes: "First iteration -- full delivery."

Phase 3: PI Close (after 4-5 iterations)

Path A: Claude Code (recommended)

/edpa:calibrate

Claude Code (the /edpa:autocalib skill) runs calibration of the signal weights via a Monte Carlo + coordinate-descent optimizer, evaluates MAD, and proposes adjustments to cw_heuristics.yaml. To generate reports:

/edpa:reports PI-2026-1

Path B: Manual CLI

3.1 Retrospective

At the end of a PI (after 4 delivery iterations + 1 IP iteration):

  1. Walk through auto-detection vs. reality on 5-10 stories
  2. For each story compare: who was auto-detected, what CW the engine assigned, does it match reality?

3.2 Ground truth for calibration (optional)

The calibrator runs against a Monte Carlo synthetic corpus and searches for MAD-optimal signal weights in .edpa/config/cw_heuristics.yaml. A ground_truth.yaml is not required — you can run it anytime, even before the first PI close.

If you want to calibrate against real records from a PI retro, write team-confirmed CW corrections into .edpa/data/ground_truth.yaml:

records:
  - person: alice          # id from .edpa/config/people.yaml
    item: S-200
    iteration: PI-2026-1.1
    confirmed_cw: 0.35     # PM/retro-confirmed share (0..1)
    notes: "design review + arch. decisions"

  - person: bob
    item: S-200
    iteration: PI-2026-1.1
    confirmed_cw: 0.65
    notes: "implementation + tests"

  # ... at least 20 records for reliable calibration

3.3 Calibrating CW signals

Claude Code (recommended):

/edpa:calibrate

Manual CLI:

python3 .edpa/engine/scripts/calibrate_signals.py \
  --ground-truth .edpa/data/ground_truth.yaml \
  --heuristics .edpa/config/cw_heuristics.yaml

calibrate_signals.py (the /edpa:calibrate skill) runs in two phases: a random Monte Carlo sample across the signal weights → coordinate-descent (Nelder-Mead) refinement around the best candidates. The metric is MAD on a synthetic corpus; lower MAD = better calibration.

Output (illustrative):

MAD=0.041200
RECORDS=20
TOTAL_DEVIATION=0.824000

MAD interpretation (Mean Absolute Deviation):

MADRatingAction
< 0.03ExcellentNo change
0.03 - 0.06GoodMinor corrections (optional)
0.06 - 0.10AcceptableConsider recalibrating signal weights
> 0.10PoorCalibration required

Correcting signal weights:

If MAD > 0.06, analyze where the largest deviations are:

  1. Look at records where abs(auto_cw - confirmed_cw) is highest
  2. Identify patterns by role (typically BO, PM, Arch — strategic roles are under-credited by git)
  3. Adjust the signals: weights in .edpa/config/cw_heuristics.yaml
  4. Re-run calibration
Removed in 2.0.0: evaluate_cw.py (replaced by calibrate_signals.py / /edpa:calibrate) and the heuristics.yaml file (replaced by cw_heuristics.yaml).

3.4 Planning the next PI

  1. New epics/features — add via backlog.py add (auto-commit) into .edpa/backlog/
  2. WSJF prioritization: python3 .edpa/engine/scripts/backlog.py wsjf
  3. Capacity planning — update .edpa/config/people.yaml (FTE changes, availability)
  4. Create new iteration files in .edpa/iterations/ for the new PI

Phase 4: Continuous operation

With Claude Code (recommended)

Every iteration:

/edpa:close-iteration PI-2026-1.X    # close the iteration
/edpa:reports PI-2026-1.X             # generate reports

Every PI:

/edpa:close-pi PI-2026-1               # close the PI: guard iterations, status, rollup
/edpa:calibrate                       # calibrate signal weights

Each iteration (1 week AI-native / 2 weeks classic)

  1. Planning — pick stories, assign assignees
  2. Daily work — branch naming, commits with references, (optional) PR review
  3. Iteration Close — EDPA engine, report generation
  4. Review — team checks the timesheets, reports corrections (manual CW override via --contributor / /contribute)

Each PI (5 weeks AI-native / 10 weeks classic)

  1. Retrospective — auto-detected CW vs. reality
  2. Ground truth — record at least 20 new entries
  3. CW calibrationcalibrate_signals.py, evaluate MAD
  4. Velocity trend — compare delivery across iterations
  5. Predictability — (delivered_sp / planned_sp) across iterations
# Status for a whole iteration
python3 .edpa/engine/scripts/backlog.py status --iteration PI-2026-1.3

# Overall project status
python3 .edpa/engine/scripts/backlog.py status

# Capacity override (e.g. extra hours outside evidence)
python3 .edpa/engine/scripts/capacity_override.py PI-2026-1.1 --add --person bob --hours 12

# Velocity / flow reports
python3 .edpa/engine/scripts/reports.py PI-2026-1.1

Optional automation via GitHub Actions

V2 has a single optional workflow (only with --with-ci):

WorkflowTriggerWhat it does
edpa-contribution-sync.ymlafter PR mergesync_pr_contributions.py materializes PR-thread signals (pr_reviewer, issue_comment) into evidence[]
Removed in 2.0.0: edpa-branch-check.yml, edpa-iteration-close.yml, edpa-sync-projects-to-git.yml, edpa-sync-git-to-projects.yml. The branch convention is now guarded by a local git hook, iteration close runs locally, and bidirectional sync does not exist.

Checklist — What to have ready

Day 1

  • Engine vendored into .edpa/engine/ (/edpa:setup or project_setup.py)
  • .edpa/config/people.yaml — team with roles, FTE, capacities (teams + people)
  • .edpa/config/edpa.yaml — project name, governance, naming
  • .edpa/config/cw_heuristics.yaml — default signal weights (from the template)
  • .edpa/config/id_counters.yaml — seeded
  • .edpa/iterations/ — PI + iterations with dates
  • Backlog populated via backlog.py add (at least 1 Epic, 3 Features, 10 Stories)
  • backlog.py validate passes without errors
  • engine.py --demo runs successfully
  • (optional) git hooks installed (--with-hooks), contribution-sync CI (--with-ci)

Week 1

  • Team works with the branch-naming convention (feature/S-XXX-description)
  • Commits reference work items (feat(S-XXX): ...)
  • post-commit hook records commit_author evidence
  • (optional) PR reviews are happening and contribution-sync materializes them

End of iteration 1

  • EDPA engine run for the iteration
  • edpa_results.json generated in .edpa/reports/iteration-<ID>/
  • Timesheets timesheet-<id>.md generated (per-person)
  • All invariants passed (all_invariants_passed: true)
  • Snapshot frozen in .edpa/snapshots/
  • Team reviewed the results

End of iterations 2-4

  • Velocity stable (deviation < 20%)
  • Predictability > 80% (delivered / planned)

End of PI 1

  • Ground truth recorded (min. 20 entries)
  • CW calibration completed (calibrate_signals.py / /edpa:calibrate)
  • MAD evaluated (target: < 0.06)
  • Signal weights adjusted in cw_heuristics.yaml (if MAD > 0.06)
  • PI 2 planning — new epics, WSJF, capacities
  • New iteration files in .edpa/iterations/ for the new PI

CLI Reference

All scripts run from the vendored engine: python3 .edpa/engine/scripts/<script>.py.

Claude Code commands (recommended)

CommandDescription
/edpa:setup --with-ci --with-hooks --with-rulesVendors the engine, seeds configs + id_counters, optional hooks/CI/rules
/edpa:addAdd a backlog item (local, auto-commit)
/edpa:close-iteration PI-2026-1.XClose an iteration — capacity, EDPA engine, reports
/edpa:reports PI-2026-1.XGenerate timesheets and PI summary
/edpa:close-pi PI-2026-1Close a PI — guard iterations, flip pi.status, rollup report
/edpa:boardVisual HTML Kanban board (local)
/edpa:calibrateCalibrate signal weights — MAD, propose cw_heuristics.yaml adjustments

engine.py — EDPA computational core

CommandDescription
engine.py --demoDemo with built-in sample data
engine.py --edpa-root .edpa --iteration PI-2026-1.3Full EDPA calculation for an iteration (reads backlog/config/heuristics from .edpa)
engine.py --edpa-root .edpa --iteration ID --output path/edpa_results.jsonCustom output path
engine.py --statusConfiguration state

backlog.py — Backlog management

CommandDescription
backlog.py add --type Story --parent F-1 --title "..." --js 5 --assignee bob --iteration PI-2026-1.1Add an item locally (ID from id_counters.yaml, auto-commit feat(<ID>):)
backlog.py add --type Epic --parent I-1 --title "..." --js 13 --bv 13 --tc 8 --rr-oe 5Add an Epic with WSJF metrics
backlog.py add ... --contributor PERSON:ROLE:CWAdd a contributor (owner/key/reviewer/consulted, CW ∈ [0,1])
backlog.py treeShow the full hierarchy (I → E → F → S)
backlog.py tree --level epic|feature|storyFilter by level
backlog.py tree --iteration PI-2026-1.1Filter stories by iteration
backlog.py show S-1Item detail
backlog.py status [--iteration PI-2026-1.1]Project / iteration status
backlog.py wsjf [--level feature]WSJF prioritization
backlog.py validateBacklog integrity check

reports.py — Timesheets and PI summary

CommandDescription
reports.py PI-2026-1.1Per-person timesheet-<id>.md + timesheet-team.md + xlsx
reports.py --pi PI-2026-1Aggregate all iterations under a PI
reports.py PI-2026-1.1 --out path/Custom output directory

board.py — Local HTML board

CommandDescription
board.py --output .edpa/board.htmlGenerate a Kanban board from .edpa/backlog/
board.py --iteration PI-2026-1.4 --openFilter by iteration + open in the browser

capacity_override.py — Capacity override

CommandDescription
capacity_override.py PI-2026-1.1 --listList existing overrides
capacity_override.py PI-2026-1.1 --add --person bob --hours 12Add an override
capacity_override.py PI-2026-1.1 --remove --person bobRemove an override

detect_contributors.py / calibrate_signals.py

CommandDescription
detect_contributors.pyConvert evidence[]contributors[] (cw) from real signals
calibrate_signals.py --ground-truth .edpa/data/ground_truth.yaml --heuristics .edpa/config/cw_heuristics.yamlCalibrate signal weights (MAD)

sync_pr_contributions.py (optional, CI)

CommandDescription
sync_pr_contributions.pyMaterializes PR-thread signals (pr_reviewer, issue_comment) into evidence[]. Runs edpa-contribution-sync.yml after a PR merge; requires EDPA_TOKEN.
Removed in 2.0.0: sync.py (bidirectional sync — does not exist in V2), issue_types.py (org Issue Types), project_setup.py --org/--repo/--project-title (GitHub Project provisioning — in V2 project_setup.py only vendors the engine), project_views.py + create_project_views.py (Project views / Playwright), evaluate_cw.py (replaced by calibrate_signals.py).

Architecture

V2 is local-first: .edpa/ (backlog in .md + config) is the single source of truth, git is the audit trail. GitHub is optional — the only inbound flow is the one-way materialization of PR-thread signals into evidence[].

                  .edpa/  (git) -- SINGLE SOURCE OF TRUTH
        backlog/**/*.md  +  config/  +  iterations/
                                |
              +-----------------+-----------------+
              |                 |                 |
        EDPA engine        backlog.py         board.py
        (compute)          (add/tree/wsjf)    (HTML board)
              |                                    |
        +-----+-----+                              v
        |           |                       .edpa/board.html
   reports/     snapshots/                  (local)
   timesheet-*.md  *.json
   xlsx

Data flow

   (optional) GitHub PR thread
   reviews / comments
              |
              |  edpa-contribution-sync.yml (after PR merge)
              |  sync_pr_contributions.py
              v  -- ONE-WAY: signals -> evidence[]
   post-commit hook / /edpa:materialize
   local_evidence.py --materialize  (dedup by ref)
              |  writes commit_author, yaml_edit, state_transition
              v  -- into evidence[]
   +-------------------------------------+
   |  .edpa/  (git) -- SOURCE OF TRUTH   |
   |  backlog *.md  config  iterations   |
   |  evidence[]  -- single signal source|
   +-------------------------------------+
              |
        aggregate_signals (skip weight-0)
        evidence[] -> contributors[] (cw)
              |
        +-----+-----------------------+
        |             |               |
   EDPA engine    reports.py      board.py
   (pure reader)  timesheets      HTML board
        |
   reports/ + snapshots/ + xlsx
Removed in 2.0.0: bidirectional GitHub Projects ↔ Git sync, org Issue Types, custom fields. No GitHub Project is provisioned or propagated.

Evidence detection

EDPA is evidence-driven. cw[person, item] = contribution_score / Σ_persons contribution_score, where contribution_score = Σ signal_weight. Default signal weights (cw_heuristics.yaml):

SignalWeightSource
commit_author4.00commit with the ID (local git, post-commit hook)
pr_reviewer2.17PR review submitted (excluding self)
issue_comment1.46issue/PR comment (excluding bots)

Local signals (commit_author, yaml_edit, state_transition) are materialized into evidence[] by the post-commit hook and /edpa:materialize; the engine reads them only from evidence[] (no git scan at compute time). PR-thread signals (pr_reviewer, issue_comment) arrive only via the optional contribution-sync. A manual /contribute @person weight:X (or --contributor) carries its weight verbatim.

Contributor role weights: owner 1.0 / key 0.6 / reviewer 0.25 / consulted 0.15; evidence_threshold 1.0. Signals aggregate additively into contribution_score per (person, item), then normalize per-item: cw = score / Σ_persons score (Σ across persons = 1.0).

EDPA calculation

The single computation path since v1.14 (no simple/full/gates mode). For each person in an iteration:

  1. Gather evidence on each item → contributors[] with cw
  2. For each (person, item) pair: score = JS × CW
  3. Per person: ratio_i = score_i / Σ scores
  4. Derived hours: hours_i = ratio_i × capacity
Invariant: Σ DerivedHours = capacity per person (exactly, not approximately). Feature/Epic/Initiative status transitions additionally distribute the parent's Job Size via gate_weights.

Troubleshooting

gh auth (only the optional PR-signal sync)

The EDPA V2 core does not need gh. If the optional contribution-sync workflow fails on authentication, add the EDPA_TOKEN secret (see docs/edpa-token-setup.md) or locally:

gh auth login        # ordinary repo scope is enough
gh auth status
V2 no longer needs org scopes (admin:org, project) — GitHub Projects and org Issue Types were removed in 2.0.0.

Backlog validate — errors

ERROR: S-200 references non-existent feature F-999

Fix:

  • Verify that every feature: reference in stories points to an existing feature
  • Every story must have a parent feature
  • Every feature must have a parent epic
WARNING: S-200 Job Size exceeds maximum (JS=13, max=8)

Fix:

  • Story JS max 8 (classic 2/10) or 5 (AI-native 1/5)
  • Split a large story into smaller ones

EDPA engine — invariant failure

INVARIANT FAILURE -- check results
All invariants passed: NO

Possible causes:

  • Person has no evidence (not assignee, no commits)
  • All CW values are 0 (no evidence above the threshold)
  • Capacity is 0

Fix:

  • Verify every person has at least one assigned work item
  • Verify commits/PRs reference the correct item IDs (S-XXX, F-XXX)
  • Verify .edpa/config/people.yaml — capacities > 0

Branch naming — rejected commit/push (git hook)

Branch name does not follow EDPA convention.
Required format: {type}/{item-id}-{description}

In V2 the convention is guarded by a local git hook (--with-hooks), no longer by CI.

Fix:

  • Format: feature/S-200-omop-parser, bugfix/S-215-fix-validation
  • Type: feature, bugfix, hotfix, chore
  • Prefix: S (Story), F (Feature), E (Epic), T (Task), D (Defect), EV (Event)
  • main, develop, release/* are exceptions (pass without checking)

commit-msg hook — missing item reference

commit-msg: no item reference found

Fix:

  • Add an item reference to the message: feat(S-200): ...
  • Or, for commits without a ticket, use the no-ticket: prefix

Python — missing dependencies

ERROR: pyyaml required. Install with: pip install pyyaml

Fix:

pip install pyyaml openpyxl ruamel.yaml

MAD too high (> 0.10)

Possible causes:

  • Strategic roles (BO, PM, Arch) have systematically lower auto-CW than reality
  • Git measures only commits/PRs, not decision-making, specification, mentoring

Fix:

  1. Analyze records with the largest abs(auto_cw - confirmed_cw)
  2. Group by role
  3. Adjust the signals: weights in .edpa/config/cw_heuristics.yaml (raise the weight of the signal that represents that role)
  4. Optionally add a manual /contribute @person weight:X on items where strategic work is invisible in git
  5. Re-run calibrate_signals.py

Iteration close — "config not found"

ERROR: .edpa/config/people.yaml not found. Run EDPA setup first.

Fix:

Run setup (vendors the engine + seeds the configs):

/edpa:setup
# or
python3 .edpa/engine/scripts/project_setup.py

Or copy from the templates in .edpa/engine/templates/*.tmpl:

cp .edpa/engine/templates/people.yaml.tmpl .edpa/config/people.yaml
cp .edpa/engine/templates/cw_heuristics.yaml.tmpl .edpa/config/cw_heuristics.yaml
cp .edpa/engine/templates/edpa.yaml.tmpl .edpa/config/edpa.yaml

The files in .edpa/config/*.yaml (not *.yaml.tmpl) must be committed in the repo.

PR-signal sync — missing evidence from a PR

PR-thread signals (pr_reviewer, issue_comment) land in evidence[] only via the optional contribution-sync.

Fix:

  • Verify .github/workflows/edpa-contribution-sync.yml is installed (/edpa:setup --with-ci)
  • Verify the EDPA_TOKEN secret (see docs/edpa-token-setup.md)
  • The workflow runs only after a PR merge; before merge, evidence holds only local signals (commit_author, yaml_edit)

Glossary

TermMeaning
PIPlanning Interval (AI-native 5 weeks = 4 delivery + 1 IP; classic SAFe 10 weeks)
IPInnovation & Planning (the last iteration of a PI)
JSJob Size — relative work size (Fibonacci)
BVBusiness Value
TCTime Criticality
RR-OERisk Reduction & Opportunity Enablement (CLI flag --rr-oe, legacy alias --rr)
WSJFWeighted Shortest Job First = (BV+TC+RR-OE)/JS
CWContribution Weight — per-item share (0.0 - 1.0); per-item Σ cw = 1.0
SignalProof of contribution from git/PR (commit_author, pr_reviewer, issue_comment) with a weight
MADMean Absolute Deviation (calibration metric)
Evidenceevidence[] on an item — aggregated signals; detect_contributors.py computes contributors[] from them
GateStatus transition on a Feature/Epic/Initiative; distributes the parent JS via gate_weights
Ground truthTeam-confirmed reality (used for calibration)