mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
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:
@@ -444,3 +444,68 @@ class TestExtractPmReview:
|
||||
digest = build_task_handoff(t, [])
|
||||
assert digest is not None
|
||||
assert "pm_review" not in digest
|
||||
|
||||
|
||||
class TestDescriptionAndParentContext:
|
||||
"""The ask (description + upstream parent_context) now rides the evidence
|
||||
payload and the task handoff so a freshly-claimed leaf receives the intake's
|
||||
analysis instead of an empty handoff."""
|
||||
|
||||
def test_payload_omits_description_when_none(self) -> None:
|
||||
"""A descriptionless task must not emit ``description: null`` in every
|
||||
evidence payload — omitted like the other noise-when-empty fields."""
|
||||
t = _task()
|
||||
ev = build_evidence_for_task(t, journal_highlights=[], files_changed=[])
|
||||
assert "description" not in ev.as_dict()
|
||||
|
||||
def test_payload_carries_description_when_set(self) -> None:
|
||||
t = _task()
|
||||
t.description = "edit prompter.py:412 to thread task_id through"
|
||||
ev = build_evidence_for_task(t, journal_highlights=[], files_changed=[])
|
||||
assert (
|
||||
ev.as_dict()["description"]
|
||||
== "edit prompter.py:412 to thread task_id through"
|
||||
)
|
||||
|
||||
def test_payload_carries_parent_context(self) -> None:
|
||||
t = _task()
|
||||
chain = [
|
||||
{
|
||||
"task_id": "p",
|
||||
"depth": 1,
|
||||
"title": "Root",
|
||||
"description": "intake analysis",
|
||||
}
|
||||
]
|
||||
ev = build_evidence_for_task(
|
||||
t, journal_highlights=[], files_changed=[], parent_context=chain
|
||||
)
|
||||
assert ev.as_dict()["parent_context"] == chain
|
||||
|
||||
def test_payload_omits_parent_context_when_empty(self) -> None:
|
||||
t = _task()
|
||||
ev = build_evidence_for_task(t, journal_highlights=[], files_changed=[])
|
||||
assert "parent_context" not in ev.as_dict()
|
||||
|
||||
def test_handoff_carries_description_and_parent_context(self) -> None:
|
||||
"""A leaf with no commits/PR yet still gets a handoff when the spec
|
||||
(description) or the upstream chain is present — the intake analysis
|
||||
reaches the dev at claim time."""
|
||||
t = _task(pr_number=None, pr_url=None, commits=[], dev_notes="")
|
||||
t.description = "the ask: edit foo.py:10"
|
||||
chain = [
|
||||
{"task_id": "p", "depth": 1, "title": "Root", "description": "upstream"}
|
||||
]
|
||||
handoff = build_task_handoff(t, [], parent_context=chain)
|
||||
assert handoff is not None
|
||||
assert handoff["description"] == "the ask: edit foo.py:10"
|
||||
assert handoff["parent_context"] == chain
|
||||
|
||||
def test_handoff_none_when_no_spec_and_no_prior_work(self) -> None:
|
||||
"""A bare task with no spec, no commits, no PR, no findings, no upstream
|
||||
chain still collapses to None — no empty handoff payload."""
|
||||
t = _task(pr_number=None, pr_url=None, commits=[], dev_notes="")
|
||||
t.commits = [] # _task's `commits or [...]` default would re-seed one
|
||||
t.acceptance_criteria_status = []
|
||||
# MagicMock auto-attrs make description a non-str -> _typed -> None.
|
||||
assert build_task_handoff(t, []) is None
|
||||
|
||||
Reference in New Issue
Block a user