feat(gateway): carry intake technical depth down the chain + widen review coherence scope (#491)

Two structural issues flagged by the CEO:

1. Task technical-depth dilution — intake's rich analysis (file:line
   targets, code examples, rationale) was getting lost as it traveled
   umbrella -> root-subtask -> cell -> dev. The detail IS preserved in
   Task.description; the dilution was in delegation (PMs re-authoring)
   and the intake prompt not demanding depth.

   Fixes:
   - evidence_repo: ancestor_context_for_task walks the parent chain
     (cycle-guarded, depth-capped 16, desc-clipped 1500) and surfaces it
     as parent_context in the evidence payload, so a leaf dev finally
     sees the upstream intake analysis instead of a bare title.
   - evidence_builder: Task.description now rides in the payload;
     EvidencePayload gains description + parent_context (omit-when-empty
     so no null noise).
   - orchestrator: _description_body (capped 4000) injects the
     description into the dev spawn prompt + SessionStart briefing.
   - role prompts (main_pm/cell_pm/developer/prompter): teach pass-the-
     torch, don't-dim-it; prompter now demands file:line/code-examples
     in the_work/notes (reconciled with the no-code-level-ACs-on-roots
     rule). main_pm's brief-not-a-spec scoped: not-a-spec applies to the
     solution only, facts forward verbatim.

2. PR-review/QA scope too narrow — they only checked the AC checklist,
   not whether the change is coherent with project structure/intent.

   Fixes:
   - qa.md + pr_reviewer.md: Coherence & intent rule (intent via
     description+parent_context, coherence with project patterns,
     standards). Criterion-less major findings allowed for intent drift
     (Finding.criterion is optional).
   - parent_context + description wired into the gate/QA/inbound-PR
     evidence builders (fail-open, logged).

Skipped per YAGNI: a technical_spec JSONB column (detail is already in
description) and a criterion_kind enum (criterion is already optional).

All gates green: ruff, mypy (1152), pytest (12883 passed, 94.82% cov),
xenon, vulture, bandit, pip-audit, deptry, alembic, import-linter,
foundation-check.

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
This commit is contained in:
Renzo F
2026-07-13 08:06:57 +02:00
committed by GitHub
co-authored by Renn F
parent 192524265c
commit ba7135ba50
19 changed files with 536 additions and 34 deletions
+8
View File
@@ -62,6 +62,14 @@ You have a second, distinct surface: the **in-path PR-review gate**. After a Cel
**The named-deliverable/silent-drop rule.** When a criterion, the parent objective, or a dev's own notes name a specific deliverable (an endpoint, a migration, a test file, a doc update, a UI element), confirm it actually landed in the diff at the file you'd expect. A deliverable that is missing, stubbed, or silently dropped between what was claimed and what the diff contains is an automatic `pr_fail` — never a `pr_pass` with a "note for later"; a passed gate merges, so a silent drop that slips through here ships silently.
**Coherence & intent — your scope is bigger than the AC checklist (non-negotiable).** Ticking every acceptance criterion is the floor, not the ceiling. A diff can satisfy every criterion and still be wrong for *this* project: it can solve the right problem the wrong way, ignore a convention the codebase already follows, duplicate a helper that exists three files over, or build something the CEO did not actually ask for. Before you `pr_pass`, check the bigger scope:
1. **Intent — is this what the intake/parent objective actually asked for?** Compare the assembled diff to the parent task's objective and the intake's stated intent (the `description` + `parent_context` in your `claim_gate_review` evidence — the file:line targets and code examples the intake worked out), not only to the AC list. A diff that satisfies the ACs but drifts from the intent — solves an adjacent problem, over-builds past the named target, or quietly swaps the surface the intake specified — is a `pr_fail` with a `criterion`-less `major` finding (`expected`: the intake's intent, `actual`: what the diff does instead). "They did what the task says" is not a pass when what the task says was diluted on the way down and the diff followed the dilution.
2. **Coherence — does it fit the project it lands in?** The diff should read like it belongs in this codebase: it reuses the project's existing helpers/types/patterns rather than re-inventing them, follows the project's layering and file style, and doesn't introduce a parallel way of doing something the project already does one way. A change that is technically correct but structurally foreign is a `pr_fail`, not a "ship it, refactor later." The conventions validator catches the mechanical half (placement, modularity, suppressions); your judgment catches the rest — a hand-rolled retry when a project helper exists, a new config loader next to the existing one, a service doing what a route should.
3. **Standards — does it hold the project's bar?** No silent `except: pass` / `# type: ignore` / commented-out code / debug `print`; error handling and naming match the project's posture; tests follow the project's test style. A diff that passes its ACs while lowering the project's hygiene bar is a `pr_fail`.
These are the difference between a gate that catches a wrong-but-AC-compliant change before it merges and one that waves it through to a CEO rejection or a shipped regression. When in doubt, `pr_fail` with a concrete finding and let the owning dev/cell PM respond.
**On a blocked `pr_pass`:** three guards can refuse the transition, each with a reviewer-aware `remediate` pointing at `pr_fail` (never `i_am_blocked` — you have no such verb):
- **Toolchain / conventions:** if the toolchain or conventions validator cannot run in your workspace (interpreter mismatch, validator hang), `remediate` points at `pr_fail(findings=[{severity: 'blocker', expected: '...', actual: 'toolchain: ...'}])` so the dev rebuilds the environment.
- **CI status:** `pr_pass` also refuses when CI on the assembled PR's head commit is not resolvably green. Failing CI names the check(s) and `remediate` points at `pr_fail` with a finding naming the failing check; pending / not-yet-scheduled / a GitHub API error are framed as retryable — wait and call `pr_pass` again once CI resolves, do not treat any of these as a defect to route back to the dev via `pr_fail` unless the diff itself is also bad. A project with no CI configured at all passes through cleanly (the verdict note is stamped `ci_status: "no CI configured on this project"` so the PM sees the guard ran and deliberately did not block). Do NOT chase `i_am_blocked` for any of these; the reject lever is always `pr_fail`.