Skip to content

closed — replaced by #4#2

Closed
AlanKharebov wants to merge 5 commits intoAxsar:mainfrom
AlanKharebov:cortex/suppress-popups-hardcoded-heads
Closed

closed — replaced by #4#2
AlanKharebov wants to merge 5 commits intoAxsar:mainfrom
AlanKharebov:cortex/suppress-popups-hardcoded-heads

Conversation

@AlanKharebov
Copy link
Copy Markdown
Collaborator

Summary

Restores Windows headless behavior and adds Night Shift progress reporting, lost during 2026-03-20 reset.

Popup suppression (W1-W3)

  • New subprocess_utils.py with no_window_flags() helper
  • Applied CREATE_NO_WINDOW to all 24 subprocess calls across 17 files
  • Monkey-patch anyio.open_process in Claude SDK adapter (lazy, only when adapter loads)

Model defaults (W4)

  • Claude SDK adapter fallback changed from claude-opus-4-6 to claude-sonnet-4-6 (matches CLAUDE_MODEL pattern)

Active head guard (W7)

  • Night Shift trigger returns 400 if no heads are active

Progress events (W8-W9)

  • project_start/project_done events during session harvest
  • file_start/file_done/chunk_progress events during extraction stages (entity, topic, event, claim)
  • Progress deque increased from 100 to 5000

Tests

  • Added unit test for no_window_flags()
  • Nano: 1444 passed
  • Micro: 1134 passed, 1 failed (pre-existing flaky test in staleness_e2e)

- Create subprocess_utils.py with no_window_flags() helper
- Apply CREATE_NO_WINDOW to all subprocess.run/Popen calls across 17 files
- Monkey-patch anyio.open_process in Claude SDK adapter for headless ops
- Fix claude_agent_sdk.py default model: use CLAUDE_MODEL env var instead
  of hardcoded claude-opus-4-6
…ess buffer to 5000

- Return 400 if no heads are active when triggering Night Shift
- Increase progress deque from 100 to 5000 to prevent event loss on large runs
…harvest

- Add on_progress callback to harvest_all()
- Emit per-project events with project name, index, total, and file count
…on stages

- Add on_chunk_progress callback to base extractor map_generate()
- All 4 extractors pass chunk progress callback through
- stages_early emits file_start/file_done for each extraction + passes on_progress to harvester
Copilot AI review requested due to automatic review settings March 25, 2026 13:13
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR restores Windows headless behavior by suppressing subprocess pop-up windows, and adds richer Night Shift progress reporting/guardrails that were lost in a prior reset.

Changes:

  • Introduces no_window_flags() and applies it broadly to subprocess launches (plus a Windows-specific anyio monkey-patch in the Claude SDK adapter).
  • Adds Night Shift progress events (project/file/chunk) and increases the in-memory progress buffer size.
  • Adds an API guard to prevent Night Shift runs when no heads are active, and updates Claude SDK adapter defaults.

Reviewed changes

Copilot reviewed 27 out of 27 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
tests/0-nano/test_subprocess_utils.py Adds unit coverage for no_window_flags() behavior.
src/multihead/subprocess_utils.py New helper for cross-platform Windows console popup suppression.
src/multihead/shell/core.py Applies creationflags=no_window_flags() to subprocess call(s).
src/multihead/shell/context.py Applies popup suppression to several git/shell subprocess calls.
src/multihead/session_harvester/harvester.py Adds optional progress callback + emits project_start/project_done.
src/multihead/resource_monitor.py Suppresses popups for nvidia-smi subprocess sampling.
src/multihead/resilience.py Suppresses popups for health-check subprocess call(s).
src/multihead/night_shift/stages_late.py Suppresses popups for git subprocess usage in late stages.
src/multihead/night_shift/stages_early.py Wires harvester progress + emits file/chunk progress events for extraction stages.
src/multihead/narrative/source_extractors/git_extractor.py Suppresses popups for git subprocess calls.
src/multihead/mcp_server/_tools_decompose.py Suppresses popups for decomposition subprocess invocation.
src/multihead/init_wizard/hardware.py Suppresses popups for hardware detection subprocess calls.
src/multihead/github_integration.py Suppresses popups for git/gh subprocess calls.
src/multihead/extractors/topic_assigner.py Plumbs on_chunk_progress into shared extraction path.
src/multihead/extractors/test_results_extractor.py Suppresses popups for test runner subprocess call.
src/multihead/extractors/event_extractor.py Plumbs on_chunk_progress into shared extraction path.
src/multihead/extractors/entity_extractor.py Plumbs on_chunk_progress into shared extraction path.
src/multihead/extractors/claim_extractor.py Plumbs on_chunk_progress into shared extraction path.
src/multihead/extractors/ci_extractor.py Suppresses popups for gh api subprocess calls.
src/multihead/extractors/base.py Adds on_chunk_progress hook and emits progress during sequential generation.
src/multihead/diagnostics.py Suppresses popups for diagnostic subprocess call(s).
src/multihead/claim_corroboration.py Suppresses popups for git subprocess calls.
src/multihead/autonomous_executor/strategies.py Suppresses popups for executor subprocess launches.
src/multihead/api/routes_system.py Adds popup suppression to Windows restart process creation flags.
src/multihead/api/routes_nightshift.py Adds active-head guard, increases progress buffer to 5000.
src/multihead/adapters/claude_agent_sdk.py Changes default model + monkey-patches anyio subprocess creation on Windows.
scripts/claude_worker.py Suppresses popups for worker subprocess calls (Claude + tmux).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.



