Skip to content

P10Y variance per implementation-plan phase instead of commit component - #56

Merged
mkonopelski-gd merged 6 commits into
mainfrom
feat/p10y-statistics-over-phases
Jul 31, 2026
Merged

P10Y variance per implementation-plan phase instead of commit component#56
mkonopelski-gd merged 6 commits into
mainfrom
feat/p10y-statistics-over-phases

Conversation

@mkonopelski-gd

Copy link
Copy Markdown
Contributor

Why

Per-component variance was mostly noise. The bucket name came from the commit subject prefix, freely chosen by each agent, so parallel variants of the same plan produced different axes — in run ws-03 only backend and frontend were common to all three variants (report said "Only 22% of components are common"), and infrastructure 130% was really 90h vs absent vs 4h. Every variant executes the same implementation plan, so phases are a genuinely comparable axis.

Key design decisions

  • The phase prefix is injected by a git hook, not by the agent. prepare-commit-msg stamps pNN_ using git config specflow.phase, which the codegen loop sets before each phase. Attribution no longer depends on agent commit hygiene, and --no-verify does not bypass this hook. Agents now write a plain <action> <subject>.
  • The hook never touches SKIP_* commits. Otherwise the seed/janitor commits would become p24_SKIP_janitor_finalize and silently enter P10Y — this keeps _subject_excluded_from_estimation the single source of truth for exclusion.
  • Phase number is the join key; names come from the shared plan. Commits carry only the number; labels are resolved once from Firestore planning_data, so every variant labels a phase identically.
  • Commits outside a codegen phase go to a visible unphased bucket (initial seed, deploy/E2E loop, strays) — never silently folded into a real phase. The marker is set only in the codegen loop and cleared for deploy.
  • Full rename, no compatibility shim (component_*phase_* across schemas, reports and TUI; KNOWN_COMPONENTS deleted). Pre-release, no consumers of the old shape.
  • Reports split phase number from description, so the TUI can ellipsis-truncate long phase names in its own column.

Folded in along the way: the TUI rendered the risk buffer fraction as a raw percent (+1% instead of +83%), and P10Y commit coverage conflated two causes — now reported separately as a P10Y service gap vs non-code (docs/config) commits filtered by technology, which are not expected to score.

Verification

make unit-tests — backend 2109 passed, mcp_server 137 passed. make check-complexity-diff METRIC=cc: 3.61 → 3.62 (+0.01).

Note: 3 TestStartupGate failures in mcp_server/tests/test_tui_app.py are pre-existing on main (verified on a clean main worktree) and unrelated to this branch.

Commit-message component prefixes (backend_/frontend_/...) were freely chosen per
agent, so only ~20% of buckets were shared across parallel generation variants and
the reported "variance" was mostly noise from mismatched buckets rather than real
signal. Every variant executes the same implementation plan, so a prepare-commit-msg
git hook now stamps each commit with the active plan phase (pNN_) deterministically,
and P10Y groups/compares hours by phase instead of by freeform component name.

Also: fixes the TUI buffer display (was rendering a fraction as a raw percent, e.g.
"+1%" instead of "+83%"), and splits P10Y commit coverage into a P10Y service-gap
cause vs a non-code (docs/config) filtered cause instead of one conflated number.
execute_all_phases calls raise_if_cancelled(db_adapter, ...) on every phase iteration;
the test's bare Mock() for generation_session_service.db_adapter isn't awaitable, so
the test failed before ever reaching the connection-error behavior it's meant to check.
Set db_adapter=None to match the documented no-op path for tests without a DB adapter.
Conflict in test/test_execute_all_phases_connection_error.py: both sides fixed the
same non-awaitable-Mock bug (ours in 9c6aa68, main in #52) by setting
svc.db_adapter = None, differing only in the explanatory comment. Kept main's
single assignment and comment, and dropped the duplicate assignment #53 left
behind on top of #52.
The breakdown rendered "Phase 1"/"Phase 2", which says nothing the Phase # column
doesn't already say. Root cause: _load_phase_names read a top-level planning_data,
but the plan is persisted per workspace under workspace_phases[ws_id].planning_data
(save_generation_plan writes it there, and get_current_phase_name reads it there).
The lookup therefore always came back empty and every row fell back to a label
derived from the number.

Reads the plan from workspace_phases now, taking the first entry that carries phases
since all workspaces share one plan, and logs when none is found. When a phase has no
name the label is an em dash rather than "Phase N" — the number has its own column.
Renames the description column accordingly across the TUI, markdown and HTML reports.
@mkonopelski-gd
mkonopelski-gd merged commit 600c231 into main Jul 31, 2026
3 checks passed
@mkonopelski-gd
mkonopelski-gd deleted the feat/p10y-statistics-over-phases branch July 31, 2026 10:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants