Skip to content

Data-driven integration dispatch: replace if-chain with target × primitive loop#494

Merged
danielmeppiel merged 9 commits intomainfrom
copilot/data-driven-integration-dispatch
Mar 30, 2026
Merged

Data-driven integration dispatch: replace if-chain with target × primitive loop#494
danielmeppiel merged 9 commits intomainfrom
copilot/data-driven-integration-dispatch

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 30, 2026

Description

The ~200-line if-chain in _integrate_package_primitives() used boolean flags (integrate_vscode, integrate_claude, integrate_opencode) to gate per-target method calls. Missing guards caused --target opencode to write to .github/ (#470). The integrators themselves had 20+ near-identical per-target methods (70–95% boilerplate), and path data was triple-encoded across KNOWN_TARGETS, the dispatch chain, and method bodies.

This PR makes integrators consume TargetProfile directly, collapsing per-target methods into parameterized ones. KNOWN_TARGETS becomes the single source of truth.

Integrators

Each file integrator gains integrate_*_for_target(target: TargetProfile, ...) and sync_for_target(target, ...). Old per-target methods become thin wrappers for backward compat.

  • CommandIntegrator: 2 integrate + 2 sync → 1 parameterized each
  • AgentIntegrator: 4 integrate + 4 sync + 3 filename → 1 each. Multi-target auto-copy removed from integrate_package_agents(); dispatch loop handles each target independently.
  • InstructionIntegrator: 2 integrate + 2 sync → 1 each. format_id selects content transform (cursor_rules_convert_to_cursor_rules, else identity).
  • HookIntegrator: Thin integrate_hooks_for_target() dispatches by target.name (genuine algorithmic diversity preserved).
  • PromptIntegrator: Thin wrappers added for uniform dispatch.

Dispatch (install.py)

# Before: boolean flags + 200-line if-chain
_integrate_package_primitives(..., integrate_vscode=True, integrate_claude=False, ...)

# After: target list + 60-line loop
_integrate_package_primitives(..., targets=_targets, ...)
# Loop: for target in targets → for primitive in target.primitives → dispatch

Uninstall (engine.py)

Phase 1 sync and Phase 2 re-integration both use target-driven loops over KNOWN_TARGETS / active_targets() instead of per-integrator calls with should_integrate_claude().

Partition (base_integrator.py)

partition_managed_files() generates buckets dynamically from KNOWN_TARGETS with backward-compat aliases (agents_copilotagents_github, instructions_cursorrules_cursor, etc.). Adding a target or primitive auto-creates the bucket.

Type of change

  • Bug fix
  • New feature
  • Documentation
  • Maintenance / refactor

Testing

  • Tested locally
  • All existing tests pass
  • Added tests for new functionality (if applicable)

9 new tests added in test_data_driven_dispatch.py:

  • Target-gating regression (5): opencode-only skips .github/, cursor-only skips .claude/, etc.
  • Exhaustiveness (2): every (target, primitive) has a dispatch path; dynamic partition matches old hardcoded bucket keys
  • Synthetic target (2): hand-built TargetProfile with custom root_dir works with real integrators — proves adding a target needs no code changes

Copilot AI and others added 6 commits March 30, 2026 19:48
Agent-Logs-Url: https://github.com/microsoft/apm/sessions/12c01ced-bc96-4e31-a0fd-b6e147a0e167

Co-authored-by: danielmeppiel <51440732+danielmeppiel@users.noreply.github.com>
…t, Instruction, Hook, Prompt)

Agent-Logs-Url: https://github.com/microsoft/apm/sessions/12c01ced-bc96-4e31-a0fd-b6e147a0e167

Co-authored-by: danielmeppiel <51440732+danielmeppiel@users.noreply.github.com>
…e uninstall sync, data-driven partition

Agent-Logs-Url: https://github.com/microsoft/apm/sessions/12c01ced-bc96-4e31-a0fd-b6e147a0e167

Co-authored-by: danielmeppiel <51440732+danielmeppiel@users.noreply.github.com>
Add tests/unit/integration/test_data_driven_dispatch.py with 9 tests
covering the data-driven integration dispatch refactoring:

- TestTargetGatingRegression (5 tests): verify each target only
  dispatches its own primitives, empty targets return zeros, and all
  targets together dispatch all primitives.
- TestExhaustivenessChecks (2 tests): structural guard ensuring every
  (target, primitive) pair has a dispatch path, and partition bucket
  keys match backward-compat aliases.
- TestSyntheticTargetProfile (2 tests): prove a hand-built TargetProfile
  works end-to-end with real integrators, confirming the architecture
  is truly data-driven.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Co-authored-by: danielmeppiel <51440732+danielmeppiel@users.noreply.github.com>
…thetic target tests, update CHANGELOG

Agent-Logs-Url: https://github.com/microsoft/apm/sessions/12c01ced-bc96-4e31-a0fd-b6e147a0e167

Co-authored-by: danielmeppiel <51440732+danielmeppiel@users.noreply.github.com>
…files, fix test comment

Agent-Logs-Url: https://github.com/microsoft/apm/sessions/12c01ced-bc96-4e31-a0fd-b6e147a0e167

