From 2a5a3701851829d18e78be7bcb5ba6ca24a7d88c Mon Sep 17 00:00:00 2001 From: AIOSAI Date: Wed, 1 Jul 2026 06:12:02 -0700 Subject: [PATCH] fix(drone): --json pass-through uses sys.stdout.write, no Rich mid-string wrap (td-49) --json was routed through Rich console.print(), which defaults to width 80 on a non-TTY and hard-wraps mid-string, producing invalid JSON (e.g. 'Security \nScan'). Write raw JSON with sys.stdout.write() in the pass-through paths (drone.py + router.py); keep Rich for drone's own human UI. Verified live. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01EccxENcB3KtyT9XuT4ybPz --- CHANGELOG.md | 16 ++++++++++++++++ src/aipass/drone/.seedgo/bypass.json | 13 ++++++++----- src/aipass/drone/apps/drone.py | 16 ++++++++-------- src/aipass/drone/apps/modules/router.py | 6 ++++-- 4 files changed, 36 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 93e93557..5080eb1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,22 @@ PyPI version — not the changelog header. to it). Hooks tests 66 green (presence_gate / cc_sessions / session_boot), telegram presence_pointer 42 green. (DPLAN-0226 P1/P2, FPLAN-0290/0291/0292) +- **Seedgo stale-template audit checker (`template_check`)** — a new advisory + standard that flags branches still carrying unrendered template markers in + their local prompts / config, so a citizen that never customized its scaffold + no longer fails silently. Auto-discovered like every other checker; advisory + (warns, never blocks). Ships with `template_content.py` and a `template.md` + standard doc, covered by `test_template_check.py`. (built by @seedgo, DPLAN-0228) + +### Fixed + +- **Drone `--json` output no longer corrupts machine JSON** — `--json` + pass-through was routed through Rich's `console.print()`, which defaults to + width 80 on a non-TTY and hard-wraps mid-string, producing invalid JSON + (e.g. `"Security \nScan"`). Fixed by writing raw JSON with `sys.stdout.write()` + in the pass-through paths (`drone.py` + `router.py`) while keeping Rich for + drone's own human UI. Verified live end-to-end. (fixed by @drone, td-49) + ### Changed - **Presence gate re-sourced to CC-native session files (presence_gate v2)** — diff --git a/src/aipass/drone/.seedgo/bypass.json b/src/aipass/drone/.seedgo/bypass.json index 7f4124ef..c0e6f90d 100644 --- a/src/aipass/drone/.seedgo/bypass.json +++ b/src/aipass/drone/.seedgo/bypass.json @@ -8,11 +8,14 @@ { "file": "apps/drone.py", "standard": "cli", - "lines": [ - 166, - 168 - ], - "reason": "Raw passthrough of module subprocess stdout/stderr — console.print() breaks routed output" + "lines": [244, 246, 360, 362, 479, 481, 585, 587], + "reason": "Raw passthrough of routed command stdout/stderr — console.print() wraps at 80 cols when piped, corrupting JSON and machine output" + }, + { + "file": "apps/modules/router.py", + "standard": "cli", + "lines": [62], + "reason": "Raw passthrough of routed command stdout — console.print() wraps at 80 cols when piped, corrupting JSON and machine output" }, { "file": "apps/handlers/scanning/formatters.py", diff --git a/src/aipass/drone/apps/drone.py b/src/aipass/drone/apps/drone.py index ecf3386e..8694fda1 100644 --- a/src/aipass/drone/apps/drone.py +++ b/src/aipass/drone/apps/drone.py @@ -241,9 +241,9 @@ def _handle_module(name: str, args: List[str]) -> int: return 1 if result.get("stdout"): - console.print(result["stdout"], end="", highlight=False, markup=False) + sys.stdout.write(result["stdout"]) if result.get("stderr"): - err_console.print(result["stderr"], end="", highlight=False, markup=False) + sys.stderr.write(result["stderr"]) return result.get("exit_code", 0) @@ -357,9 +357,9 @@ def _handle_custom_command(args: list[str]) -> int: return 1 if result.stdout: - console.print(result.stdout, end="", highlight=False) + sys.stdout.write(result.stdout) if result.stderr: - err_console.print(result.stderr, end="", highlight=False) + sys.stderr.write(result.stderr) return result.exit_code @@ -476,9 +476,9 @@ def _handle_target(args: List[str]) -> int: return 1 if result.stdout: - console.print(result.stdout, end="", highlight=False) + sys.stdout.write(result.stdout) if result.stderr: - err_console.print(result.stderr, end="", highlight=False) + sys.stderr.write(result.stderr) return result.exit_code @@ -582,9 +582,9 @@ def main() -> int: return 1 if isinstance(result, dict): if result.get("stdout"): - console.print(result["stdout"], end="", highlight=False) + sys.stdout.write(result["stdout"]) if result.get("stderr"): - err_console.print(result["stderr"], end="", highlight=False) + sys.stderr.write(result["stderr"]) return result.get("exit_code", 0) return 0 if result else 1 diff --git a/src/aipass/drone/apps/modules/router.py b/src/aipass/drone/apps/modules/router.py index 9c994d40..68a30191 100644 --- a/src/aipass/drone/apps/modules/router.py +++ b/src/aipass/drone/apps/modules/router.py @@ -14,6 +14,7 @@ to the handler layer. """ import os +import sys from pathlib import Path from typing import Dict, List, Optional @@ -58,7 +59,7 @@ def handle_command(command: Optional[str] = None, args: Optional[List[str]] = No cmd_args = args[2:] if len(args) > 2 else None result = route_command(target, cmd, args=cmd_args) if result.stdout: - console.print(result.stdout, highlight=False) + sys.stdout.write(result.stdout) return result.exit_code == 0 if command == "route_all": if not args: @@ -132,7 +133,8 @@ def print_introspection(): console.print("[yellow]Connected Handlers:[/yellow]") console.print(" [cyan]handlers/[/cyan]") console.print( - " - [cyan]router_handler.py[/cyan] [dim](execute_branch_command — resolves and executes branch commands)[/dim]" + " - [cyan]router_handler.py[/cyan] " + "[dim](execute_branch_command — resolves and executes branch commands)[/dim]" ) console.print(" - [cyan]executor.py[/cyan] [dim](CommandResult — subprocess execution result dataclass)[/dim]") console.print()