Skip to content

Commit 3a2dcb1

Browse files
committed
Another pass complete. make verify is green — smoke, audit, operator smoke, and 93 unit tests.
What changed Doctor as a first-class operator surface format_doctor_report() — human-readable summary with checks, bootstrap fill status, sample task, recommendations, and next command roadmap(action='doctor') includes a report field health.py roadmap doctor CLI subcommand prints the formatted report Richer steering digest build_project_steering_digest() now includes agent_next_call from the fill plan (single source for autofill → validate flow) Evidence pipeline hardening extend_evidence() ensures project_fingerprint is attached when upgrading light → standard/full tiers (checkpoint cache paths no longer miss fingerprint) Native write hints carry digest roadmap_write_hint() attaches project_steering_digest when bootstrap is incomplete (sample task + remaining count on _roadmap_write_hint) Cockpit report Shows sample fill replacement preview from digest when bootstrap placeholders remain Audit & tests (+3) Digest agent_next_call, doctor report, extend_evidence fingerprint Tests: digest agent_next_call, doctor report, native bridge digest on write hint Per-project steering now flows through doctor CLI, write transforms, and evidence extension — not only checkpoint/cockpit tool responses.
1 parent da18998 commit 3a2dcb1

9 files changed

Lines changed: 146 additions & 0 deletions

File tree

health.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -649,6 +649,12 @@ def handle_dietcode_command(raw_args: str) -> Optional[str]:
649649
except ImportError:
650650
from lib.agent.roadmap.cockpit import format_cockpit_report
651651
return format_cockpit_report()
652+
if roadmap_sub == "doctor":
653+
try:
654+
from plugins.dietcode.lib.agent.roadmap.doctor import format_doctor_report
655+
except ImportError:
656+
from lib.agent.roadmap.doctor import format_doctor_report
657+
return format_doctor_report()
652658
if roadmap_sub == "progress":
653659
try:
654660
from plugins.dietcode.lib.agent.roadmap.progress import format_progress_report, read_tail

lib/agent/roadmap/bootstrap_fill.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -498,6 +498,7 @@ def build_project_steering_digest(
498498
"suggested_replacement": first.get("suggested_replacement"),
499499
"evidence_source": first.get("evidence_source"),
500500
}
501+
digest["agent_next_call"] = fill_plan.get("agent_next_call")
501502
return digest
502503

503504

lib/agent/roadmap/cockpit.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,12 @@ def format_cockpit_report(*, workspace: Optional[str] = None) -> str:
214214
make_targets = digest.get("makefile_targets") or data.get("project_fingerprint", {}).get("makefile_targets") or []
215215
if make_targets:
216216
lines.append(f"Makefile targets: {', '.join(make_targets[:4])}")
217+
sample = digest.get("sample_fill_task") or {}
218+
if sample.get("suggested_replacement"):
219+
repl = str(sample["suggested_replacement"])
220+
if len(repl) > 90:
221+
repl = repl[:89] + "…"
222+
lines.append(f"Sample fill: {repl}")
217223
lines.append("")
218224
lines.append(data.get("operator_summary") or "")
219225
if data.get("progress_phase"):

