Runtime Decoupling Refactor Implementation Plan
Runtime Decoupling Refactor Implementation Plan
For Claude: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
Goal: Reduce coupling across runtime modules while keeping current endpoint and datetime behavior unchanged.
Architecture: Split runtime responsibilities into independent modules: endpoint parsing, translation state detection, locale decision, and time rendering. Keep language-router focused on routing/translation trigger, and make time-localizer consume a stable state interface instead of directly reading multiple side channels. Preserve behavior through test-first migration.
Tech Stack: Jekyll, vanilla JavaScript (IIFE modules), Node assert tests.
Task 1: Add behavior lock tests for current runtime contract
Files:
- Create:
tests/runtime-locale-decision.test.js - Modify:
tests/endpoint-utils.test.js - Test:
tests/runtime-locale-decision.test.js,tests/endpoint-utils.test.js,tests/time-localizer.test.js
Step 1: Write the failing test
// tests/runtime-locale-decision.test.js
const assert = require('assert');
const { resolveDisplayLocale } = require('../assets/js/endpoint-utils');
assert.strictEqual(resolveDisplayLocale('fr', null, 'fr', 'en-US', true), 'en-US');
assert.strictEqual(resolveDisplayLocale('fr', 'de', 'de', 'en-US', false), 'fr');
assert.strictEqual(resolveDisplayLocale('fr', 'de', 'de', 'en-US', true), 'de');
Step 2: Run test to verify it fails
Run: node tests/runtime-locale-decision.test.js
Expected: FAIL (new file not complete or missing cases)
Step 3: Write minimal implementation
Use current behavior as source of truth and fill complete assertions for:
- no endpoint -> browser default
- endpoint pending -> keep current
- endpoint confirmed -> switch
- clear endpoint after previous success -> browser default
Step 4: Run test to verify it passes
Run: node tests/runtime-locale-decision.test.js && node tests/endpoint-utils.test.js && node tests/time-localizer.test.js
Expected: PASS
Step 5: Commit
git add tests/runtime-locale-decision.test.js tests/endpoint-utils.test.js
git commit -m "test: lock runtime locale decision behavior"
Task 2: Extract translation status detection into a standalone module
Files:
- Create:
assets/js/translation-status.js - Modify:
assets/js/time-localizer.js - Modify:
_includes/head.html - Test:
tests/translation-status.test.js
Step 1: Write the failing test
// tests/translation-status.test.js
const assert = require('assert');
const { isTranslationAppliedFromClassName } = require('../assets/js/translation-status');
assert.strictEqual(isTranslationAppliedFromClassName('translated-ltr'), true);
assert.strictEqual(isTranslationAppliedFromClassName('translated-rtl foo'), true);
assert.strictEqual(isTranslationAppliedFromClassName('foo bar'), false);
Step 2: Run test to verify it fails
Run: node tests/translation-status.test.js
Expected: FAIL with module not found
Step 3: Write minimal implementation
function isTranslationAppliedFromClassName(className) {
var value = String(className || '');
return /(^|\s)translated-(ltr|rtl)(\s|$)/.test(value);
}
Export for Node and attach for browser in IIFE style.
Step 4: Run test to verify it passes
Run: node tests/translation-status.test.js
Expected: PASS
Step 5: Commit
git add assets/js/translation-status.js assets/js/time-localizer.js _includes/head.html tests/translation-status.test.js
git commit -m "refactor: extract translation status detector"
Task 3: Extract datetime rendering into pure helper module
Files:
- Create:
assets/js/time-format.js - Modify:
assets/js/time-localizer.js - Test:
tests/time-format.test.js
Step 1: Write the failing test
// tests/time-format.test.js
const assert = require('assert');
const { formatDateTime } = require('../assets/js/time-format');
const iso = '2026-03-03T14:17:15.000Z';
const out = formatDateTime(iso, 'en-US', 'Asia/Shanghai');
assert.ok(typeof out === 'string' && out.length > 0);
Step 2: Run test to verify it fails
Run: node tests/time-format.test.js
Expected: FAIL with module not found
Step 3: Write minimal implementation
function formatDateTime(input, locale, timeZone) {
var date = new Date(input);
if (Number.isNaN(date.getTime())) return null;
var options = timeZone ? { timeZone: timeZone } : undefined;
return date.toLocaleString(locale || undefined, options);
}
Step 4: Run test to verify it passes
Run: node tests/time-format.test.js
Expected: PASS
Step 5: Commit
git add assets/js/time-format.js assets/js/time-localizer.js tests/time-format.test.js
git commit -m "refactor: isolate datetime formatting helper"
Task 4: Introduce runtime state channel and remove cross-module probing in localizer
Files:
- Create:
assets/js/runtime-state.js - Modify:
assets/js/language-router.js - Modify:
assets/js/time-localizer.js - Modify:
_includes/head.html - Test:
tests/runtime-state.test.js
Step 1: Write the failing test
// tests/runtime-state.test.js
const assert = require('assert');
const { createRuntimeState } = require('../assets/js/runtime-state');
const s = createRuntimeState();
s.setEndpoint('fr');
s.setTranslatedTarget('fr');
s.setTranslationApplied(true);
assert.deepStrictEqual(s.get(), {
endpoint: 'fr',
translatedTarget: 'fr',
translationApplied: true
});
Step 2: Run test to verify it fails
Run: node tests/runtime-state.test.js
Expected: FAIL with module not found
Step 3: Write minimal implementation
Implement a tiny state container with:
get()setEndpoint(value)setTranslatedTarget(value)setTranslationApplied(value)- optional
subscribe(listener)for future decoupling
language-router writes endpoint and target updates to this channel.
time-localizer reads only this channel + browser locale/timezone.
Step 4: Run test to verify it passes
Run: node tests/runtime-state.test.js && node tests/endpoint-utils.test.js && node tests/time-localizer.test.js
Expected: PASS
Step 5: Commit
git add assets/js/runtime-state.js assets/js/language-router.js assets/js/time-localizer.js _includes/head.html tests/runtime-state.test.js
git commit -m "refactor: add runtime state channel for low coupling"
Task 5: Narrow MutationObserver scope and keep behavior
Files:
- Modify:
assets/js/time-localizer.js - Modify:
assets/js/language-router.js - Test:
tests/runtime-observer-scope.test.js
Step 1: Write the failing test
// tests/runtime-observer-scope.test.js
const assert = require('assert');
const { resolveObserverRootSelector } = require('../assets/js/time-localizer');
assert.strictEqual(resolveObserverRootSelector(), '.post-content, .page-content, .home');
Step 2: Run test to verify it fails
Run: node tests/runtime-observer-scope.test.js
Expected: FAIL (helper missing)
Step 3: Write minimal implementation
Expose and use one selector constant for observer roots:
.post-content.page-content.home
Fallback to document.body only when none exists.
Step 4: Run test to verify it passes
Run: node tests/runtime-observer-scope.test.js && node tests/endpoint-utils.test.js && node tests/time-localizer.test.js
Expected: PASS
Step 5: Commit
git add assets/js/time-localizer.js assets/js/language-router.js tests/runtime-observer-scope.test.js
git commit -m "refactor: narrow observer scope to content containers"
Task 6: Cleanup duplicated helpers and stale branches
Files:
- Modify:
assets/js/endpoint-utils.js - Modify:
assets/js/language-router.js - Modify:
assets/js/time-localizer.js - Test:
tests/endpoint-utils.test.js,tests/time-localizer.test.js
Step 1: Write the failing test
Add assertions that old side-channel fallback branches are no longer needed once runtime state channel is present.
Step 2: Run test to verify it fails
Run: node tests/endpoint-utils.test.js && node tests/time-localizer.test.js
Expected: FAIL on deprecated paths
Step 3: Write minimal implementation
Remove duplicated resolution paths and keep single source for:
- endpoint selection
- translation applied status
- display locale decision
Do not change external behavior.
Step 4: Run test to verify it passes
Run: node tests/endpoint-utils.test.js && node tests/time-localizer.test.js
Expected: PASS
Step 5: Commit
git add assets/js/endpoint-utils.js assets/js/language-router.js assets/js/time-localizer.js tests/endpoint-utils.test.js tests/time-localizer.test.js
git commit -m "refactor: remove duplicated runtime branches"
Task 7: Final verification and docs
Files:
- Modify:
docs/plans/2026-03-04-runtime-decoupling-refactor.md - Optional:
README.md(if you choose to add runtime architecture notes)
Step 1: Run full verification
Run:
node tests/runtime-locale-decision.test.jsnode tests/translation-status.test.jsnode tests/time-format.test.jsnode tests/runtime-state.test.jsnode tests/runtime-observer-scope.test.jsnode tests/endpoint-utils.test.jsnode tests/time-localizer.test.js
Expected: all PASS
Step 2: Capture architecture summary
Document final module boundaries and data flow in this plan file under a Final Architecture section.
Step 3: Commit
git add docs/plans/2026-03-04-runtime-decoupling-refactor.md
git commit -m "docs: record runtime decoupling architecture and verification"