# ADR-079: Shared Helper Governance — /shared/ Directory Structure, Adoption Policy, and Breaking Change Protocol

## Status

Accepted, 2026-06-27.

## Status History

```yaml
status_history:
  - date: 2026-06-27
    status: Proposed
    changed_by: hkl
    reason: Formalising governance for shared helpers as codebase grows; establishes criteria for shared vs. copy-verbatim, breaking change protocol, and versioning policy
    changed_via: adr-kit (360lm)
  - date: 2026-06-27
    status: Accepted
    changed_by: hkl
    reason: Platform architecture depends on clear shared helper boundaries; prevents uncontrolled cross-PWA coupling while maintaining single-file (ADR-013) principle
    changed_via: adr-kit (360lm)
```

## Context

The 360lm platform has evolved to include reusable code patterns that appear in multiple PWAs:
- `safe-bottom.css` (ADR-002) — CSS padding helper for notch-safe layouts, included in 34+ PWAs
- `maps-client.js` (ADR-004) — Maps API abstraction (Google primary, TomTom fallback), used in vehicle, recce, tour-planner, tour-pg
- `tour.js` — Guided tour overlay framework, used in counters, recce, tour-planner, tour-pg
- `maps-client-test.html` — Test harness for maps-client.js

These live in `/var/www/360lm/shared/` and are referenced via `<link>` or `<script src="/shared/...">` (never npm imports, per ADR-013). However, there is no formal governance for:
1. When a helper belongs in `/shared/` vs. being copy-pasted into each PWA
2. How to version and update shared helpers without breaking consuming PWAs
3. The process for adding new shared helpers
4. How to handle breaking changes across multiple PWAs atomically

Without governance, the project risks:
- Creating cross-PWA coupling that is hard to reason about
- Breaking multiple PWAs simultaneously when a shared helper changes
- Duplication if developers don't know a helper exists in `/shared/`

## Decision

### 1. Criteria for a Helper to Belong in `/shared/`

A code pattern belongs in `/shared/` (as a shared helper, not copy-verbatim) if and only if ALL three criteria are met:

1. **Used by 3+ PWAs** — The pattern appears in at least three different PWAs. Code used by 1–2 PWAs should be copy-pasted; shared files are for platform-wide patterns.
2. **No PWA-specific state** — The helper has no PWA-specific configuration, naming, or business logic. It provides pure platform infrastructure (CSS resets, API abstraction, UI framework). Helpers with business logic (amount formatting for finance, image capture annotations) must be copy-pasted into each PWA so each PWA owns its logic.
3. **Documents or referenced in an ADR** — The shared helper must be documented in an ADR or explicitly referenced in an existing ADR before being added to `/shared/`. This ensures architectural review and prevents ad-hoc shared code.

**Current `/shared/` inventory (2026-06-27):**

| Helper | Consumers | Type | Documented |
|---|---|---|---|
| `safe-bottom.css` | 34+ PWAs (all) | CSS platform infrastructure | ADR-002 |
| `maps-client.js` | vehicle, recce, tour-planner, tour-pg (4) | API abstraction | ADR-004 |
| `tour.js` | counters, recce, tour-planner, tour-pg (4) | UI framework | Implicit (referenced in tour-pg, tour-planner PWA code) |
| `maps-client-test.html` | Test harness only | Test infrastructure | Not required — internal test only |

**Decision Maker:** hkl

### 2. Breaking Changes to Shared Helpers

When a breaking change must be made to a shared helper (e.g., API signature change, CSS output change):

1. **Update ALL consuming PWAs in a single PR** — Never stage a shared helper change without updating all consumers in the same commit. This prevents silent failures where some PWAs continue using the old API.
2. **Bump SW CACHE_VER in all consuming PWAs** — Update the `CACHE_VER` constant in `sw.js` for each consuming PWA (see ADR-005). This ensures all cached assets are invalidated on deploy.
3. **No filename versioning (e.g., `maps-client-v2.js`)** — Update helpers in-place. The shared file is always the current version. If backwards compatibility is needed, add a wrapper function or feature-flag inside the helper, not a separate file.
4. **Deploy once, all at once** — Single deployment to dev, then prod, via standard deploy script (ADR-069). No staged rollout across PWAs (not possible on single VPS; all PWAs deployed together).

### 3. Adding a New Shared Helper

Before adding a new file to `/shared/`:

1. **Create or reference an ADR** — Document the decision in a new ADR or add a reference to an existing ADR. ADR must justify why the pattern is 3+ PWAs, has no business logic, and belongs in shared infra.
2. **Announce to team** — Comment in the ADR or shared channel that a new shared helper is live, so developers can adopt it instead of duplicating code.
3. **Link from PWA code** — When a PWA begins using the shared helper, add a comment in the PWA's `index.html` pointing to the shared file and the governing ADR (e.g., `<!-- safe-bottom.css: ADR-002 -->`).

### 4. Copy-Verbatim (NOT Shared) Helpers

Code patterns that do NOT meet the three criteria above must be **copy-pasted into each PWA**. Examples:

- **Image annotation logic** (vehicle PWA) — business-specific image capture + canvas annotation. Copy into recce if recce needs it; do not put in `/shared/`.
- **Amount formatting** (finance PWA) — Indian number formatting with locale rules. Copy into any PWA needing it; do not centralize without cross-PWA ADR review.
- **Form validation** (used by 2 PWAs only) — Belongs in one PWA or the other, or copy-pasted if both need it. Shared only if a third PWA adopts the same pattern.

This aligns with ADR-013 (single self-contained HTML file) — each PWA owns its logic and can evolve independently. Shared helpers are reserved for true platform infrastructure.

## Implementation Notes

**Governance enforcement:**

- Code review gates: before approving a PR that adds a file to `/shared/`, verify the file meets all three criteria and is documented in an ADR.
- Before updating a shared helper, check all consuming PWAs (use `grep -r "filename"` across the repo). Update all in the same PR.
- Playwright test suite (ADR-022) should validate that consuming PWAs load the shared helper correctly (e.g., `safe-bottom.css` loads and applies max() padding).

**Documentation:**

- Keep `/shared/` inventory in this ADR updated (or in a cross-reference document) so developers know what exists.
- Each shared helper's `.js` file should include a comment at the top linking to its governing ADR.
- Each PWA consuming a shared helper should note the dependency in `index.html` (e.g., `<!-- maps-client: ADR-004 -->`).

## Alternatives Considered

- **No shared helpers — every PWA copies all code.** Rejected: `safe-bottom.css` is 34 PWAs now; duplication makes future fixes costly (update 34 files). Shared CSS/JS for true platform infrastructure saves maintenance.

- **Publish to npm, manage versions in package.json.** Rejected: ADR-013 mandates no build pipeline, no npm packages at the PWA level. All dependencies are self-hosted static files. npm would require bundler and `package.json` per PWA, violating the single-file principle.

- **Allow any code in `/shared/` without ADR review.** Rejected: leads to accidental cross-PWA coupling, hidden dependencies, and breaking changes affecting multiple PWAs simultaneously. ADR requirement ensures architectural intent.

- **Versioned filenames (e.g., `maps-client-v1.js`, `maps-client-v2.js`).** Rejected: splits a logical helper across multiple files, confuses PWAs about which version to use, duplicates code. Breaking changes should be rare; when they happen, update all consumers in one PR.

- **Feature flags inside shared helpers instead of atomic updates.** Rejected: adds conditional logic and test burden. Better to update all PWAs atomically — single VPS means single deploy anyway.

## Consequences

**Positive:**

- Clear boundary between shared platform infra and PWA-specific code — reduces accidental coupling.
- Breaking changes to shared helpers are explicit, reviewed, and atomic across all PWAs.
- New developers know: if a pattern is in `/shared/`, it's blessed by architecture; if not, copy it into your PWA and own it.
- Maintenance burden for true platform helpers (e.g., `safe-bottom.css` CSS reset logic) is centralized — one fix applies to all 34 PWAs automatically.

**Negative / Trade-offs:**

- Shared helper additions require ADR review, which adds process overhead. Mitigated: ADRs are lightweight; most shared decisions are strategic and benefit from review anyway.
- Developers must remember: shared helpers are rare; most code stays in the PWA. Copy-verbatim is the default. Mitigated: clear criteria in this ADR.
- Breaking changes require updating multiple files (all consuming PWAs' `sw.js`). Mitigated: batch updates in one PR; automation could flag consuming PWAs automatically.

**Risks and mitigations:**

- Risk: New developer adds a file to `/shared/` without meeting the three criteria → unexpected coupling. Mitigation: code review gates; require ADR link in PR.
- Risk: A shared helper is updated but one PWA is missed → inconsistent behaviour. Mitigation: use `grep` to verify all consumers are updated in the same commit; Playwright tests check helper integration.
- Risk: A helper used by only 2 PWAs is mistakenly put in `/shared/`. Mitigation: clear "3+ PWAs" criterion; code review catches this.

## Related Decisions

- **ADR-002** (safe-bottom.css) — Mandates all PWAs link this shared helper.
- **ADR-004** (maps-client.js) — Mandates vehicle, recce, tour-planner, tour-pg use this shared API abstraction.
- **ADR-005** (CACHE_VER) — When shared helpers are updated, consuming PWAs must bump CACHE_VER to invalidate old cached versions.
- **ADR-013** (single HTML file, no framework) — Enforces that shared helpers are static files (`<link>`, `<script src="">`), never npm imports.
- **ADR-028** (feature ownership follows domain) — Each PWA owns its business logic; shared helpers are platform infrastructure only, not domain-specific features.

## References

- `docs/adr/ADR-002-safe-bottom-css-mandatory.md` — CSS safe-area helper
- `docs/adr/ADR-004-maps-google-primary-tomtom-fallback.md` — Maps client API
- `docs/adr/ADR-005-sw-cache-ver-string-bump.md` — Service worker cache versioning
- `docs/adr/ADR-013-single-html-file-no-framework.md` — Single-file PWA principle
- `/var/www/360lm/shared/` — Directory listing
- `memory/project_arch.md` — All PWAs and their dependencies
