# Pathrule Pattern: Vue 3 Production Patterns (1.0.0)
# ::pathrule:package:vue-3

### [RULE] Preserve ref identity across component boundaries  (path: /src/components)
<!-- scope: folder | priority: high | strict -->

Vue tracks access through refs and reactive proxies. Copying a property into a plain local severs that access path, so the template keeps rendering an old value even though the source object changed.

- Use `toRef` or `toRefs` when a property must be passed independently; do not destructure a reactive object into plain variables and assume dependency tracking survives.
- Treat props as readonly inputs. Emit an event or expose a model binding for changes instead of mutating nested prop state through a reference the parent also owns.
- Use `computed` for derived values and keep the getter pure. A computed getter that writes another ref creates an order-dependent loop and hides the true owner of state.
- Unwrap refs in templates, but keep `.value` explicit in TypeScript code so assignments and dependency reads remain visible during review.

See /src/composables for the adjacent decision or procedure that completes this constraint.

---

### [RULE] Keep server and browser renders deterministic  (path: /src/components)
<!-- scope: folder | priority: high | strict -->

Hydration assumes the browser receives the same tree the server produced. Reading time, randomness, local storage, viewport size, or DOM state during setup makes the first client render disagree before Vue can attach listeners.

- Do not call `Date.now`, `Math.random`, storage APIs, or layout measurement while producing SSR markup unless the value is serialized from the server and reused by the client.
- Move DOM-dependent work to `onMounted` and render a stable placeholder before mount. The placeholder must preserve structure where layout shift matters.
- Never keep request-specific mutable state in a module singleton on the server. Create stores and app state per request so one user's data cannot leak into another render.
- Use stable keys that come from data identity. Generating keys during render changes node correspondence and can silently attach state to the wrong row.

See /src/stores for the adjacent decision or procedure that completes this constraint.

---

### [MEMORY] A composable owns one lifecycle and one state boundary  (path: /src/composables)

A useful composable is not merely a function whose name starts with `use`. Its value is lifecycle ownership: it creates state, subscribes to an external source, and guarantees that the subscription ends with the component scope that requested it.

- Register event listeners, observers, timers, and subscriptions inside the composable, then clean them with `onScopeDispose` so nested effect scopes are safe too.
- Return readonly refs for state consumers should observe but not mutate. Expose named commands for transitions that require validation or side effects.
- Accept refs or getters when an input can change. Normalize them at the edge rather than reading an initial value and freezing the composable to it.
- Split network caching, domain state, and DOM integration into separate composables; one `useApp` object that exposes everything has no enforceable ownership boundary.

See /src/components for the rule or workflow that puts this decision into practice.

---

### [MEMORY] Pinia stores hold shared domain state, not every remote response  (path: /src/stores)

Pinia is most effective when it models durable client-side domain state such as the active workspace, a draft, or a multi-step workflow. Treating it as a second cache for every API response creates invalidation work and two competing sources of truth.

- Keep store state serializable when SSR is enabled so the server snapshot can be transferred and hydrated without custom object behavior.
- Define transitions as actions when they enforce invariants or combine writes. Components may update a simple local preference directly, but domain changes should have names.
- Do not copy query results into a store merely to make them global. Share the query key and cache instead, then invalidate through the data-fetching layer.
- Instantiate stores per server request and dispose test instances between cases; global reuse creates order-dependent tests and cross-request leaks.

See /src/components for the rule or workflow that puts this decision into practice.

---

### [MEMORY] Watchers are integration tools, not derivation tools  (path: /src/components)

A watcher is appropriate when a reactive change must cross Vue's boundary into an external system, such as updating a URL, starting a request, or writing storage. It is a poor substitute for a value that can be derived synchronously.

- Prefer `computed` when the output is another value. It caches by dependency and cannot drift out of sync with its inputs.
- When a watcher starts asynchronous work, register cleanup and abort the prior operation before issuing the next one so late responses cannot overwrite newer state.
- Choose `watch` when the dependency list should be explicit and `watchEffect` only when automatic dependency discovery is genuinely clearer.
- Avoid deep watchers over large objects. Watch the specific scalar or immutable reference that represents the change instead of traversing the whole graph.

See /src/composables for the rule or workflow that puts this decision into practice.

---

### [SKILL] review-vue-reactivity-boundaries  (path: /)

---
name: review-vue-reactivity-boundaries
description: Review Vue component, composable, store, and SSR boundaries after a feature or refactor.
---

# Review Vue Reactivity Boundaries

Run this procedure when the affected surface changes, before the result is promoted to production. Record evidence for every step instead of accepting a plausible-looking result.

- [ ] Trace each displayed value back to its ref, reactive proxy, computed getter, or serialized server input; flag every plain copy that can become stale.
- [ ] List every watcher and confirm it crosses into an external side effect, cancels prior async work, and cannot be replaced by a computed value.
- [ ] Inspect composables for listener, timer, observer, and subscription cleanup through the active effect scope.
- [ ] Compare server and first-client inputs for time, randomness, storage, viewport, and request-specific state; render a deterministic placeholder where needed.
- [ ] Run the changed flow through navigation and component teardown, then verify that Pinia and query caches have one owner for each piece of shared data.

## Exit criteria

The change is complete only when the expected behavior, failure behavior, and rollback path have all been exercised with representative data. Preserve the evidence with the change so the next operator can repeat the same checks.