Co-authored-by: danielmeppiel <51440732+danielmeppiel@users.noreply.github.com>
Copilot AI changed the title [WIP] Refactor integrators to use TargetProfile data directly Data-driven integration dispatch: replace if-chain with target × primitive loop Mar 30, 2026
Copilot AI requested a review from danielmeppiel March 30, 2026 20:18
@danielmeppiel
Copy link
Copy Markdown
Collaborator

Closes #482 and #488

@danielmeppiel danielmeppiel marked this pull request as ready for review March 30, 2026 20:52
Copilot AI review requested due to automatic review settings March 30, 2026 20:52
Copy link
Copy Markdown
Contributor

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

Refactors integration/uninstall dispatch to a data-driven TargetProfile x primitive loop, replacing a large boolean-gated if-chain and reducing per-target boilerplate across integrators. This aligns integration behavior with KNOWN_TARGETS as the single source of truth and addresses prior target-gating regressions (e.g., opencode writing into .github/).

Changes:

  • Replace boolean flag gating in install.py with a target-driven dispatch loop calling integrate_*_for_target() methods.
  • Add target-driven APIs (integrate_*_for_target, sync_for_target) to integrators while keeping legacy wrappers for backward compatibility.
  • Update uninstall sync/re-integration to iterate targets/primitives; add unit tests covering gating, dispatch exhaustiveness, and synthetic targets.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
tests/unit/test_uninstall_transitive_cleanup.py Updates mocks to match new active_targets()-based uninstall reintegration flow.
tests/unit/integration/test_data_driven_dispatch.py Adds regression/exhaustiveness tests for target x primitive dispatch and dynamic managed-file partitioning.
tests/unit/integration/test_command_integrator.py Updates gating regression tests to use targets=[KNOWN_TARGETS[...]] and target-driven methods.
src/apm_cli/integration/prompt_integrator.py Adds target-driven prompt integrate/sync entry points and keeps legacy API.
src/apm_cli/integration/instruction_integrator.py Reworks instruction integration into integrate_instructions_for_target() with format_id-based transforms; adds sync_for_target() and legacy wrappers.
src/apm_cli/integration/hook_integrator.py Adds a thin integrate_hooks_for_target() dispatcher preserving per-target hook logic.
src/apm_cli/integration/command_integrator.py Adds integrate_commands_for_target() / sync_for_target() and rewires legacy methods to delegate.
src/apm_cli/integration/base_integrator.py Makes partition_managed_files() generate buckets dynamically from KNOWN_TARGETS with backward-compat aliases.
src/apm_cli/integration/agent_integrator.py Adds target-driven agent integrate/sync and consolidates filename logic via PrimitiveMapping.
src/apm_cli/commands/uninstall/engine.py Updates uninstall cleanup + reintegration to target-driven loops using KNOWN_TARGETS / active_targets().
src/apm_cli/commands/install.py Implements target x primitive dispatch loop in _integrate_package_primitives() and switches call sites to pass targets.
Comments suppressed due to low confidence (1)

src/apm_cli/commands/install.py:940

  • CLI log messages use Unicode box-drawing characters (via \u2514\u2500) when reporting primitive integration results. This violates the repository ASCII-only output requirement and can cause encoding failures on Windows terminals. Use ASCII-only markers / bracket STATUS_SYMBOLS instead.
                _log_integration(
                    f"  \u2514\u2500 {_int_result.files_integrated} {_label} integrated -> {_deploy_dir}"
                )

…target-gating

Review comment fixes:
- Replace Unicode box-drawing chars (U+2514, U+2500) with ASCII '|--' in
  install.py log messages (lines 913, 938)
- Fix uninstall O(M*P) regression: use partition_bucket_key() for O(1) bucket
  lookup in engine.py instead of re-scanning sync_managed per (target, prim)
- Make partition_managed_files truly O(M): replace linear prefix_map scan with
  (root_dir, subdir) component dict for O(1) path routing
- Use mapping.extension instead of hardcoded '.md' in
  CommandIntegrator.integrate_commands_for_target()

Issue #482 fix (skill target-gating):
- Thread targets= parameter through SkillIntegrator.integrate_package_skill(),
  _integrate_native_skill(), _promote_sub_skills_standalone(), and standalone
  copy_skill_to_target(). Previously these called active_targets() internally,
  ignoring --target flag. Now the dispatch loop's target list is respected.

Tests:
- 8 new tests: 3 for skill target-gating (#482), 5 for partition_bucket_key

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@danielmeppiel danielmeppiel merged commit d76490d into main Mar 30, 2026
6 checks passed
@danielmeppiel danielmeppiel deleted the copilot/data-driven-integration-dispatch branch March 30, 2026 21:32
danielmeppiel added a commit that referenced this pull request Mar 31, 2026
…er, deps update)

Merge main into PR #452 branch to resolve 11 conflict markers across 3 files:
- CHANGELOG.md: trivial reorder of entries
- install.py: merge scope + auth_resolver params, preserve both --global
  and data-driven dispatch architecture from PR #494
- engine.py: take main's data-driven sync loop which already uses
  project_root-relative paths (inherently scope-aware)

Fix Rich line-wrapping in test_global_without_packages_and_no_manifest_errors
by setting COLUMNS=200 to prevent mid-word path breaks.

All 3198 unit tests pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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.

Data-Driven Integration Dispatch: Implementation Plan

3 participants