lib/agent/roadmap/doctor.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,3 +222,49 @@ def _check(name: str, ok: bool, detail: str = "") -> None:
222222
"last_error": last_error or None,
223223
"recommendations": recommendations,
224224
}
225+
226+
227+
def format_doctor_report(*, workspace: Optional[str] = None) -> str:
228+
"""Human-readable roadmap doctor summary."""
229+
data = run_checks(workspace=workspace)
230+
lines = [
231+
"🗺️ Roadmap doctor",
232+
f"Workspace: {data.get('workspace') or '(unresolved)'}",
233+
]
234+
if data.get("steering_brief"):
235+
lines.append(f"Project: {data['steering_brief']}")
236+
if data.get("stack_summary"):
237+
lines.append(f"Stack: {data['stack_summary']}")
238+
lines.append(f"Enabled: {data.get('enabled')}")
239+
lines.append("")
240+
241+
for check in data.get("checks") or []:
242+
mark = "✓" if check.get("ok") else "✕"
243+
detail = check.get("detail") or ""
244+
suffix = f" — {detail}" if detail and detail not in ("ok", "enabled", "disabled in config") else ""
245+
lines.append(f"{mark} {check.get('name')}{suffix}")
246+
247+
digest = data.get("project_steering_digest") or {}
248+
remaining = digest.get("bootstrap_remaining")
249+
if remaining and int(remaining) > 0:
250+
lines.append("")
251+
lines.append(
252+
f"Bootstrap fill: {remaining} phrase(s) — roadmap(action='apply_bootstrap_fill', context='write')"
253+
)
254+
sample = digest.get("sample_fill_task") or {}
255+
if sample.get("template_phrase"):
256+
lines.append(f" sample: “{sample['template_phrase'][:50]}…”")
257+
258+
recs = data.get("recommendations") or []
259+
if recs:
260+
lines.append("")
261+
lines.append("Recommendations:")
262+
for rec in recs[:6]:
263+
lines.append(f" • {rec}")
264+
265+
next_rec = data.get("recommended_next_action") or {}
266+
if next_rec.get("command"):
267+
lines.append("")
268+
lines.append(f"→ {next_rec['command']}")
269+
270+
return "\n".join(lines)

