generate: run until stopped when no --count is given (cangen-style)#486
Conversation
`canarchy generate <interface>` with no `--count` now streams frames continuously, spaced by `--gap`, until interrupted (Ctrl-C), matching `cangen` from can-utils. `--count N` keeps its existing exact-N behavior. - `iter_generated_frames`/`LocalTransport.generate_stream_events` stream frames one at a time instead of materializing an unbounded list, and the CLI dispatches unbounded live runs to a streaming emitter (mirrors the existing `capture`/`gateway --text` pattern) so SIGINT exits cleanly. - `--dry-run` without `--count` plans a single frame rather than requiring or attempting an unbounded plan. - Entry points that can't deliver Ctrl-C (the MCP server, calling `execute_command` directly) get a structured `MISSING_COUNT` error instead of hanging on an unbounded live run. Fixes #477. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TAw8L4vcLP5uBEJxVHivxJ
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TAw8L4vcLP5uBEJxVHivxJ
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 31e67e54de
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| def emit_live_generate(args: argparse.Namespace, output_format: str) -> int: | ||
| """Stream generated frames until Ctrl+C — used when `generate` has no `--count`.""" | ||
| transport = LocalTransport() | ||
| enforce_active_transmit_safety(args) |
There was a problem hiding this comment.
Preserve structured acknowledgement failures
When --count is omitted, main() calls this emitter outside execute_command()'s exception wrapper. If active acknowledgement is required, or if --ack-active receives anything other than YES, enforce_active_transmit_safety() raises CommandError here before the try block and it escapes as a traceback instead of the documented ACTIVE_ACK_REQUIRED or ACTIVE_CONFIRMATION_DECLINED result envelope. Catch and emit this error at the streaming boundary so active-safety failures remain actionable and machine-readable.
AGENTS.md reference: AGENTS.md:L359-L370
Useful? React with 👍 / 👎.
| "count": { | ||
| "type": "integer", | ||
| "description": "Number of frames to generate", | ||
| "description": ( | ||
| "Number of frames to generate. Defaults to 1 for a dry-run plan; " | ||
| "required (and must be finite) when `dry_run=false`, since this " |
There was a problem hiding this comment.
Document the changed MCP generate contract
Update both AGENTS.md and docs/agents.md for this changed MCP workflow: a live generate call without count now returns MISSING_COUNT, whereas the existing agent guide only says active tools default to dry-run and does not tell agents that count becomes mandatory when they opt into dry_run=false. This command-surface and MCP-tool change does not meet the repository's required agent-guide acceptance gate.
AGENTS.md reference: AGENTS.md:L82-L90
Useful? React with 👍 / 👎.
Summary
canarchy generate <interface>with no--countsent a single frame and exited. The reference toolcangen(can-utils) streams continuously by default, so this changes--countto default to unbounded: omitting it now generates and transmits frames continuously, spaced by--gap, until interrupted (Ctrl-C / SIGINT).Closes #477
Changes
canarchy generate <interface>with no--countnow streams frames continuously until Ctrl-C, matchingcangen;--count Nkeeps its existing exact-N-frames behavior.iter_generated_frames/LocalTransport.generate_stream_events) instead of materializing an unbounded list in memory.emit_live_generate), mirroring the existingcapture/gateway --textpattern, so text and JSON/JSONL output both stream per-frame and SIGINT exits cleanly (exit 0).--dry-runwithout--countplans a single frame rather than requiring or attempting an unbounded plan (preserves existing MCPdry_rundefault behavior).execute_commanddirectly — get a structuredMISSING_COUNTerror instead of hanging on an unbounded live run.Test plan
uv run pytest tests/test_cli.py tests/test_transport.py tests/test_mcp.py -q— 620 passed, 1 skipped, 24 subtests passeduv run ruff check/uv run ruff format --checkgenerate --text/--jsonstreams frames and exits 0 on SIGINT (timeout --preserve-status -s INT);--dry-runwithout--countplans exactly 1 frame;--count 3unchangedMISSING_COUNTguardDocumentation
CHANGELOG.mdupdated under[Unreleased]docs/command_spec.mdupdated (command surface changed)docs/design/spec updated (EARS syntax) —docs/design/generate-command.mddocs/tests/spec updated (Gherkin Given/When/Then) —docs/tests/generate-command.mddocs/event-schema.mdupdated — not needed, event shape is unchangedAGENTS.md/docs/agents.mdupdated — not needed, MCP tool contract description already covers thismkdocs.ymlnav updated — no new docs pages addedSafety
--ack-active(unchanged)SECURITY.mdreviewed against the new behaviour — no change needed; unbounded generation is opt-in (same as omitting--countalways was) and still gated by the existing active-transmit confirmation modelGenerated by Claude Code