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
11 changes: 11 additions & 0 deletions .edpa/backlog/defects/D-1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
id: D-1
type: Defect
title: Git hooks not registered after update / under lefthook — contribution evidence stops firing
status: Funnel
js: 0
bv: 0
tc: 0
rr_oe: 0
wsjf: 0.0
---
8 changes: 8 additions & 0 deletions .edpa/config/id_counters.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
counters:
Defect: 1
Epic: 12
Event: 2
Feature: 120
Initiative: 1
Risk: 3
Story: 226
44 changes: 0 additions & 44 deletions .github/workflows/edpa-sync-git-to-projects.yml

This file was deleted.

85 changes: 0 additions & 85 deletions .github/workflows/edpa-sync-projects-to-git.yml

This file was deleted.

44 changes: 44 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,49 @@
# Changelog

## 2.3.0 — 2026-06-03 — Robust git-hook registration + lefthook support

Git hooks could silently stop firing — most visibly the `post-commit`
`local_evidence.py` emitter, so **contribution evidence stopped landing on
items** — whenever a hook slot was already occupied (typically by lefthook,
which owns `.git/hooks/`) or after a plugin update left a stale snapshot.
Registration is now robust and hook-manager-aware.

### Fixed
- `project_setup.install_hooks` no longer skips a slot with a blunt
`not dst.exists()` guard. EDPA marks its hooks with an `EDPA-MANAGED-HOOK`
sentinel and decides per slot: install when missing, refresh when EDPA-owned,
and **never clobber a foreign hook** (it warns + prints the exact chain-in
line instead).
- The SessionStart auto-update (`update_engine.sh`) now **re-registers EDPA git
hooks after a version bump** when the project already uses them — fixing
"hooks gone / contribution stopped after update". Opt-out repos stay untouched.

### Added
- **lefthook support.** When a `lefthook.yml` (or other lefthook config) is
present, `--with-hooks` detects it and prints a paste-ready lefthook snippet
(with `use_stdin: true` on `pre-push` — required, or lefthook hangs the push)
instead of writing `.git/hooks/`. EDPA never edits your lefthook config.
- `project_setup.py --check-hooks` — a read-only hooks doctor (active / missing
/ foreign / lefthook) — and `--refresh-hooks` — register/refresh only.

### Changed
- `scripts/hooks/install.sh` is now a thin delegator to
`project_setup.py --refresh-hooks`; the old conflicting `core.hooksPath`
mechanism (and its stale `.claude/edpa/...` path) is gone.
- ANSI colour codes in `project_setup.py` are suppressed when stdout is not a
TTY (no escape-code leak into captured SessionStart output).
- Removed the dead generic `pre-commit` hook (superseded by `pre-commit-id-safety`),
and the dead V1 GitHub-Projects sync workflows (`edpa-sync-git-to-projects.yml`,
`edpa-sync-projects-to-git.yml`) — they called a `sync.py` removed in 2.0.0 and
had been failing on every release.
- Docs + website (CZ/EN) now document hook registration, the lefthook snippet,
foreign-hook behavior, and `--check-hooks` verification.

### Tests
- New `tests/test_project_setup_hooks.py` (fresh install, refresh, foreign-skip,
lefthook snippet validity) plus self-heal cases in
`tests/test_update_engine_hook.py`. Full suite green.

## 2.2.1 — 2026-06-01 — Skill names: drop the redundant `edpa-` prefix; server + create-pi are command-only

Plugin skill invocations were doubly namespaced — `/edpa:edpa-setup`,
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ All invariants passed: YES

## Key Features

- **Zero manual input** — hours derived from **local git evidence**: post-commit hook emits `commit_author` + `/contribute` signals; engine reads `yaml_edit`, gate-event, and in-flight Story activity directly from `git log`.
- **Zero manual input** — hours derived from **local git evidence**: post-commit hook emits `commit_author` + `/contribute` signals; engine reads `yaml_edit`, gate-event, and in-flight Story activity directly from `git log`. Hooks register into `.git/hooks/` (or, under lefthook, via a printed snippet) and can be verified with `project_setup.py --check-hooks`.
- **Mathematical guarantee** — derived hours always sum to declared capacity
- **Gates mode (default)** — credits each Initiative/Epic/Feature status transition as a mini-deliverable, so prep work (LBC, decomposition, design) gets credited as it happens, not only at final Done. Validated to ±0.35 pp stability under ±20 % CW perturbation across 100 Monte Carlo runs.
- **C7.5 in-flight Story credit** — Stories with `yaml_edit` activity in the iteration window receive partial credit (`js × credit_factor`, default 0.40) even before they reach Done; the `story_activity_events[]` audit log in `edpa_results.json` records what was credited and why.
Expand Down
2 changes: 1 addition & 1 deletion docs/E2E-TEST-PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -785,7 +785,7 @@ Plán je úspěšně provedený, pokud:
| `.edpa/engine/scripts/calibrate_signals.py` | auto-kalibrace CW vah (Monte Carlo + coord descent) |
| `.edpa/engine/scripts/hooks/commit-msg-ticket-attached` | commit-msg hook — vyžaduje item ref / escape |
| `.edpa/engine/scripts/hooks/post-commit-evidence` | post-commit hook — emituje evidence |
| `.edpa/engine/scripts/hooks/pre-commit` | pre-commit hook — schema + ID safety |
| `.edpa/engine/scripts/hooks/pre-commit-id-safety` | pre-commit hook — ID safety |
| `plugin/edpa/templates/github-workflows/edpa-contribution-sync.yml` | (volitelně) jediný V2 GH Action |
| `plugin/hooks/hooks.json` | Claude Code hooks (validate, commit info) |
| `plugin/.claude-plugin/plugin.json` | plugin manifest (single source of truth verze) |
Expand Down
72 changes: 66 additions & 6 deletions docs/RUNBOOK.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,9 @@ engine + `.edpa/` tree.

