diff --git a/CHANGELOG.md b/CHANGELOG.md
index 22c086933..95b36f79a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ### Fixed
 
+- `apm compile --watch` now honors `targets: [claude, cursor]` (and every other multi-target / single-target configuration) on every recompile. Previously the watch path bypassed the resolver the one-shot path uses and let `CompilationConfig.from_apm_yml` fall back to the all-families default, silently regenerating `GEMINI.md` after every file edit. The watch path now resolves the effective target via the same helper the one-shot path uses and forwards it as `target=` into both the initial compile and every debounced recompile. (#1345)
 - Fixed hook commands using relative paths that break for Claude target (#1310)
 - Fixed target not propagating to intermediate CompilationConfig during compilation (#765)
 - Fixed direct GitHub API and ADO/GHES `git ls-remote` calls not respecting `PROXY_REGISTRY_ONLY` mode; all four validation code paths (virtual package, GitHub.com API, ADO/GHES git, and parse-failure fallback) now skip outbound network probes and return `True` when proxy-only mode is active. (#615)
diff --git a/src/apm_cli/commands/compile/cli.py b/src/apm_cli/commands/compile/cli.py
index 530ff3dd3..5dd10cf8f 100644
--- a/src/apm_cli/commands/compile/cli.py
+++ b/src/apm_cli/commands/compile/cli.py
@@ -1,10 +1,16 @@
 """APM compile command CLI."""
 
+from __future__ import annotations
+
 import sys
-from pathlib import Path  # noqa: F401
+from pathlib import Path
+from typing import TYPE_CHECKING
 
 import click
 
+if TYPE_CHECKING:
+    from ...core.target_detection import CompileTargetType
+
 from ...compilation import AgentsCompiler, CompilationConfig
 from ...constants import AGENTS_MD_FILENAME, APM_DIR, APM_MODULES_DIR, APM_YML_FILENAME
 from ...core.command_logger import CommandLogger
@@ -252,6 +258,66 @@ def _family_of(name: str) -> str | None:
     return target  # single string pass-through
 
 
+def _resolve_effective_target(
+    target: str | list[str] | None,
+) -> tuple[CompileTargetType, str, str | list[str] | None]:
+    """Resolve the CLI --target arg to the compiler-understood effective target.
+
+    Mirrors the resolution the one-shot compile path performs (load
+    apm.yml ``target:`` / ``targets:``, run :func:`_resolve_compile_target`
+    on both, fall back to :func:`detect_target` for the auto-detect case)
+    so the watch path can build ``CompilationConfig`` with the same
+    ``target=`` value the one-shot path uses (#1345).
+
+    Args:
+        target: The raw ``--target`` CLI argument (None, str, or list).
+
+    Returns:
+        Tuple ``(effective_target, detection_reason, config_target)`` where
+        ``effective_target`` is what to pass as ``target=`` to
+        :meth:`CompilationConfig.from_apm_yml`, ``detection_reason`` is the
+        provenance label, and ``config_target`` is the raw apm.yml value
+        (str | list | None) for user-facing label rendering.
+    """
+    from ...core.target_detection import detect_target
+    from ...models.apm_package import APMPackage
+
+    config_target = None
+    apm_yml_path = Path(APM_YML_FILENAME)
+    if apm_yml_path.exists():
+        apm_pkg = APMPackage.from_apm_yml(apm_yml_path)
+        config_target = apm_pkg.target
+        if config_target is None:
+            try:
+                from ...core.apm_yml import parse_targets_field
+                from ...utils.yaml_io import load_yaml
+
+                _raw = load_yaml(apm_yml_path)
+                if isinstance(_raw, dict):
+                    _yaml_targets = parse_targets_field(_raw)
+                    if _yaml_targets:
+                        config_target = (
+                            _yaml_targets[0] if len(_yaml_targets) == 1 else _yaml_targets
+                        )
+            except Exception:
+                pass
+
+    compile_target = _resolve_compile_target(target)
+    compile_config_target = _resolve_compile_target(config_target)
+
+    if isinstance(compile_target, frozenset):
+        return compile_target, "explicit --target flag", config_target
+    if isinstance(compile_config_target, frozenset) and compile_target is None:
+        return compile_config_target, "apm.yml target", config_target
+
+    detected_target, detection_reason = detect_target(
+        project_root=Path("."),
+        explicit_target=compile_target,
+        config_target=compile_config_target if isinstance(compile_config_target, str) else None,
+    )
+    return detected_target, detection_reason, config_target
+
+
 @click.command(help="Compile APM context into distributed AGENTS.md files")
 @click.option(
     "--output",
@@ -455,7 +521,20 @@ def compile(
 
         # Watch mode
         if watch:
-            _watch_mode(output, chatmode, no_links, dry_run, verbose=verbose)
+            # Resolve the same effective target the one-shot path uses so
+            # `targets: [claude, cursor]` does not silently regress to the
+            # all-families fanout on every recompile (#1345).
+            effective_target, _detection_reason, config_target = _resolve_effective_target(target)
+            _watch_mode(
+                output,
+                chatmode,
+                no_links,
+                dry_run,
+                verbose=verbose,
+                effective_target=effective_target,
+                target_label_user=target,
+                target_label_config=config_target,
+            )
             return
 
         logger.start("Starting context compilation...", symbol="cogs")
diff --git a/src/apm_cli/commands/compile/watcher.py b/src/apm_cli/commands/compile/watcher.py
index f5b0253a9..612fd4cfb 100644
--- a/src/apm_cli/commands/compile/watcher.py
+++ b/src/apm_cli/commands/compile/watcher.py
@@ -1,112 +1,185 @@
 """APM compile watch mode."""
 
+from __future__ import annotations
+
 import time
+from typing import TYPE_CHECKING, Any
 
 from ...compilation import AgentsCompiler, CompilationConfig
 from ...constants import AGENTS_MD_FILENAME, APM_DIR, APM_YML_FILENAME
 from ...core.command_logger import CommandLogger
 
+if TYPE_CHECKING:
+    from ...core.target_detection import CompileTargetType
+
+
+def _format_target_label(
+    effective_target: CompileTargetType | None,
+    target_label_user: str | list[str] | None,
+    target_label_config: str | list[str] | None,
+) -> str | None:
+    """Render a one-shot-parity 'Compiling for ...' label for the watch path.
+
+    Mirrors the family-aware label the one-shot compile path emits so the
+    user sees the same string in watch mode (#1345).
+    """
+    from ...core.target_detection import (
+        get_target_description,
+        should_compile_agents_md,
+        should_compile_claude_md,
+        should_compile_gemini_md,
+    )
+
+    if isinstance(effective_target, frozenset):
+        if isinstance(target_label_user, list):
+            source = f"--target {','.join(target_label_user)}"
+        elif isinstance(target_label_config, list):
+            source = f"apm.yml target: [{', '.join(target_label_config)}]"
+        else:
+            source = "multi-target"
+        parts = []
+        if should_compile_agents_md(effective_target):
+            parts.append("AGENTS.md")
+        if should_compile_claude_md(effective_target):
+            parts.append("CLAUDE.md")
+        if should_compile_gemini_md(effective_target):
+            parts.append("GEMINI.md")
+        return f"Compiling for {' + '.join(parts)} ({source})"
+    if effective_target is None:
+        return None
+    return f"Compiling for {get_target_description(effective_target)}"
+
+
+class APMFileHandler:
+    """Watchdog file-system handler that recompiles APM context on edits.
+
+    Defined at module scope (rather than inside ``_watch_mode``) so unit
+    tests can instantiate it without spinning up a watchdog ``Observer``
+    -- the regression for #1345 lives entirely in the ``from_apm_yml``
+    call site this class owns.
+    """
+
+    def __init__(
+        self,
+        output: str,
+        chatmode: str | None,
+        no_links: bool,
+        dry_run: bool,
+        logger: CommandLogger,
+        effective_target: CompileTargetType | None = None,
+    ) -> None:
+        self.output = output
+        self.chatmode = chatmode
+        self.no_links = no_links
+        self.dry_run = dry_run
+        self.logger = logger
+        self.effective_target = effective_target
+        self.last_compile = 0.0
+        self.debounce_delay = 1.0  # 1 second debounce
+
+    def on_modified(self, event: Any) -> None:
+        if getattr(event, "is_directory", False):
+            return
+        src_path = getattr(event, "src_path", "")
+        if not src_path.endswith(".md") and not src_path.endswith(APM_YML_FILENAME):
+            return
+        current_time = time.time()
+        if current_time - self.last_compile < self.debounce_delay:
+            return
+        self.last_compile = current_time
+        self._recompile(src_path)
 
-def _watch_mode(output, chatmode, no_links, dry_run, verbose=False):
-    """Watch for changes in .apm/ directories and auto-recompile."""
+    def _recompile(self, changed_file: str) -> None:
+        """Recompile after a file change, honoring the resolved target."""
+        try:
+            self.logger.progress(f"File changed: {changed_file}", symbol="eyes")
+            self.logger.progress("Recompiling...", symbol="gear")
+
+            config = CompilationConfig.from_apm_yml(
+                output_path=self.output if self.output != AGENTS_MD_FILENAME else None,
+                chatmode=self.chatmode,
+                resolve_links=not self.no_links if self.no_links else None,
+                dry_run=self.dry_run,
+                target=self.effective_target,
+            )
+
+            compiler = AgentsCompiler(".")
+            result = compiler.compile(config, logger=self.logger)
+
+            if result.success:
+                if self.dry_run:
+                    self.logger.success("Recompilation successful (dry run)", symbol="sparkles")
+                else:
+                    self.logger.success(f"Recompiled to {result.output_path}", symbol="sparkles")
+            else:
+                self.logger.error("Recompilation failed")
+                for error in result.errors:
+                    self.logger.error(f"  {error}")
+
+        except Exception as e:
+            self.logger.error(f"Error during recompilation: {e}")
+
+
+def _watch_mode(
+    output: str,
+    chatmode: str | None,
+    no_links: bool,
+    dry_run: bool,
+    verbose: bool = False,
+    effective_target: CompileTargetType | None = None,
+    target_label_user: str | list[str] | None = None,
+    target_label_config: str | list[str] | None = None,
+) -> None:
+    """Watch for changes in .apm/ directories and auto-recompile.
+
+    ``effective_target`` is the compiler-understood target resolved by
+    :func:`apm_cli.commands.compile.cli._resolve_effective_target` (the
+    same resolver the one-shot path uses) and is forwarded as ``target=``
+    into every :meth:`CompilationConfig.from_apm_yml` call so watch mode
+    honors ``targets: [claude, cursor]`` instead of silently fanning out
+    to all families on every recompile (#1345).
+    """
     logger = CommandLogger("compile-watch", verbose=verbose, dry_run=dry_run)
 
     try:
-        # Try to import watchdog for file system monitoring
         from pathlib import Path
 
         from watchdog.events import FileSystemEventHandler
         from watchdog.observers import Observer
 
-        class APMFileHandler(FileSystemEventHandler):
-            def __init__(self, output, chatmode, no_links, dry_run, logger):
-                self.output = output
-                self.chatmode = chatmode
-                self.no_links = no_links
-                self.dry_run = dry_run
-                self.logger = logger
-                self.last_compile = 0
-                self.debounce_delay = 1.0  # 1 second debounce
-
-            def on_modified(self, event):
-                if event.is_directory:
-                    return
-                # Only react to relevant files
-                if not event.src_path.endswith(".md") and not event.src_path.endswith(
-                    APM_YML_FILENAME
-                ):
-                    return
-                # Debounce rapid changes
-                current_time = time.time()
-                if current_time - self.last_compile < self.debounce_delay:
-                    return
-
-                self.last_compile = current_time
-                self._recompile(event.src_path)
-
-            def _recompile(self, changed_file):
-                """Recompile after file change."""
-                try:
-                    self.logger.progress(f"File changed: {changed_file}", symbol="eyes")
-                    self.logger.progress("Recompiling...", symbol="gear")
-
-                    # Create configuration from apm.yml with overrides
-                    config = CompilationConfig.from_apm_yml(
-                        output_path=self.output if self.output != AGENTS_MD_FILENAME else None,
-                        chatmode=self.chatmode,
-                        resolve_links=not self.no_links if self.no_links else None,
-                        dry_run=self.dry_run,
-                    )
-
-                    # Create compiler and compile
-                    compiler = AgentsCompiler(".")
-                    result = compiler.compile(config, logger=self.logger)
-
-                    if result.success:
-                        if self.dry_run:
-                            self.logger.success(
-                                "Recompilation successful (dry run)", symbol="sparkles"
-                            )
-                        else:
-                            self.logger.success(
-                                f"Recompiled to {result.output_path}", symbol="sparkles"
-                            )
-                    else:
-                        self.logger.error("Recompilation failed")
-                        for error in result.errors:
-                            self.logger.error(f"  {error}")
-
-                except Exception as e:
-                    self.logger.error(f"Error during recompilation: {e}")
-
-        # Set up file watching
-        event_handler = APMFileHandler(output, chatmode, no_links, dry_run, logger)
+        # Adapt the test-friendly module-level handler to watchdog's
+        # FileSystemEventHandler base so Observer.schedule() accepts it.
+        class _WatchdogAdapter(APMFileHandler, FileSystemEventHandler):
+            pass
+
+        event_handler = _WatchdogAdapter(
+            output,
+            chatmode,
+            no_links,
+            dry_run,
+            logger,
+            effective_target=effective_target,
+        )
         observer = Observer()
 
-        # Watch patterns for APM files
         watch_paths = []
 
-        # Check for .apm directory
         if Path(APM_DIR).exists():
             observer.schedule(event_handler, APM_DIR, recursive=True)
             watch_paths.append(f"{APM_DIR}/")
 
-        # Check for .github/instructions and agents/chatmodes
         if Path(".github/instructions").exists():
             observer.schedule(event_handler, ".github/instructions", recursive=True)
             watch_paths.append(".github/instructions/")
 
-        # Watch .github/agents/ (new standard)
         if Path(".github/agents").exists():
             observer.schedule(event_handler, ".github/agents", recursive=True)
             watch_paths.append(".github/agents/")
 
-        # Watch .github/chatmodes/ (legacy)
         if Path(".github/chatmodes").exists():
             observer.schedule(event_handler, ".github/chatmodes", recursive=True)
             watch_paths.append(".github/chatmodes/")
 
-        # Watch apm.yml if it exists
         if Path(APM_YML_FILENAME).exists():
             observer.schedule(event_handler, ".", recursive=False)
             watch_paths.append(APM_YML_FILENAME)
@@ -116,12 +189,17 @@ def _recompile(self, changed_file):
             logger.progress("Run 'apm init' to create an APM project")
             return
 
-        # Start watching
         observer.start()
         logger.progress(f" Watching for changes in: {', '.join(watch_paths)}", symbol="eyes")
         logger.progress("Press Ctrl+C to stop watching...", symbol="info")
 
-        # Do initial compilation
+        # Surface the same family-aware label the one-shot path prints so
+        # users see at-a-glance which AGENTS / CLAUDE / GEMINI files watch
+        # mode will (re)write (#1345).
+        label = _format_target_label(effective_target, target_label_user, target_label_config)
+        if label:
+            logger.progress(label, symbol="gear")
+
         logger.progress("Performing initial compilation...", symbol="gear")
 
         config = CompilationConfig.from_apm_yml(
@@ -129,6 +207,7 @@ def _recompile(self, changed_file):
             chatmode=chatmode,
             resolve_links=not no_links if no_links else None,
             dry_run=dry_run,
+            target=effective_target,
         )
 
         compiler = AgentsCompiler(".")
diff --git a/tests/unit/commands/compile/__init__.py b/tests/unit/commands/compile/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/unit/commands/compile/test_watch_target_forwarding.py b/tests/unit/commands/compile/test_watch_target_forwarding.py
new file mode 100644
index 000000000..816f2193f
--- /dev/null
+++ b/tests/unit/commands/compile/test_watch_target_forwarding.py
@@ -0,0 +1,121 @@
+"""Regression tests for #1345: watch mode must forward the resolved target.
+
+The non-watch ``apm compile`` path correctly omits ``GEMINI.md`` when
+``apm.yml`` declares ``targets: [claude, cursor]`` (#1019/#1074). Before
+this fix, the watch path bypassed target resolution and let
+``CompilationConfig.from_apm_yml`` fall back to the all-families default,
+silently regenerating ``GEMINI.md`` on every recompile.
+
+These tests pin the contract:
+
+1. ``APMFileHandler._recompile`` calls ``CompilationConfig.from_apm_yml``
+   with ``target=<effective_target>``.
+2. The captured target is the exact frozenset the resolver produced --
+   and ``should_compile_gemini_md`` returns False for it, so the bug
+   cannot silently come back.
+3. When no target is configured (``effective_target=None``), the watcher
+   forwards ``None`` and lets ``from_apm_yml`` keep its dataclass default.
+"""
+
+from __future__ import annotations
+
+from types import SimpleNamespace
+from unittest.mock import MagicMock, patch
+
+import pytest
+
+from apm_cli.commands.compile.watcher import APMFileHandler
+from apm_cli.core.target_detection import should_compile_gemini_md
+
+
+@pytest.fixture
+def fake_logger():
+    return SimpleNamespace(
+        progress=MagicMock(),
+        success=MagicMock(),
+        error=MagicMock(),
+        warning=MagicMock(),
+    )
+
+
+def _make_handler(fake_logger, effective_target):
+    return APMFileHandler(
+        output="AGENTS.md",
+        chatmode=None,
+        no_links=False,
+        dry_run=False,
+        logger=fake_logger,
+        effective_target=effective_target,
+    )
+
+
+def test_recompile_forwards_frozenset_target(fake_logger):
+    """`targets: [claude, cursor]` -> frozenset({'claude','agents'}) is forwarded."""
+    effective = frozenset({"claude", "agents"})
+    handler = _make_handler(fake_logger, effective)
+
+    with (
+        patch(
+            "apm_cli.commands.compile.watcher.CompilationConfig.from_apm_yml"
+        ) as mock_from_apm_yml,
+        patch("apm_cli.commands.compile.watcher.AgentsCompiler") as mock_compiler_cls,
+    ):
+        mock_from_apm_yml.return_value = MagicMock()
+        mock_compiler_cls.return_value.compile.return_value = SimpleNamespace(
+            success=True, output_path="AGENTS.md", errors=[]
+        )
+
+        handler._recompile("dummy.md")
+
+    assert mock_from_apm_yml.call_count == 1
+    captured_kwargs = mock_from_apm_yml.call_args.kwargs
+    assert "target" in captured_kwargs, (
+        "Watcher must forward target= to CompilationConfig.from_apm_yml; "
+        "missing it is the #1345 regression."
+    )
+    assert captured_kwargs["target"] == effective
+
+    # Outcome assertion: no GEMINI.md is emitted for this target. If a
+    # future change reintroduces the all-families fanout, this fails.
+    assert should_compile_gemini_md(captured_kwargs["target"]) is False
+
+
+def test_recompile_forwards_none_when_no_target_configured(fake_logger):
+    """Auto-detect / unset target case: forward None, no surprise override."""
+    handler = _make_handler(fake_logger, None)
+
+    with (
+        patch(
+            "apm_cli.commands.compile.watcher.CompilationConfig.from_apm_yml"
+        ) as mock_from_apm_yml,
+        patch("apm_cli.commands.compile.watcher.AgentsCompiler") as mock_compiler_cls,
+    ):
+        mock_from_apm_yml.return_value = MagicMock()
+        mock_compiler_cls.return_value.compile.return_value = SimpleNamespace(
+            success=True, output_path="AGENTS.md", errors=[]
+        )
+
+        handler._recompile("dummy.md")
+
+    assert mock_from_apm_yml.call_args.kwargs["target"] is None
+
+
+def test_recompile_forwards_single_string_target(fake_logger):
+    """Single-target case (e.g. `--target claude`) is forwarded verbatim."""
+    handler = _make_handler(fake_logger, "claude")
+
+    with (
+        patch(
+            "apm_cli.commands.compile.watcher.CompilationConfig.from_apm_yml"
+        ) as mock_from_apm_yml,
+        patch("apm_cli.commands.compile.watcher.AgentsCompiler") as mock_compiler_cls,
+    ):
+        mock_from_apm_yml.return_value = MagicMock()
+        mock_compiler_cls.return_value.compile.return_value = SimpleNamespace(
+            success=True, output_path="AGENTS.md", errors=[]
+        )
+
+        handler._recompile("dummy.md")
+
+    assert mock_from_apm_yml.call_args.kwargs["target"] == "claude"
+    assert should_compile_gemini_md("claude") is False
