- Chrome (any recent version supporting MV3)
- Node.js 18+ (for running tests — not needed for the extension itself)
- No build step, no bundler, no
npm install
- Open Chrome →
chrome://extensions - Enable Developer mode (toggle, top right)
- Click Load unpacked
- Select the repo folder (
/path/to/IntentLock) - Open a new tab — the onboarding wizard appears
| What changed | How to reload |
|---|---|
background.js or any imported module |
Extensions page → refresh icon on the IntentLock card |
content.js, page-tracker.js, intervention-overlay.js |
Extensions page refresh + reload the affected tab |
manifest.json |
Extensions page refresh (same as above) |
newtab.js, options.js, popup.js |
Close and reopen the page/popup |
heuristic-policy.js |
Extensions page refresh (background imports it) |
The reload button is the circular arrow (↻) under the extension card in chrome://extensions.
The extension is loaded from the repo directly. If you keep a separate "loadable" copy at a different path (e.g. /Users/you/Documents/Intentlock), sync it manually:
rsync -a --exclude='.git' --exclude='node_modules' \
/path/to/IntentLock/ /path/to/Intentlock/Or just point Chrome at the repo folder and avoid the sync entirely.
# Single suite
node --test tests/heuristic-policy.test.mjs
# All suites
node --test tests/*.mjs
# Verbose (shows individual test names)
node --test --reporter=spec tests/*.mjsAll tests use Node's built-in node:test + assert/strict. No test runner to install.
| File | What it covers |
|---|---|
heuristic-policy.test.mjs |
Intent classification, site lookup, policy builders, drift scoring, migration (48 tests) |
background.test.mjs |
Service worker helper functions (2 tests) |
drift.test.mjs |
Legacy heuristic drift scoring |
drift-cache.test.mjs |
TTL cache hit/miss/eviction |
drift-threshold.test.mjs |
Score threshold boundary conditions |
cooldown.test.mjs |
Per-domain override cooldown logic |
llm-backoff.test.mjs |
Quota backoff state machine |
llm.test.mjs |
LLM call mocking and response parsing |
providers.test.mjs |
Provider config validation, API key checks |
page-tracker.test.mjs |
Dwell accumulation, SPA navigation detection |
error-log.test.mjs |
Error classification, log rotation |
intervention-overlay.test.mjs |
Shadow DOM overlay construction |
distraction-sites.test.mjs |
Legacy default domain list |
static-smoke.test.mjs |
Manifest integrity, referenced assets exist, minimum permissions |
IntentLock/
├── manifest.json # MV3 manifest
├── background.js # Service worker — session, drift, intervention
├── content.js # Injected into every page
├── page-tracker.js # Dwell time + SPA detection (used by content.js)
├── intervention-overlay.js # Shadow-DOM overlay (used by content.js)
├── heuristic-policy.js # Policy engine — pure, node-testable
├── drift.js # Legacy heuristic constants + evaluateHeuristicDrift
├── drift-cache.js # In-memory TTL cache for LLM results
├── llm.js # LLM drift check
├── llm-backoff.js # Quota/rate-limit backoff guard
├── providers.js # Multi-provider LLM abstraction
├── distraction-sites.js # Legacy 8-domain default list
├── error-log.js # Diagnostic log (chrome.storage.local)
├── newtab.html / newtab.js # New tab override — onboarding + session form
├── newtab.css # Shared styles (used by newtab + options)
├── options.html / options.js # Settings page
├── popup.html / popup.js # Toolbar popup
├── intervention.html / .js # Tab-replacement intervention page
├── diagnostics.html / .js # Error log viewer
├── history.html / .js # Session history viewer
├── icon.svg / icon*.png # Extension icons
├── CHANGELOG.md
├── tests/ # node:test test suites
└── docs/ # This documentation
├── architecture.md
├── heuristic-policy.md
├── storage-schema.md
└── development.md
- Append an entry to
SITE_CATEGORIESinheuristic-policy.js:{ id: 'my_category', label: 'My Category', description: 'One line description', defaultPolicy: 'warn', domains: ['example.com', 'another.com'], }
- Add it to all three strictness presets in
STRICTNESS_PRESETS - If relevant, add it to
CATEGORY_ALIGNMENTfor the intent categories that align with it - Add a test in
tests/heuristic-policy.test.mjs - Run
node --test tests/heuristic-policy.test.mjs
No other files need to change — the settings grid and policy schema pick it up automatically.
- Append an entry to
INTENT_CATEGORIESinheuristic-policy.js - Add it to
CATEGORY_ALIGNMENTwith its aligned site category IDs - Add keyword classification tests in
tests/heuristic-policy.test.mjs
- Add an entry to
PROVIDERSinproviders.js:my_provider: { id: 'my_provider', label: 'My Provider', apiStyle: 'openai', // 'openai' | 'gemini' | 'ollama' defaultModel: 'my-model', defaultBaseUrl: 'https://api.example.com/v1/chat/completions', authType: 'bearer', isLocal: false, }
- Add it to the
<select>inoptions.htmlif it needs a dedicated UI entry (custom provider flow handles most cases already) - Add provider validation tests in
tests/providers.test.mjs
- No
eval, noinnerHTMLwith user input anywhere in the codebase - No remote code — all domain data and rules ship in the extension package.
providers.jsmakesfetch()calls only when the user has configured a provider and a session is active (LLM inference, not code loading) - No telemetry —
heuristicPolicyand all session data stay inchrome.storage.local - API key in session storage only —
chrome.storage.sessionis cleared on browser close; the key is never written tochrome.storage.localorchrome.storage.sync - Domain validation on user input in Settings:
HOSTNAME_RE = /^[a-z0-9][a-z0-9\-.]*\.[a-z]{2,}$/— rejects IPs, wildcards, protocols, and paths - Fail-open — bad policy or missing storage key never throws to the caller; falls back to
buildDefaultPolicy('deep_work', 'balanced')
Intervention not firing?
- Open
chrome://extensions→ IntentLock → Service Worker → Inspect → Console - Check
evaluateDriftlogs — look for cooldown or debounce skips - Try Settings → Test intervention to confirm the pipeline works end-to-end
Overlay not showing?
- The content script may not be injected (Chrome/extension pages,
file://, some CSP-strict sites) - Check the tab's console for
content.jserrors - Background falls back to tab replacement (
intervention.html) if the content script doesn't respond
LLM not triggering?
- Settings → LLM provider — confirm provider + key are saved
- Settings → Diagnostics — check for
api_error,quota_exceeded,invalid_api_key - The
drift-cache.jscaches results for 60 seconds; wait or reload the session to force a fresh check
Policy not loading?
- Open Service Worker console →
chrome.storage.local.get(['heuristicPolicy'], console.log) - If absent,
loadConfig()will fall back todeep_work / balancedand log to the error log