**Flags (all recommended for team workflows):**
- `--with-hooks` — pre-commit + commit-msg + post-commit + pre-push git hooks
(ID safety, ticket-attached, local `commit_author` evidence emission).
(ID safety, ticket-attached, local `commit_author` evidence emission). Detects
lefthook and prints a paste-ready snippet instead of touching `.git/hooks/`;
never clobbers a foreign hook. See **Git hooks** below.
- `--with-ci` — copies `edpa-contribution-sync.yml`; materializes PR-thread
signals (`pr_reviewer`, `issue_comment`) into `evidence[]` after merge.
Optional, GitHub-only — local commit evidence flows without it.
Expand All @@ -86,7 +88,7 @@ engine + `.edpa/` tree.
**Expected output (last steps):**

```
[1] Vendor engine ✓ Vendored engine → .edpa/engine/ (37 scripts, VERSION 2.1.9)
[1] Vendor engine ✓ Vendored engine → .edpa/engine/ (37 scripts, VERSION 2.3.0)
[2] Directory tree ✓ Directory tree at .edpa/
[3] Config templates ✓ Seeded people.yaml, edpa.yaml, cw_heuristics.yaml
[4] ID counter ✓ id_counters.yaml seeded
Expand All @@ -101,8 +103,61 @@ EDPA setup complete.
hand-edit; the SessionStart hook re-syncs it on plugin update).
- `.edpa/config/{edpa.yaml,people.yaml,cw_heuristics.yaml,id_counters.yaml}`.
- `.edpa/{backlog,iterations,reports,snapshots}/` tree.
- (flags) `.git/hooks/*`, `.github/workflows/edpa-contribution-sync.yml`,
`.claude/rules/`.
- (flags) `.git/hooks/*` (or a lefthook snippet),
`.github/workflows/edpa-contribution-sync.yml`, `.claude/rules/`.

**Git hooks — registration, lefthook, verification:**

`--with-hooks` writes four hooks into `.git/hooks/` (`pre-commit`, `pre-push`,
`commit-msg`, `post-commit`). The `post-commit` one runs `local_evidence.py` —
**this is what records contribution evidence onto items**, so if it isn't
registered, contributions silently never appear. The registration is
deliberately careful:

- **Re-running is safe and self-refreshing.** Re-run `/edpa:setup --with-hooks`
(or `python3 .edpa/engine/scripts/project_setup.py --refresh-hooks`) any time:
EDPA-owned hooks are overwritten with the current version, missing ones
reinstalled. EDPA marks its hooks with an `EDPA-MANAGED-HOOK` sentinel.
- **Foreign hooks are never clobbered.** If a non-EDPA file already occupies a
slot, EDPA skips it and prints a loud warning with the exact line to chain
EDPA in by hand (`sh .edpa/engine/scripts/hooks/<hook> "$@"`).
- **lefthook (or any tool that owns `.git/hooks/`).** lefthook generates its own
dispatcher shims into `.git/hooks/` (and can set `core.hooksPath`), so a plain
copy would be ignored or clobbered — this is the usual cause of "contribution
stopped working after an update". EDPA detects `lefthook.yml` and, instead of
writing `.git/hooks/`, prints a paste-ready block. Add it to your
`lefthook.yml`, then run `lefthook install`:

```yaml
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 # pre-push refs arrive on stdin — without this lefthook hangs
```

- **After a plugin update**, the SessionStart auto-update re-registers EDPA hooks
automatically when the project already uses them (and, under lefthook, reminds
you to verify). No manual step needed for the plain `.git/hooks/` case.
- **Verify any time** (read-only doctor — no changes):

```bash
python3 .edpa/engine/scripts/project_setup.py --check-hooks
```
Reports each hook as active / missing / foreign, or flags lefthook so you know
to register via the snippet.

**Next:** edit `people.yaml` (your team) + `edpa.yaml` (`project.name`), then
create items locally:
Expand Down Expand Up @@ -367,11 +422,16 @@ git add .github/workflows/edpa-collision-check.yml
git commit -m "ci: add EDPA collision check"
```

Verify hooks are installed:
Verify hooks are installed (read-only doctor — works for `.git/hooks/` and
flags lefthook):
```bash
ls -la .git/hooks/pre-commit .git/hooks/pre-push # must be -rwxr-xr-x
python3 .edpa/engine/scripts/project_setup.py --check-hooks
```

If your repo uses **lefthook**, `--with-hooks` prints a paste-ready snippet
instead of writing `.git/hooks/`; add it to `lefthook.yml` and run
`lefthook install` (see §1 → *Git hooks — registration, lefthook, verification*).

See [`docs/dev-collisions.md`](dev-collisions.md) for decision tree, common collision shapes (single / multi / parent-chain / cascading), troubleshooting, and the `--target develop` flag for Git Flow projects.

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

*Capacity derivation from delivery evidence*

**Version 2.2.1 — June 2026 — Jaroslav Urbanek, Lead Architect**
**Version 2.3.0 — June 2026 — Jaroslav Urbanek, Lead Architect**

---

Expand Down
33 changes: 29 additions & 4 deletions docs/playbook.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ V terminalu s Claude Code nainstalovanym:
/edpa:setup --with-ci --with-hooks --with-rules
```

Claude Code (skill `/edpa:setup`) provede kroky 1.1-1.4 automaticky -- vendoruje engine do `.edpa/engine/`, naseje konfiguraci a `id_counters.yaml`, a volitelne nainstaluje git hooky, PR-signal CI workflow a `.claude/rules/`. Idempotentni -- opakovane spusteni nic nerozbije.
Claude Code (skill `/edpa:setup`) provede kroky 1.1-1.4 automaticky -- vendoruje engine do `.edpa/engine/`, naseje konfiguraci a `id_counters.yaml`, a volitelne nainstaluje git hooky, PR-signal CI workflow a `.claude/rules/`. Idempotentni -- opakovane spusteni nic nerozbije. `--with-hooks` je lefthook-aware (pri pritomnem `lefthook.yml` vypise snippet misto zapisu do `.git/hooks/`); stav overis pres `--check-hooks`.

### Cesta B: Manualni CLI

Expand All @@ -101,6 +101,8 @@ curl -fsSL https://edpa.technomaton.com/install.sh | sh
python3 .edpa/engine/scripts/project_setup.py --with-ci --with-hooks --with-rules
```

> `--with-hooks` je lefthook-aware (pri pritomnem `lefthook.yml` vypise paste-ready snippet misto zapisu do `.git/hooks/`); stav hooku overis pres `--check-hooks`.

Vysledna struktura:

```
Expand Down Expand Up @@ -446,7 +448,30 @@ Povolene prefixy: `S` (Story), `F` (Feature), `E` (Epic), `T` (Task), `D` (Defec
| pre-commit | ID safety -- kontrola referenci |
| commit-msg | Vyzaduje referenci itemu nebo `no-ticket:` |
| post-commit | Zaznamenava `commit_author` evidence |
| pre-push | Kontrola ID kolizi |
| pre-push | Kontrola ID kolizi vuci remote |

Registrace je idempotentni a sebe-obnovujici: EDPA znacka sve hooky sentinelem `EDPA-MANAGED-HOOK`, opakovany `--with-hooks` (nebo `--refresh-hooks`) je osvezi a cizi (ne-EDPA) hook v dane pozici **nikdy nepreplacne** -- vypise hlasku + radek k rucnimu zaretezeni (`sh .edpa/engine/scripts/hooks/<hook> "$@"`). Pokud je v repu **lefthook** (`lefthook.yml`), `.git/hooks/` vlastni lefthook, takze EDPA tam nezapisuje a misto toho vypise hotovy snippet do `lefthook.yml`; pote spust `lefthook install`. Stav hooku overis read-only pres `--check-hooks` (kazdy hook jako active / missing / foreign, pripadne flag lefthook).

```yaml
# lefthook.yml -- EDPA hooky (pre-push MUSI mit use_stdin: true, jinak push zatuhne)
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 konvence:**

Expand Down Expand Up @@ -782,13 +807,13 @@ V2 ma **jediny** volitelny workflow (jen s `--with-ci`):
- [ ] Backlog naplneny pres `backlog.py add` (alespon 1 Epic, 3 Features, 10 Stories)
- [ ] `backlog.py validate` projde bez chyb
- [ ] `engine.py --demo` projde uspesne
- [ ] (volitelne) git hooky nainstalovany (`--with-hooks`), contribution-sync CI (`--with-ci`)
- [ ] (volitelne) git hooky nainstalovany (`--with-hooks`) a overeny (`--check-hooks` -> vsechny active; pri lefthooku snippet v `lefthook.yml` + `lefthook install`), contribution-sync CI (`--with-ci`)

### Tyden 1

- [ ] Tym pracuje s branch naming konvenci (`feature/S-XXX-popis`)
- [ ] Commity referuji work items (`feat(S-XXX): ...`)
- [ ] post-commit hook zaznamenava `commit_author` evidenci
- [ ] post-commit hook zaznamenava `commit_author` evidenci (overeno `--check-hooks`; je-li post-commit `missing`/`foreign`, contribution evidence se tise nezapisuji -- u lefthooku doplnit snippet + `lefthook install`)
- [ ] (volitelne) PR reviews probihaji a contribution-sync je materializuje

### Konec iterace 1
Expand Down
Loading