Skip to content

refactor(view): drop mutual conflict; align with multi-driver pattern#92

Merged
markshust merged 1 commit into
developfrom
feature/drop-view-conflicts
May 27, 2026
Merged

refactor(view): drop mutual conflict; align with multi-driver pattern#92
markshust merged 1 commit into
developfrom
feature/drop-view-conflicts

Conversation

@markshust
Copy link
Copy Markdown
Collaborator

Summary

PR #88 added Composer `conflict` declarations between `marko/view-latte` and `marko/view-twig`. This drops them, aligning view with the same pattern used by `marko/database`, `marko/cache`, `marko/queue`, etc. — where both drivers can be installed simultaneously and Marko's DI catches the double-binding at boot via `BindingConflictException`.

Why drop the conflicts?

Three reasons:

1. Marko already has the canonical detection mechanism. `BindingConflictException` throws at boot time when two modules bind the same interface. That's the framework's loud, expressive error path. Composer `conflict` declarations duplicate this in a less expressive medium — Composer can only say "incompatible," not "two modules bind ViewInterface, remove one."

2. O(n²) maintenance burden. Each new driver in a family requires updating every existing sibling's `conflict` block. PR #91 was going to ship a CI test enforcing sync between `known-drivers.php` and these conflict blocks — but that's infrastructure to maintain other infrastructure. `known-drivers.php` alone is enough source of truth.

3. Monorepo asymmetry. Because of the conflicts, view-twig couldn't sit in `require` next to view-latte — it had to live as a "phantom" via root `autoload-dev` namespace mappings, while view-latte was fully installed. After this change, view-twig is a regular monorepo package, fully symmetric with view-latte. Matches how database-mysql + database-pgsql coexist today.

What changed

  • Removed `"conflict": {"marko/view-twig": "*"}` from `packages/view-latte/composer.json`
  • Removed `"conflict": {"marko/view-latte": "*"}` from `packages/view-twig/composer.json`
  • Deleted `packages/view-latte/tests/ComposerConflictTest.php` (asserted the conflict declaration that no longer exists)
  • Removed the conflict assertion test in `packages/view-twig/tests/PackageTest.php`
  • Added `marko/view-twig` to root `composer.json`'s `repositories[]` and `require` (mirrors view-latte)
  • Removed the phantom `Marko\View\Twig\: packages/view-twig/src/` mapping from root `autoload-dev` (no longer needed — view-twig's own composer.json autoload kicks in via path repo install)
  • Removed `twig/twig` from root `require-dev` (now transitive via view-twig's `require`)
  • Updated `packages/framework/tests/RootComposerJsonTest.php` — added view-twig to the `$allPackages` array, bumped the "70 packages" descriptions to 71

Trade-off

Failure detection moves from `composer install` time to boot time. In practice the gap is seconds (any subsequent request triggers boot), and the boot-time error message is more actionable than Composer's dependency-resolution failures. Aligned with Magento's long-established prior art (module conflicts caught at DI level, not at composer.json) — which Marko inherits its DI heritage from.

Downstream impact

This blocks/feeds two open plans:

Test plan

  • `packages/view/tests/` — 43 tests pass
  • `packages/view-latte/tests/` — 42 tests pass
  • `packages/view-twig/tests/` — 52 tests pass
  • `packages/framework/tests/` — 27 tests pass (RootComposerJsonTest updated for the new package count)
  • `composer update` resolves with both view-latte and view-twig installed simultaneously

🤖 Generated with Claude Code

PR #88 added Composer `conflict` declarations between view-latte and
view-twig. This is duplication — Marko's DI already catches
double-bindings via BindingConflictException at boot. The conflict
forced view-twig into "autoload-dev only" mode in the monorepo,
making view asymmetric with database/cache/queue (where both drivers
sit in `require` together).

Drops the Composer conflict blocks. view-twig becomes a regular
monorepo package: in `repositories[]`, in `require`, full autoload via
its own composer.json (no more manual src namespace in root autoload-dev).

twig/twig is no longer needed in root require-dev — it's pulled in
transitively now that view-twig is in require.

Trade-off: failure point moves from `composer install` time to boot
time. The boot-time error ("Two modules bind ViewInterface: X and Y")
is more actionable than Composer's dependency-resolution noise, and
fires immediately on the next request. This pattern aligns with how
Magento handles module conflicts (DI-level, not composer-level) and
how every other Marko multi-driver family already works today.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added the refactor Code refactoring with no behavior change label May 27, 2026
markshust added a commit that referenced this pull request May 27, 2026


PR #92 settled the architectural question: Marko relies on DI-level
BindingConflictException for double-binding detection, not Composer
`conflict` declarations. This plan no longer adds conflict blocks or
validates them — every rollout task is correspondingly simpler.

Changes:
- Deleted task 004 (pilot conflict blocks) — no longer needed
- Updated _plan.md task table, objective, success criteria, risks
- Removed assertConflictBlocksMatch from KnownDriversValidator (task 001);
  helper now exposes assertSkeletonSuggestContainsAll + assertDocsUrlsResolveToValidPattern
- Stripped conflict sub-step / requirement / acceptance criterion from
  rollout tasks 008-016 (multi-driver) and 011 (inertia)
- Rewrote task 017 (view): both view-latte and view-twig now in monorepo
  post-PR #92, no conflicts, both belong in known-drivers.php
- Cleaned up "vacuous conflict assertion" phrasing from single-driver
  tasks 018-024
- Simplified task 024 (page-cache): removed page-cache-entity conflict
  verification (moot when no drivers have conflicts)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@markshust markshust merged commit 6c2e25f into develop May 27, 2026
1 check passed
@markshust markshust deleted the feature/drop-view-conflicts branch May 27, 2026 13:49
markshust added a commit that referenced this pull request May 27, 2026
…mark ready

Closes the gap left by deleting the conflict-blocks pilot task in the
previous commit. Tasks 005-025 each decrement by one (005 -> 004,
006 -> 005, ..., 025 -> 024). All cross-references updated:
- File names
- Task headers
- Depends-on fields
- _plan.md task table
- Prose references

Also updates two stale references to view-twig in Discovery Notes that
predated PR #88 / #92 (both drivers are now in monorepo, view rollout
is no longer a single-driver case).

Status: planning -> ready (plan-orchestrate expects this).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

refactor Code refactoring with no behavior change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant