|
170 | 170 | }, |
171 | 171 | ) |
172 | 172 |
|
| 173 | +DEFERRED_ROADMAP_ITEMS: tuple[dict[str, Any], ...] = ( |
| 174 | + { |
| 175 | + "id": "deeper-roadmap-ownership-modeling", |
| 176 | + "title": "Deeper roadmap ownership modeling", |
| 177 | + "subsystem": "roadmap", |
| 178 | + "owner": "roadmap", |
| 179 | + "source_section": "Roadmap State Audit And Closure Map", |
| 180 | + "deferred_reason": "Earlier roadmap audit work prioritized command visibility, JSON output, tests, and daily-loop health before richer ownership state.", |
| 181 | + "suggested_phase": 62, |
| 182 | + "status": "active", |
| 183 | + }, |
| 184 | + { |
| 185 | + "id": "private-pattern-source-aliases", |
| 186 | + "title": "Private pattern source aliases from local config", |
| 187 | + "subsystem": "roadmap", |
| 188 | + "owner": "roadmap", |
| 189 | + "source_section": "Inspiration Pattern Registry", |
| 190 | + "deferred_reason": "Exact reference source names belong only in gitignored host-local config, while public docs should expose neutral pattern families.", |
| 191 | + "suggested_phase": 62, |
| 192 | + "status": "active", |
| 193 | + }, |
| 194 | + { |
| 195 | + "id": "cross-producer-provenance-audit", |
| 196 | + "title": "Cross-producer provenance audits across historical sources", |
| 197 | + "subsystem": "work-inbox", |
| 198 | + "owner": "work", |
| 199 | + "source_section": "Scanner And Inbox Closure", |
| 200 | + "deferred_reason": "The scanner closeout phase tightened the common path first and left historical compatibility backfill for a focused pass.", |
| 201 | + "suggested_phase": 64, |
| 202 | + "status": "planned", |
| 203 | + }, |
| 204 | + { |
| 205 | + "id": "expanded-chat-export-parsers", |
| 206 | + "title": "Expanded chat export provider aliases and parser fixtures", |
| 207 | + "subsystem": "chat-surfaces", |
| 208 | + "owner": "chat", |
| 209 | + "source_section": "Chat Surface Export Completion", |
| 210 | + "deferred_reason": "The no-live-API boundary kept the first implementation focused on local export contracts and privacy gates.", |
| 211 | + "suggested_phase": 66, |
| 212 | + "status": "planned", |
| 213 | + }, |
| 214 | + { |
| 215 | + "id": "outbound-backup-status-messages", |
| 216 | + "title": "Outbound backup operator status messages", |
| 217 | + "subsystem": "backup-health", |
| 218 | + "owner": "backup", |
| 219 | + "source_section": "Backup And Recovery Closure", |
| 220 | + "deferred_reason": "Outbound notifications require product-specific surfaces and remain outside the local read-only operator loop.", |
| 221 | + "suggested_phase": None, |
| 222 | + "status": "out-of-scope", |
| 223 | + }, |
| 224 | + { |
| 225 | + "id": "tool-projection-parity-closeout", |
| 226 | + "title": "Separate tool projection parity closeout receipt", |
| 227 | + "subsystem": "tool-catalog", |
| 228 | + "owner": "tools", |
| 229 | + "source_section": "Shared Tool Catalog Completion", |
| 230 | + "deferred_reason": "Projection state is represented in packs and sync plans first, while closeout state needs a focused compatibility pass.", |
| 231 | + "suggested_phase": 68, |
| 232 | + "status": "planned", |
| 233 | + }, |
| 234 | + { |
| 235 | + "id": "context-harness-destination-writes", |
| 236 | + "title": "Context pack writes into harness destinations", |
| 237 | + "subsystem": "context", |
| 238 | + "owner": "context", |
| 239 | + "source_section": "Context Engineering Packs", |
| 240 | + "deferred_reason": "Context sync planning remains read-only until a future explicit context apply command exists.", |
| 241 | + "suggested_phase": 70, |
| 242 | + "status": "planned", |
| 243 | + }, |
| 244 | + { |
| 245 | + "id": "learning-accepted-risk-quieting", |
| 246 | + "title": "Rich accepted-risk quieting across learning sources", |
| 247 | + "subsystem": "learning", |
| 248 | + "owner": "learn", |
| 249 | + "source_section": "Self-Learning Loop Closure", |
| 250 | + "deferred_reason": "Candidate import routing exists first, while source-specific quieting policies remain subsystem-owned.", |
| 251 | + "suggested_phase": 74, |
| 252 | + "status": "planned", |
| 253 | + }, |
| 254 | + { |
| 255 | + "id": "security-sarif-output", |
| 256 | + "title": "Dependency-free security SARIF output", |
| 257 | + "subsystem": "security", |
| 258 | + "owner": "security", |
| 259 | + "source_section": "Security Plugin Closure", |
| 260 | + "deferred_reason": "JSON and Markdown evidence bundles exist, and SARIF needs a focused schema compatibility pass without new dependencies.", |
| 261 | + "suggested_phase": 76, |
| 262 | + "status": "planned", |
| 263 | + }, |
| 264 | + { |
| 265 | + "id": "stale-issue-repair-imports", |
| 266 | + "title": "Stale active issue repair imports", |
| 267 | + "subsystem": "work", |
| 268 | + "owner": "work", |
| 269 | + "source_section": "Issue And TDD Loop Closure", |
| 270 | + "deferred_reason": "The first acceptance phase completed the local acceptance rollup before repair-import routing.", |
| 271 | + "suggested_phase": 80, |
| 272 | + "status": "planned", |
| 273 | + }, |
| 274 | + { |
| 275 | + "id": "repo-shareable-workflow-rule-templates", |
| 276 | + "title": "Repo-shareable workflow rule templates", |
| 277 | + "subsystem": "install", |
| 278 | + "owner": "templates", |
| 279 | + "source_section": "Issue And TDD Loop Closure", |
| 280 | + "deferred_reason": "The first acceptance phase avoided mixing public workflow rules with personal preferences.", |
| 281 | + "suggested_phase": 79, |
| 282 | + "status": "planned", |
| 283 | + }, |
| 284 | + { |
| 285 | + "id": "safe-memory-autofix-planning", |
| 286 | + "title": "Safe memory-care autofix planning", |
| 287 | + "subsystem": "memory-care", |
| 288 | + "owner": "memory", |
| 289 | + "source_section": "Memory And Handoff Closure", |
| 290 | + "deferred_reason": "Memory-care closeouts record review state first, with mutation-free repair planning left for a focused pass.", |
| 291 | + "suggested_phase": 83, |
| 292 | + "status": "planned", |
| 293 | + }, |
| 294 | + { |
| 295 | + "id": "recursive-repo-root-discovery", |
| 296 | + "title": "Safe repo root discovery from configured roots", |
| 297 | + "subsystem": "repo-fleet", |
| 298 | + "owner": "repos", |
| 299 | + "source_section": "Repository Fleet Readiness", |
| 300 | + "deferred_reason": "Explicit repo config avoids accidentally exposing private repo names or paths; discovery needs a dry-run privacy-safe plan.", |
| 301 | + "suggested_phase": 91, |
| 302 | + "status": "planned", |
| 303 | + }, |
| 304 | +) |
| 305 | + |
173 | 306 |
|
174 | 307 | def _roadmap_path(target: Path) -> Path: |
175 | 308 | return target / "ROADMAP.md" |
@@ -360,6 +493,29 @@ def _normalize_documented_command(command: str, known_prefixes: set[str]) -> str |
360 | 493 | return command |
361 | 494 |
|
362 | 495 |
|
| 496 | +def _deferred_items() -> list[dict[str, Any]]: |
| 497 | + return [dict(item) for item in DEFERRED_ROADMAP_ITEMS] |
| 498 | + |
| 499 | + |
| 500 | +def _deferred_item_checks(items: list[dict[str, Any]]) -> list[dict[str, Any]]: |
| 501 | + missing_owner = [item["id"] for item in items if not item.get("owner")] |
| 502 | + missing_phase = [item["id"] for item in items if item.get("status") != "out-of-scope" and not item.get("suggested_phase")] |
| 503 | + return [ |
| 504 | + { |
| 505 | + "status": WARN if missing_owner else OK, |
| 506 | + "name": "roadmap_deferred_missing_owner", |
| 507 | + "detail": f"{len(missing_owner)} deferred item(s) missing owner" if missing_owner else "none", |
| 508 | + "items": missing_owner, |
| 509 | + }, |
| 510 | + { |
| 511 | + "status": WARN if missing_phase else OK, |
| 512 | + "name": "roadmap_deferred_missing_phase", |
| 513 | + "detail": f"{len(missing_phase)} deferred item(s) missing suggested phase" if missing_phase else "none", |
| 514 | + "items": missing_phase, |
| 515 | + }, |
| 516 | + ] |
| 517 | + |
| 518 | + |
363 | 519 | def audit_payload(target: Path) -> dict[str, Any]: |
364 | 520 | target = target.expanduser().resolve() |
365 | 521 | roadmap = _parse_roadmap(target) |
@@ -400,10 +556,14 @@ def audit_payload(target: Path) -> dict[str, Any]: |
400 | 556 | "commands": missing_docs[:20], |
401 | 557 | } |
402 | 558 | ) |
| 559 | + deferred_items = _deferred_items() |
| 560 | + checks.extend(_deferred_item_checks(deferred_items)) |
403 | 561 | issues = [check for check in checks if check.get("status") != OK] |
404 | 562 | return { |
405 | 563 | "target": str(target), |
406 | 564 | "roadmap": roadmap, |
| 565 | + "deferred_items": deferred_items, |
| 566 | + "deferred_item_count": len(deferred_items), |
407 | 567 | "documented_commands": documented, |
408 | 568 | "normalized_documented_commands": normalized_documented, |
409 | 569 | "cli_commands": cli_commands, |
@@ -463,6 +623,7 @@ def audit(*, target: Path, json_output: bool = False, import_issues: bool = Fals |
463 | 623 | print(f"roadmap audit: {payload['target']}") |
464 | 624 | print(f"roadmap: {payload['roadmap']['path']}") |
465 | 625 | print(f"sections: {len(payload['roadmap']['sections'])}") |
| 626 | + print(f"deferred_items: {payload['deferred_item_count']}") |
466 | 627 | print(f"issues: {payload['issue_count']}") |
467 | 628 | for check in payload["checks"]: |
468 | 629 | print(f"[{check['status']}] {check['name']}: {check['detail']}") |
|
0 commit comments