lib/agent/roadmap/evidence.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,14 @@ def extend_evidence(
335335
if not evidence.get("code_soup_audit"):
336336
evidence["code_soup_audit"] = assess_code_soup(root, heavy_scan=heavy)
337337

338+
if not evidence.get("project_fingerprint"):
339+
try:
340+
from plugins.dietcode.lib.agent.roadmap.project_fingerprint import build_project_fingerprint
341+
342+
evidence["project_fingerprint"] = build_project_fingerprint(root)
343+
except Exception:
344+
evidence["project_fingerprint"] = {"steering_identity": root.name}
345+
338346
return evidence
339347

340348

lib/agent/roadmap/native_bridge.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,18 @@ def roadmap_write_hint(*, tool_name: str = "", args: Any = None, workspace: Opti
152152
elif project_brief:
153153
followup += " If bootstrap template phrases remain, preview roadmap(action='apply_bootstrap_fill') or apply with context='write'."
154154

155+
digest: dict[str, Any] = {}
156+
if bootstrap_inc and check.get("allowed") and check.get("workspace"):
157+
try:
158+
from plugins.dietcode.lib.agent.roadmap.bootstrap_fill import attach_bootstrap_steering_fields
159+
from plugins.dietcode.lib.agent.roadmap.steering_context import build_steering_context
160+
161+
steering = build_steering_context(workspace=check.get("workspace"))
162+
attached = attach_bootstrap_steering_fields(steering, tier="light")
163+
digest = attached.get("project_steering_digest") or {}
164+
except Exception:
165+
digest = {}
166+
155167
next_action = (
156168
"roadmap(action='apply_bootstrap_fill', context='write') then roadmap(action='validate')"
157169
if bootstrap_inc
@@ -176,6 +188,8 @@ def roadmap_write_hint(*, tool_name: str = "", args: Any = None, workspace: Opti
176188
"roadmap_path": check.get("roadmap_path"),
177189
"expected_path": check.get("expected_path"),
178190
"project_steering_brief": project_brief,
191+
"bootstrap_incomplete": bootstrap_inc,
192+
"project_steering_digest": digest or None,
179193
"write_rejected": False,
180194
}
181195

lib/tools/roadmap_tools.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,9 +125,12 @@ def _dispatch(
125125
if act == "doctor":
126126
root = resolve_workspace_root(workspace)
127127
install = ensure_workspace_skills(root)
128+
from plugins.dietcode.lib.agent.roadmap.doctor import format_doctor_report
129+
128130
report = run_checks(workspace=root)
129131
report["skill_install"] = install
130132
report["action"] = "doctor"
133+
report["report"] = format_doctor_report(workspace=root)
131134
return _finish_payload(act, report)
132135

133136
if act == "cockpit":

scripts/roadmap_audit.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,28 @@ def main() -> int:
240240
failures.append("bootstrap_fill_plan missing now_suggestions")
241241
if not fill_plan.get("project_brief"):
242242
failures.append("bootstrap_fill_plan missing project_brief")
243+
from plugins.dietcode.lib.agent.roadmap.bootstrap_fill import build_project_steering_digest
244+
245+
digest = build_project_steering_digest(fp, fill_plan=fill_plan)
246+
if fill_plan.get("tasks") and not digest.get("agent_next_call"):
247+
failures.append("steering digest missing agent_next_call")
248+
249+
from plugins.dietcode.lib.agent.roadmap.doctor import format_doctor_report, run_checks
250+
251+
dr = run_checks(workspace=str(root))
252+
if not dr.get("recommended_next_action"):
253+
failures.append("doctor missing recommended_next_action")
254+
if "apply_bootstrap_fill" not in format_doctor_report(workspace=str(root)):
255+
failures.append("doctor report missing bootstrap fill hint")
256+
257+
from plugins.dietcode.lib.agent.roadmap.evidence import extend_evidence
258+
259+
extended = extend_evidence(
260+
{"workspace": str(root), "evidence_tier": "light", "git": evidence.get("git") or {}, "roadmap": evidence.get("roadmap") or {}},
261+
tier="standard",
262+
)
263+
if not extended.get("project_fingerprint"):
264+
failures.append("extend_evidence missing project_fingerprint")
243265

244266
brief = session_brief(workspace=str(root))
245267
if not brief or not brief.get("roadmap_path"):

tests/test_roadmap_checkpoint.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1126,6 +1126,46 @@ def test_explain_stale_report_includes_project(self) -> None:
11261126
)
11271127
self.assertIn("Stale Project", report)
11281128

1129+
def test_steering_digest_includes_agent_next_call(self) -> None:
1130+
from plugins.dietcode.lib.agent.roadmap.bootstrap_fill import attach_bootstrap_steering_fields
1131+
from plugins.dietcode.lib.agent.roadmap.steering_context import build_steering_context
1132+
from plugins.dietcode.lib.agent.roadmap.schema import bootstrap_skeleton
1133+
1134+
with tempfile.TemporaryDirectory() as tmp:
1135+
root = Path(tmp)
1136+
(root / "ROADMAP.md").write_text(bootstrap_skeleton(project_hint="Digest Call"), encoding="utf-8")
1137+
steering = build_steering_context(workspace=str(root))
1138+
attached = attach_bootstrap_steering_fields(steering, tier="light")
1139+
digest = attached.get("project_steering_digest") or {}
1140+
self.assertIn("apply_bootstrap_fill", digest.get("agent_next_call") or "")
1141+
1142+
def test_format_doctor_report_bootstrap_hint(self) -> None:
1143+
from plugins.dietcode.lib.agent.roadmap.doctor import format_doctor_report
1144+
from plugins.dietcode.lib.agent.roadmap.schema import bootstrap_skeleton
1145+
1146+
with tempfile.TemporaryDirectory() as tmp:
1147+
root = Path(tmp)
1148+
(root / "ROADMAP.md").write_text(bootstrap_skeleton(project_hint="Doctor Report"), encoding="utf-8")
1149+
report = format_doctor_report(workspace=str(root))
1150+
self.assertIn("Roadmap doctor", report)
1151+
self.assertIn("apply_bootstrap_fill", report)
1152+
1153+
def test_native_bridge_write_hint_includes_digest(self) -> None:
1154+
from plugins.dietcode.lib.agent.roadmap.native_bridge import roadmap_write_hint
1155+
from plugins.dietcode.lib.agent.roadmap.schema import bootstrap_skeleton
1156+
1157+
with tempfile.TemporaryDirectory() as tmp:
1158+
root = Path(tmp)
1159+
(root / "ROADMAP.md").write_text(bootstrap_skeleton(project_hint="Bridge Digest"), encoding="utf-8")
1160+
hint = roadmap_write_hint(
1161+
tool_name="write_file",
1162+
args={"path": "ROADMAP.md"},
1163+
workspace=str(root),
1164+
)
1165+
digest = hint.get("project_steering_digest") or {}
1166+
self.assertTrue(hint.get("bootstrap_incomplete"))
1167+
self.assertTrue(digest.get("bootstrap_remaining") or digest.get("sample_fill_task"))
1168+
11291169

11301170
class RoadmapWorkspaceResolutionTests(unittest.TestCase):
11311171
def setUp(self) -> None:

0 commit comments

Comments
 (0)