def test_no_window_flags_windows():
with patch.object(sys, "platform", "win32"):
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On non-Windows platforms, subprocess.CREATE_NO_WINDOW is typically not defined, so this test will raise AttributeError even though sys.platform is patched. Patch/define subprocess.CREATE_NO_WINDOW in the test (e.g., via patch.object(..., create=True)) or assert via getattr(subprocess, "CREATE_NO_WINDOW", ...) so the test suite passes on Linux/macOS.

Suggested change
with patch.object(sys, "platform", "win32"):
with patch.object(sys, "platform", "win32"), patch.object(subprocess, "CREATE_NO_WINDOW", 0, create=True):

Copilot uses AI. Check for mistakes.
def no_window_flags() -> int:
"""Return CREATE_NO_WINDOW on Windows, 0 elsewhere."""
if sys.platform == "win32":
return subprocess.CREATE_NO_WINDOW
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

subprocess.CREATE_NO_WINDOW may not exist on non-Windows interpreters (even if sys.platform is spoofed in tests). Using getattr(subprocess, "CREATE_NO_WINDOW", 0) (or a hasattr check) avoids an AttributeError and makes this helper more robust.

Suggested change
return subprocess.CREATE_NO_WINDOW
return getattr(subprocess, "CREATE_NO_WINDOW", 0)

Copilot uses AI. Check for mistakes.
Comment on lines 49 to 53
return {"status": "already_running"}
_nightshift_status["running"] = True
_nightshift_status["current_stage"] = None
_nightshift_status["progress"] = deque(maxlen=100)
_nightshift_status["progress"] = deque(maxlen=5000)

Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Progress events are appended to _nightshift_status["progress"] (via the debug progress callback) while /status converts the same deque to a list. Mutating a deque during iteration can raise RuntimeError, and (because _emit swallows exceptions) can silently drop progress events. Consider synchronizing both appends and reads (e.g., protect with a lock, or funnel updates through an async task that holds _nightshift_lock).

Copilot uses AI. Check for mistakes.
Comment on lines 31 to +45
concurrency: int = Query(1, description="Parallel LLM calls per stage (1=sequential)"),
) -> dict[str, Any]:
"""Trigger a Night Shift run in the background."""
# Check for at least one active head before starting
head_manager = request.app.state.head_manager
states = head_manager.get_states()
has_active = any(
info.get("state") == HeadState.ACTIVE.value
for info in states.values()
)
if not has_active:
return JSONResponse(
status_code=400,
content={"error": "No active heads — wake a head before running Night Shift"},
)
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This endpoint is annotated to return dict[str, Any] but returns a JSONResponse for the 400 case. To keep the return type consistent and improve generated OpenAPI docs, prefer raising fastapi.HTTPException(status_code=400, detail=...) (or update the return type annotation/response model to include the JSONResponse path).

Copilot uses AI. Check for mistakes.
Comment on lines +274 to +275
"""Build a callback that emits file_start/file_done/chunk_progress events."""
total = len(chunks)
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

total = len(chunks) is assigned but never used, which is easy to miss and may be flagged by linters. Either remove it or use it (and consider aligning the docstring with the implementation: the returned callback currently emits only chunk_progress, not file_start/file_done).

Suggested change
"""Build a callback that emits file_start/file_done/chunk_progress events."""
total = len(chunks)
"""Build a callback that emits chunk_progress events for the given chunks."""

Copilot uses AI. Check for mistakes.
@AlanKharebov AlanKharebov deleted the cortex/suppress-popups-hardcoded-heads branch March 25, 2026 13:21
@AlanKharebov AlanKharebov changed the title fix: suppress subprocess popups, active head guard, Night Shift progress events closed — replaced by #4 Mar 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants