# System Invariants

**Purpose:** Single authoritative document naming, explaining, and referencing all enforced invariants.  
**Owner:** Development Team  
**Last Updated:** 2025-12-22  
**Status:** active  
**Baseline:** `v1.0.11-p0-p1.4-p1.5-p2.6-p2.7-complete`

---

## Overview

This document defines the **invariants** (unchanging rules) that this project enforces. These invariants are **policy-as-code** — they are enforced by tooling, not just documented as conventions.

**Why this matters:**
- New contributors can understand "what not to break"
- Future work (P2, P3, etc.) has explicit constraints
- Violations are caught automatically, not discovered later
- The baseline tag (`v1.0.11-p0-p1.4-complete`) represents a state where all invariants are enforced

**How to use this document:**
- Before making changes, review relevant invariants
- If you violate an invariant, CI will fail with a clear error
- If you need to change an invariant, update this document and the enforcing code together

---

## 1. Packaging Invariants (P0)

### What

The npm package must not contain forbidden files, and packaging is controlled by a whitelist approach.

**Specific rules:**
- `npm pack --dry-run` must not contain:
  - `xcuserdata/`, `*.xcuserstate`, `DerivedData/` (Xcode user state)
  - `ios/App/` (test app, not library code)
  - `.DS_Store`, `*.swp`, `*.swo`, `*.orig`, `*.rej` (editor/macOS junk)
- `package.json.files` whitelist is **authoritative** (primary control)
- `.npmignore` is secondary (belt-and-suspenders only)

### Why

- **Publish safety:** Prevents shipping developer-local files, test apps, and build artifacts
- **Package size:** Keeps published tarball clean and minimal
- **Security:** Avoids leaking local development state
- **Professionalism:** Published packages should only contain intended library code

### How

**Enforced by:** `scripts/verify.sh` → `check_package()` function

**Enforcement mechanism:**
1. Runs `npm pack --dry-run` to simulate package creation
2. Extracts file list from pack output (handles multiple npm output formats)
3. Scans for forbidden patterns using regex: `xcuserdata/|\.xcuserstate|DerivedData/|\.tgz|ios/App/|\.DS_Store|\.swp|\.swo|\.orig|\.rej`
4. **Hard-fails** if any forbidden files are found
5. Provides actionable error messages with remediation hints

**Location:** `scripts/verify.sh:216-316` (function `check_package()`)

**Verification command:**
```bash
./ci/run.sh  # Includes package checks
# Or manually:
npm pack --dry-run | grep -E "xcuserdata|xcuserstate|DerivedData|ios/App/"
```

### Where

- **Enforcing code:** `scripts/verify.sh:216-316` (`check_package()`)
- **Policy definition:** `docs/progress/00-STATUS.md:104-113` (Packaging Invariants section)
- **Package configuration:** `package.json` (`files` field)
- **Secondary exclusion:** `.npmignore` (belt-and-suspenders)

---

## 2. Core Module Purity (P1.4)

### What

The `src/core/` module must remain platform-agnostic and portable. It cannot import platform-specific or Node.js built-in modules.

**Specific rules:**
- `src/core/` must not import:
  - **Node builtins:** `fs`, `path`, `os`, `child_process`, `crypto`, `http`, `https`, `net`, `tls`, `zlib`, `stream`, `util`, `url`, `worker_threads`, `perf_hooks`, `vm`
  - **Platform modules:** `@capacitor/*`, `react`, `capacitor`
- `package.json.exports['./core']` must exist and point to valid build artifacts
- Core types must remain platform-agnostic (no platform-specific types in core)

### Why

- **Portability:** Core module can be used in any JavaScript/TypeScript environment
- **Architectural separation:** Platform-specific code belongs in adapters, not core
- **Testability:** Core can be tested without platform dependencies
- **Reusability:** Core types/interfaces can be shared across platforms without coupling

### How

**Enforced by:** `scripts/verify.sh` → `check_core_source()` + `check_core_artifacts()`

**Source checks (pre-build):**
1. Verifies `src/core/` directory exists
2. Checks for required core files (`index.ts`, `errors.ts`, `enums.ts`, `events.ts`, `contracts.ts`, `guards.ts`)
3. Scans all files in `src/core/` for forbidden imports using comprehensive regex:
   ```bash
   (from\s+['\"]|require\s*\(\s*['\"]|import\s*\(\s*['\"])(${NODE_BUILTINS}|react|@capacitor/|capacitor)['\"]
   ```
4. **Hard-fails** if forbidden imports are found
5. Prints offending lines and policy reminder

**Artifact checks (post-build):**
1. Verifies build artifacts exist: `dist/esm/core/index.js`, `dist/esm/core/index.d.ts`
2. Validates `package.json.exports['./core']` exists using Node.js script
3. **Hard-fails** if artifacts or exports are missing

**Location:**
- Source checks: `scripts/verify.sh:413-464` (function `check_core_source()`)
- Artifact checks: `scripts/verify.sh:467-496` (function `check_core_artifacts()`)

**Verification command:**
```bash
./ci/run.sh  # Includes core module checks
# Or manually check source:
grep -RInE "(from\s+['\"]|require\s*\(\s*['\"]|import\s*\(\s*['\"])(${NODE_BUILTINS}|react|@capacitor/|capacitor)['\"]" src/core
```

### Where

- **Enforcing code:**
  - Source checks: `scripts/verify.sh:413-464` (`check_core_source()`)
  - Artifact checks: `scripts/verify.sh:467-496` (`check_core_artifacts()`)
- **Policy definition:** `docs/progress/P2-DESIGN.md:67-77` (Core Module Purity section)
- **Core module location:** `src/core/`
- **Package exports:** `package.json` (`exports['./core']` field)

---

## 3. CI Authority (P0)

### What

`./ci/run.sh` is the **only** supported CI entrypoint. All release gates, merge gates, and automation must invoke `./ci/run.sh`, not `npm run build` directly.

**Specific rules:**
- `./ci/run.sh` is the canonical CI command
- All gates (release, merge, automation) must call `./ci/run.sh`
- `npm run build` must not be called directly in gates (it doesn't include invariant checks)
- `./scripts/verify.sh` is an implementation detail (wrapped by `./ci/run.sh`)

### Why

- **Single source of truth:** One command that runs all checks
- **Invariant enforcement:** `verify.sh` (called by `ci/run.sh`) encodes packaging, core-purity, and export checks
- **Consistency:** All environments (local, CI, release) use the same verification
- **Debuggability:** Failures are actionable and consistent across environments
- **Policy-as-code:** The contract is explicit, not implicit

### How

**Enforced by:** `ci/README.md` (policy-as-code contract) + `githooks/pre-push` (optional automation)

**Enforcement mechanism:**
1. **Documentation contract:** `ci/README.md` explicitly states the policy (line 5-6)
2. **Git hook (optional):** `githooks/pre-push` calls `./ci/run.sh` before allowing pushes
3. **Makefile target:** `make ci` runs `./ci/run.sh` (convenience alias)
4. **Process enforcement:** Team must follow the contract (not automatically enforced, but CI will fail if invariants are violated)

**Location:**
- Policy contract: `ci/README.md:5-6` (Contract / Policy-as-code block)
- CI entrypoint: `ci/run.sh` (wraps `./scripts/verify.sh`)
- Git hook: `githooks/pre-push` (optional, calls `./ci/run.sh`)

**Verification command:**
```bash
./ci/run.sh  # The canonical CI command
# Or:
make ci      # Convenience alias
```

### Where

- **Policy contract:** `ci/README.md:5-6` (Contract / Policy-as-code block)
- **CI entrypoint:** `ci/run.sh` (wraps `./scripts/verify.sh`)
- **Verification script:** `scripts/verify.sh` (implementation detail)
- **Git hook:** `githooks/pre-push` (optional automation)
- **Makefile:** `Makefile` (`make ci` target)
- **Documentation:** `docs/progress/00-STATUS.md:115-117` (Local CI Policy section)

---

## 4. Export Correctness (P0)

### What

All `package.json.exports` paths must match actual build artifacts. Exported paths must exist after build.

**Specific rules:**
- `package.json.exports["./web"]` paths must match actual build artifacts
- `package.json.exports["./core"]` paths must match actual build artifacts
- All exported paths must exist after `npm run build`
- Build must succeed (TypeScript compilation + Rollup bundling)

### Why

- **Runtime correctness:** Broken exports cause import failures at runtime
- **Type safety:** Missing type definitions break TypeScript consumers
- **Publish safety:** Broken exports are discovered before publish, not after
- **Consumer trust:** Correct exports are a basic contract with package consumers

### How

**Enforced by:** `scripts/verify.sh` → `check_build()` function

**Enforcement mechanism:**
1. Runs `npm run build` to generate build artifacts
2. Verifies build succeeds (exit code check)
3. Checks for required build outputs:
   - `dist/esm/web.d.ts`, `dist/esm/web.js`
   - `dist/esm/core/index.d.ts`, `dist/esm/core/index.js`
4. **Hard-fails** if build fails or artifacts are missing
5. Core artifact validation also checks `package.json.exports['./core']` exists (via `check_core_artifacts()`)

**Location:** `scripts/verify.sh:191-214` (function `check_build()`)

**Verification command:**
```bash
./ci/run.sh  # Includes build checks
# Or manually:
npm run build && ls -la dist/esm/web.* dist/esm/core/index.*
```

### Where

- **Enforcing code:** `scripts/verify.sh:191-214` (`check_build()`)
- **Export definitions:** `package.json` (`exports` field)
- **Build artifacts:** `dist/esm/` (generated by `npm run build`)
- **Policy definition:** `docs/progress/00-STATUS.md:111` (Export correctness requirement)

---

## 5. Documentation Structure (P1.5)

### What

Documentation must follow the index-first rule and maintain drift guards. New docs must be discoverable via the index or explicitly archived.

**Specific rules:**
- **Index-first rule:** New docs must be linked from `docs/00-INDEX.md` or placed in `_archive/`/`_reference/`
- **Progress docs are authoritative:** `docs/progress/` is the single source of truth for project state
- **Archive structure:** Historical docs go in `docs/_archive/` (underscore indicates "not active doc surface")
- **Drift guards:** Key docs have standard headers (Purpose, Owner, Last Updated, Status)

### Why

- **Discoverability:** Contributors can find docs via the index
- **Prevents sprawl:** Index-first rule prevents undocumented files
- **Maintainability:** Drift guards (Last Updated, Status) help identify stale docs
- **Audit trail:** Archive preserves history without cluttering active navigation
- **Authority:** Progress docs are clearly marked as "truth" vs "guides"

### How

**Enforced by:** `docs/00-INDEX.md` (index-first rule) + documentation process

**Enforcement mechanism:**
1. **Index-first rule:** Stated in `docs/00-INDEX.md:298-305` (Maintenance section)
2. **Process enforcement:** Team must add new docs to index (not automatically enforced, but discoverability suffers if not followed)
3. **Drift guards:** Standard header format in progress docs:
   ```markdown
   **Purpose:** [one sentence]
   **Owner:** Development Team
   **Last Updated:** YYYY-MM-DD
   **Status:** active|archived
   ```
4. **Archive structure:** `docs/_archive/` clearly separated from active docs

**Location:**
- Index: `docs/00-INDEX.md` (central navigation hub)
- Index-first rule: `docs/00-INDEX.md:298-305` (Maintenance section)
- Progress docs: `docs/progress/` (authoritative state)
- Archive: `docs/_archive/` (historical artifacts)

**Verification command:**
```bash
# Manual review:
# 1. Check that new docs are in index
# 2. Verify progress docs have drift guards
# 3. Confirm archive structure is standardized
```

### Where

- **Index:** `docs/00-INDEX.md` (central navigation hub)
- **Index-first rule:** `docs/00-INDEX.md:298-305` (Maintenance section)
- **Progress docs:** `docs/progress/` (authoritative state)
- **Archive structure:** `docs/_archive/` (historical artifacts)
- **Policy definition:** `docs/progress/P2-DESIGN.md:105-113` (Documentation Structure section)

---

## 6. Baseline Tag Integrity

### What

The baseline tag `v1.0.11-p0-p1.4-p1.5-p2.6-p2.7-complete` represents a known-good architectural baseline where all invariants are enforced. Future work must not invalidate this baseline.

**Specific rules:**
- Baseline tag: `v1.0.11-p0-p1.4-p1.5-p2.6-p2.7-complete`
- This tag represents:
  - All P0 invariants enforced (packaging, CI authority, exports)
  - All P1.4 invariants enforced (core module purity)
  - All P1.5 invariants enforced (documentation structure)
  - All P2.6 invariants enforced (type safety)
  - All P2.7 invariants enforced (system invariants documentation)
  - All tooling in place (`verify.sh`, `ci/run.sh`)
- Future work must not require rollback to this baseline
- Future work must not break any invariant enforced at baseline

### Why

- **Safety anchor:** Provides a known-good state to rollback to if needed
- **Reference point:** Future work can compare against baseline
- **Confidence:** Baseline represents a tested, stable state
- **Historical record:** Tag preserves the state where foundation was complete

### How

**Enforced by:** Git tag + process (not automatically enforced, but baseline must remain valid)

**Enforcement mechanism:**
1. **Git tag:** `v1.0.11-p0-p1.4-p1.5-p2.6-p2.7-complete` exists in repository
2. **Process enforcement:** Team must not break baseline (CI will catch invariant violations)
3. **Validation:** Can verify baseline by checking out tag and running `./ci/run.sh` (should pass)

**Location:**
- Baseline tag: `v1.0.11-p0-p1.4-p1.5-p2.6-p2.7-complete` (Git tag)
- Baseline description: `docs/progress/00-STATUS.md:126` (Baseline Tag section)
- Previous baseline: `v1.0.11-p0-p1.4-complete` (historical reference)

**Verification command:**
```bash
# Verify baseline is still valid:
git checkout v1.0.11-p0-p1.4-p1.5-p2.6-p2.7-complete
./ci/run.sh  # Should pass
git checkout -  # Return to current branch
```

### Where

- **Baseline tag:** `v1.0.11-p0-p1.4-p1.5-p2.6-p2.7-complete` (Git tag)
- **Baseline description:** `docs/progress/00-STATUS.md:126` (Baseline Tag section)
- **Previous baseline:** `v1.0.11-p0-p1.4-complete` (historical reference)
- **Status doc:** `docs/progress/00-STATUS.md:17-25` (What This Baseline Includes section)

---

## Summary

### Invariant Enforcement Matrix

| Invariant | Enforced By | Hard-Fail? | Verification Command |
|-----------|-------------|------------|---------------------|
| Packaging | `verify.sh` → `check_package()` | ✅ Yes | `./ci/run.sh` |
| Core Purity | `verify.sh` → `check_core_source()` + `check_core_artifacts()` | ✅ Yes | `./ci/run.sh` |
| CI Authority | `ci/README.md` (contract) | ⚠️ Process | Manual review |
| Export Correctness | `verify.sh` → `check_build()` | ✅ Yes | `./ci/run.sh` |
| Documentation Structure | `docs/00-INDEX.md` (index-first rule) | ⚠️ Process | Manual review |
| Baseline Integrity | Git tag + process | ⚠️ Process | `git checkout v1.0.11-p0-p1.4-p1.5-p2.6-p2.7-complete && ./ci/run.sh` |

**Legend:**
- ✅ **Hard-Fail:** CI automatically fails if violated
- ⚠️ **Process:** Enforced by process/documentation, not automatic

---

## For New Contributors

**Before making changes:**
1. Review relevant invariants above
2. Run `./ci/run.sh` to verify current state passes
3. Make your changes
4. Run `./ci/run.sh` again — it will catch invariant violations automatically

**If CI fails:**
- Read the error message — it explains which invariant was violated
- Check the "Where" section above for the enforcing code
- Fix the violation (or discuss changing the invariant if needed)

**If you need to change an invariant:**
1. Update this document (`docs/SYSTEM_INVARIANTS.md`)
2. Update the enforcing code (usually `scripts/verify.sh`)
3. Update any related documentation
4. Ensure the change is backward-compatible or properly versioned

---

## Related Documentation

- **P2 Design:** `docs/progress/P2-DESIGN.md` — Defines P2 scope and constraints
- **Progress Status:** `docs/progress/00-STATUS.md` — Current status and packaging invariants
- **CI Documentation:** `ci/README.md` — Local CI usage and contract
- **Verification Script:** `scripts/verify.sh` — Implementation of invariant checks

---

**Last Updated:** 2025-12-22  
**Maintained By:** Development Team  
**Status:** active

---

## Type Safety Notes

**Policy:** All external boundaries use `unknown`, all data payloads use `Record<string, unknown>`. No `any` allowed except documented TypeScript limitations.

**Allowed Exception:**
- **`src/utils/PlatformServiceMixin.ts:258`** — `any[]` required for TypeScript mixin constructor pattern
  - **Reason:** TypeScript's mixin pattern requires `any[]` for constructor arguments (language limitation, not design choice)
  - **Status:** Documented with inline comment explaining the limitation
  - **Verification:** `rg '\bany\b' src/` returns zero matches except this documented exception

**Verification:**
- Run `rg -n "\bany\b" src/ --type ts | grep -v "node_modules" | grep -v "test"` — should return only the documented exception
- All external boundaries (`src/web.ts`, plugin interfaces) use `unknown` for inputs
- All data payloads (`src/observability.ts`, `src/core/events.ts`) use `Record<string, unknown>`

