276 lines
13 KiB
Python
276 lines
13 KiB
Python
#!/usr/bin/env python3
|
|
"""Bootstrap AirDbg debugging context files."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
from datetime import datetime, timezone
|
|
from pathlib import Path
|
|
from typing import Dict, Tuple
|
|
|
|
MARKER_BEGIN = "<!-- AIRDBG:BEGIN -->"
|
|
MARKER_END = "<!-- AIRDBG:END -->"
|
|
|
|
|
|
def now_iso() -> str:
|
|
return datetime.now(timezone.utc).isoformat()
|
|
|
|
|
|
def airdbg_agents_block() -> str:
|
|
return f"""{MARKER_BEGIN}
|
|
## AirDbg Debug Workflow
|
|
|
|
1. Use AirDbg for `/airdbg` debugging and repair sessions.
|
|
2. Before fixing, load project context:
|
|
- `AirPlan/AGENTS.md`
|
|
- `AirPlan/docs/architecture/adr/`
|
|
- `AirPlan/docs/architecture/c4/module.md`
|
|
- `AirPlan/docs/debug/debug-log.md`
|
|
3. Reproduce the issue before changing code whenever feasible.
|
|
4. Identify root cause, then apply the smallest verifiable fix.
|
|
5. Record reproduction, root cause, fix, validation, and residual risk in `AirPlan/docs/debug/debug-log.md`.
|
|
6. When debugging needs graphical comparison, screenshots, GUI operation, browser/desktop UI, canvas, layout, focus, popup, or visual evidence, call AirXDB for screenshots, GUI exploration, or operation validation.
|
|
7. For every local or remote GUI test or validation, do not treat process liveness, window launch, command success, or clean logs as a pass; require image/GUI evidence plus at least one GUI operation or state check. If screenshots are not diagnostically useful, such as many embedded-screen or display-pipeline scenarios, record equivalent screen evidence instead.
|
|
8. If the GUI target is a remote device, test machine, VM, server, or SSH host, call the AirXDB remote device helper before local Computer MCP; it should probe SSH, auto-configure missing remote screenshot tools when possible, and save evidence under `AirPlan/docs/debug/airxdb-artifacts/`.
|
|
9. When debugging needs packet capture, pcap reading, BPF filters, DNS/TCP/UDP/TLS/HTTP connectivity, ports, proxy, firewall, packet loss, retransmits, resets, or latency evidence, call AirNDB.
|
|
10. If the network target is a remote device, test machine, VM, container host, server, or SSH host, call the AirNDB remote device helper before local capture; it should probe SSH, auto-configure missing remote tcpdump/dumpcap when possible, and save evidence under `AirPlan/docs/network/airndb-captures/`.
|
|
11. When debugging needs C/C++ static analysis, cppcheck, code quality, security-relevant findings, CWE, null pointer, bounds, resource, conversion, uninitialized-variable evidence, or static-analysis capability to support validation, call AirSDB and read `AirPlan/docs/staticanalysis.md` plus XML/JSON artifacts when helpful.
|
|
12. If the static-analysis target is a remote device, test machine, VM, container host, server, or SSH host, call the AirSDB remote device helper before local cppcheck; it should probe SSH, auto-configure missing remote cppcheck when possible, and maintain `staticanalysis.md`.
|
|
13. Record AirXDB, AirNDB, and AirSDB commands, remote target when applicable, screenshot/pcap/staticanalysis/report paths, observations, and how they relate to root cause in `AirPlan/docs/debug/debug-log.md`.
|
|
14. Update ADR records when a fix changes long-term behavior, contracts, dependencies, data ownership, error handling, GUI automation boundaries, remote-device boundaries, network boundaries, static-analysis boundaries, or architecture decisions.
|
|
15. Update C4 module docs when module boundaries, dependencies, public interfaces, data ownership, GUI automation, local/remote screenshot evidence, local/remote packet capture, static-analysis evidence, network observability, or visual validation boundaries change.
|
|
16. Keep ADRs concise because they are AI context records.
|
|
{MARKER_END}
|
|
"""
|
|
|
|
|
|
def c4_module_template() -> str:
|
|
return """# C4 Module
|
|
|
|
## System Context
|
|
- TODO: Describe the system, users, and important external systems.
|
|
|
|
## Containers
|
|
- TODO: Describe deployable/runtime units.
|
|
|
|
## Modules
|
|
|
|
| Module | Responsibility | Public Interfaces | Dependencies | Data Ownership | Debug-Relevant Notes |
|
|
| --- | --- | --- | --- | --- | --- |
|
|
| TODO | TODO | TODO | TODO | TODO | TODO |
|
|
|
|
## Error and Observability Flow
|
|
- TODO: Describe where errors are raised, logged, retried, surfaced, or recovered.
|
|
|
|
## GUI / Visual Debug Boundaries
|
|
- AirXDB screenshot evidence, GUI operation checks, graphical comparison, or visual validation used in debugging: TODO
|
|
- AirXDB remote device evidence, SSH target, remote screenshot tool, or remote display constraints used in debugging: TODO
|
|
- Browser bridge, desktop control, canvas, focus, popup, layout, or multi-display constraints relevant to defects: TODO
|
|
|
|
## Network / Packet Debug Boundaries
|
|
- AirNDB pcap evidence, tcpdump/WinDump commands, BPF filters, packet summaries, or network validation used in debugging: TODO
|
|
- AirNDB remote device evidence, SSH target, remote tcpdump/dumpcap tool, or remote capture permissions used in debugging: TODO
|
|
- DNS, TCP, UDP, TLS, HTTP, proxy, firewall, port, NAT, WSL/container/VM/host network constraints relevant to defects: TODO
|
|
|
|
## Static Analysis Boundaries
|
|
- AirSDB cppcheck evidence, staticanalysis.md entries, XML/JSON reports, suppressions, or quality gates used in debugging: TODO
|
|
- AirSDB remote device evidence, SSH target, remote cppcheck tool, or remote static-analysis permissions used in debugging: TODO
|
|
|
|
## Change Log
|
|
- TODO: Record module-boundary changes caused by fixes.
|
|
"""
|
|
|
|
|
|
def adr_template() -> str:
|
|
return """# ADR-0001: AirDbg Debug Context Governance
|
|
|
|
- Status: Accepted
|
|
- Date: TODO
|
|
|
|
## Context
|
|
Debugging sessions need durable AI-readable context so future fixes can understand prior decisions.
|
|
|
|
## Decision
|
|
Use AirDbg to maintain `AirPlan/AGENTS.md`, `AirPlan/docs/architecture/c4/module.md`, `AirPlan/docs/architecture/adr/`, and `AirPlan/docs/debug/debug-log.md` during repair work.
|
|
When debugging requires graphical comparison, screenshots, GUI operations, or visual evidence, use AirXDB for evidence gathering and operation validation, then return to AirDbg for root-cause analysis and focused repair. If the GUI target is remote, use AirXDB's remote device helper before local Computer MCP so SSH and remote screenshot tooling are checked or auto-configured.
|
|
Do not treat process liveness, window launch, command success, or clean logs as sufficient proof for a GUI pass. Require image/GUI evidence and a GUI operation or state check for every local or remote GUI validation. If screenshots are not diagnostically useful, such as many embedded-screen or display-pipeline scenarios, record equivalent screen evidence and why screenshots were skipped.
|
|
When debugging requires packet capture, pcap analysis, DNS/TCP/UDP/TLS/HTTP connectivity evidence, ports, proxy, firewall, retransmits, resets, or latency analysis, use AirNDB for bounded network evidence, then return to AirDbg for root-cause analysis and focused repair. If the network target is remote, use AirNDB's remote device helper before local capture so SSH and remote tcpdump/dumpcap are checked or auto-configured.
|
|
When debugging requires C/C++ static analysis, cppcheck, code quality, security-relevant findings, CWE evidence, or static-analysis capability to support validation, use AirSDB for local or remote static-analysis evidence and `staticanalysis.md` context, then return to AirDbg for root-cause analysis and focused repair.
|
|
|
|
## Consequences
|
|
- Fixes carry reproducible context across sessions.
|
|
- Architecture-impacting repairs must update ADR and C4 module docs.
|
|
- ADRs stay concise and focused on decisions.
|
|
- GUI evidence and graphical operation results are linked to root cause and validation records.
|
|
- Embedded-screen or display-pipeline cases that skip screenshots still record equivalent screen evidence and the reason screenshots were not useful.
|
|
- Network packet evidence and pcap summaries are linked to root cause and validation records.
|
|
- Static-analysis evidence and staticanalysis.md summaries are linked to root cause and validation records.
|
|
- Remote-device SSH targets, auto-configuration outcomes, and permission limits are recorded when they affect debugging.
|
|
|
|
## Alternatives
|
|
- Chat-only debugging notes: rejected because context is easy to lose.
|
|
"""
|
|
|
|
|
|
def debug_log_template() -> str:
|
|
return """# Debug Log
|
|
|
|
Append entries for AirDbg sessions.
|
|
|
|
## Entry Template
|
|
|
|
### YYYY-MM-DD: short problem title
|
|
|
|
- Symptom: TODO
|
|
- Expected: TODO
|
|
- Actual: TODO
|
|
- Reproduction: TODO
|
|
- Root cause: TODO
|
|
- AirXDB local/remote evidence: TODO
|
|
- AirNDB local/remote evidence: TODO
|
|
- AirSDB local/remote static-analysis evidence: TODO
|
|
- Fix: TODO
|
|
- Validation: TODO
|
|
- ADR/C4 updates: TODO
|
|
- Residual risk: TODO
|
|
"""
|
|
|
|
|
|
def write_if_missing(path: Path, content: str) -> bool:
|
|
if path.exists():
|
|
return False
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
path.write_text(content, encoding="utf-8", newline="\n")
|
|
return True
|
|
|
|
|
|
def upsert_agents_md(path: Path) -> str:
|
|
block = airdbg_agents_block().rstrip() + "\n"
|
|
|
|
if path.exists():
|
|
original = path.read_text(encoding="utf-8")
|
|
existed = True
|
|
else:
|
|
original = "# AGENTS.md\n\n"
|
|
existed = False
|
|
|
|
begin = original.find(MARKER_BEGIN)
|
|
end = original.find(MARKER_END)
|
|
|
|
if begin >= 0 and end > begin:
|
|
end += len(MARKER_END)
|
|
updated = original[:begin].rstrip() + "\n\n" + block + original[end:].lstrip()
|
|
status = "updated"
|
|
else:
|
|
updated = original.rstrip() + "\n\n" + block
|
|
status = "updated" if existed else "created"
|
|
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
path.write_text(updated, encoding="utf-8", newline="\n")
|
|
return status
|
|
|
|
|
|
def artifact_map(project_root: Path) -> Dict[str, Path]:
|
|
return {
|
|
"AGENTS.md": project_root / "AirPlan" / "AGENTS.md",
|
|
"c4_module": project_root / "AirPlan" / "docs" / "architecture" / "c4" / "module.md",
|
|
"adr_dir": project_root / "AirPlan" / "docs" / "architecture" / "adr",
|
|
"adr_0001": project_root / "AirPlan" / "docs" / "architecture" / "adr" / "ADR-0001-airdbg-debug-context-governance.md",
|
|
"debug_log": project_root / "AirPlan" / "docs" / "debug" / "debug-log.md",
|
|
"state": project_root / "AirPlan" / "state" / "airdbg" / "state.json",
|
|
}
|
|
|
|
|
|
def write_state(path: Path, enabled: bool, project_root: Path) -> None:
|
|
artifacts = artifact_map(project_root)
|
|
health = {
|
|
name: artifacts[name].exists()
|
|
for name in [
|
|
"AGENTS.md",
|
|
"c4_module",
|
|
"adr_dir",
|
|
"adr_0001",
|
|
"debug_log",
|
|
]
|
|
}
|
|
|
|
payload = {
|
|
"enabled": enabled,
|
|
"updatedAt": now_iso(),
|
|
"projectRoot": str(project_root),
|
|
"artifactHealth": health,
|
|
}
|
|
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
path.write_text(json.dumps(payload, indent=2) + "\n", encoding="utf-8")
|
|
|
|
|
|
def enter_mode(project_root: Path) -> Tuple[str, Dict[str, str]]:
|
|
artifacts = artifact_map(project_root)
|
|
results: Dict[str, str] = {}
|
|
|
|
results["AGENTS.md"] = upsert_agents_md(artifacts["AGENTS.md"])
|
|
results["c4_module"] = "created" if write_if_missing(artifacts["c4_module"], c4_module_template()) else "exists"
|
|
artifacts["adr_dir"].mkdir(parents=True, exist_ok=True)
|
|
results["adr_dir"] = "exists"
|
|
results["adr_0001"] = "created" if write_if_missing(artifacts["adr_0001"], adr_template()) else "exists"
|
|
results["debug_log"] = "created" if write_if_missing(artifacts["debug_log"], debug_log_template()) else "exists"
|
|
|
|
write_state(artifacts["state"], True, project_root)
|
|
return "enabled", results
|
|
|
|
|
|
def exit_mode(project_root: Path) -> Tuple[str, Dict[str, str]]:
|
|
artifacts = artifact_map(project_root)
|
|
write_state(artifacts["state"], False, project_root)
|
|
return "disabled", {}
|
|
|
|
|
|
def status_mode(project_root: Path) -> Tuple[str, Dict[str, str]]:
|
|
artifacts = artifact_map(project_root)
|
|
state_file = artifacts["state"]
|
|
|
|
enabled = False
|
|
if state_file.exists():
|
|
try:
|
|
payload = json.loads(state_file.read_text(encoding="utf-8"))
|
|
enabled = bool(payload.get("enabled"))
|
|
except json.JSONDecodeError:
|
|
enabled = False
|
|
|
|
results = {
|
|
name: ("ok" if path.exists() else "missing")
|
|
for name, path in artifacts.items()
|
|
if name != "state"
|
|
}
|
|
return ("enabled" if enabled else "disabled"), results
|
|
|
|
|
|
def parse_args() -> argparse.Namespace:
|
|
parser = argparse.ArgumentParser(description="Manage AirDbg project artifacts.")
|
|
parser.add_argument("--mode", choices=["enter", "exit", "status"], default="enter")
|
|
parser.add_argument("--project", default=".")
|
|
return parser.parse_args()
|
|
|
|
|
|
def main() -> None:
|
|
args = parse_args()
|
|
project_root = Path(args.project).expanduser().resolve()
|
|
|
|
if args.mode == "enter":
|
|
mode_state, result = enter_mode(project_root)
|
|
elif args.mode == "exit":
|
|
mode_state, result = exit_mode(project_root)
|
|
else:
|
|
mode_state, result = status_mode(project_root)
|
|
|
|
print(f"airdbg_mode={mode_state}")
|
|
print(f"project_root={project_root}")
|
|
for key, value in result.items():
|
|
print(f"{key}={value}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|