Make a plan's completion state travel with the file. Persist every step and acceptance-criterion as an HTML checked attribute (and .completed class) written by the agent, and rip out the per-browser localStorage layer so a plan renders identically on any machine, in any browser, and in git.
Read and implement all steps in the plan at docs/plans/persist-checkbox-state-in-html-attributes.html — Make plan-HTML checkbox state portable via HTML attributes
persist-checkbox-state-in-html-attributes.html
docs/plans/persist-checkbox-state-in-html-attributes.html
Context
Every plan generated by plan-agent ships an interactive checklist. Today the acceptance-criteria checkboxes persist their ticked state to localStorage (see saveState()/restoreState() in SKELETON.html, keyed by document.title). localStorage is per-browser and per-origin — it is never written back into the .html file. The result: copy a plan to another machine, open it in a different browser, or commit it to git, and every tick is gone. The on-disk file always shows an empty checklist.
There is also a silent divergence: restoreState() only ever adds checks (if (state[i]) cb.checked = true) and can never uncheck, so an HTML checked attribute and the localStorage snapshot can drift apart. Step completion already travels correctly — it is encoded as a .completed class on each .step-card, which lives in the file — but acceptance criteria do not.
The decision (confirmed up front): make the HTML checked attribute the single, portable source of truth, written into the file by the agent during implement/finalize. Browser ticks stay ephemeral, and the localStorage layer is removed entirely so there is nothing to diverge from. This mirrors how step state already works and makes the whole plan self-describing on disk.
Files to Modify
kit/plugins/plan-agent/skills/implementation-plan/reference/SKELETON.htmlmodified remove localStorage, drive state from attributeskit/plugins/plan-agent/skills/implementation-plan/SKILL.mdmodified attributes as portable source of truthkit/plugins/plan-agent/CHANGELOG.mdmodified changelog entry for the changetests/test-checkbox-portability.shnew portability smoke testtests/fixtures/checkbox-portability/fixture.htmlnew plan with pre-marked attributes
Steps
Steps 1–2 must complete before Step 3 (SKILL.md docs depend on the final behavior). Step 4 (fixture + test) may run after Step 1 to support Step 2's verify, but must be committed together.
localStorage layer out of reference/SKELETON.html
STORAGE_KEY, saveState(), restoreState(), the saveState() call inside the criteria change listener, and the restoreState() invocation; relabel the section comment from "Progress bar + localStorage" to "Progress bar — state from HTML attributes". Why: localStorage is per-browser and never written to the file, so it cannot carry state across machines or into git — removing it makes the HTML checked attribute the single source of truth with nothing to diverge from.Verify
grep -c -E 'localStorage|STORAGE_KEY|saveState|restoreState' reference/SKELETON.html returns 0. The IIFE still defines updateProgress() and a change listener, but no persistence functions remain.SKELETON.html
updateProgress() and updateCompletion() still run once on load, reading cb.checked (the native reflection of the HTML checked attribute) and the .step-card.completed classes; keep the live change listeners for in-browser feedback but with no persistence side effect. Why: with localStorage gone, the file's attributes and classes must be the only inputs to the progress bar and completion checklist so a freshly opened file on any machine renders the true state.Verify
tests/fixtures/checkbox-portability/fixture.html with two of three criteria carrying checked attributes: the progress label reads "2 / 3 done" and the bar fills to ~67% on first paint, before any click.SKILL.md
checked attribute (<input type="checkbox"> → <input type="checkbox" checked>) and unmarking means removing it — an attribute edit, not a JS property toggle. Add a clause to the "readable without JavaScript" bullet under HTML Output Requirements naming the checked attribute as the portable source of truth and forbidding localStorage. Why: the agent is now the sole writer of canonical state, so the skill must instruct it to edit attributes consistently and prevent any future reintroduction of browser-only persistence.Verify
grep -n "checked" SKILL.md shows the add/remove-attribute guidance in Steps 6 and 8; the HTML Output Requirements bullet names the checked attribute as the source of truth and excludes localStorage.tests/fixtures/checkbox-portability/fixture.html (a minimal plan with some criteria pre-marked via checked attributes and a step pre-marked via .completed) and tests/test-checkbox-portability.sh that asserts (a) reference/SKELETON.html contains no localStorage, and (b) the fixture's checked attributes and .completed class are present in the file on disk. Why: locks in the portability guarantee so a later edit cannot silently reintroduce localStorage or break attribute-driven state. Follows the repo's shell-test convention (e.g. tests/pages/test-pages-smoke.sh).Verify
bash tests/test-checkbox-portability.sh exits 0; temporarily re-adding localStorage to the skeleton makes it exit non-zero.kit/plugins/plan-agent/CHANGELOG.md
localStorage removal and the move to attribute-based, portable checkbox state. Why: repo convention requires a CHANGELOG entry plus a conventional-commit message (refactor(kit/plugins/plan-agent): …) so CI applies the correct version bump after merge.Verify
CHANGELOG.md shows the new entry above prior entries; no version field in marketplace.json was hand-edited (CI owns bumps).Tests
File: tests/test-checkbox-portability.sh
Type: smoke test
Asserts: a plan whose criteria carry checked attributes (and whose step carries .completed) keeps those exact marks in the file bytes — re-reading the file from a clean shell (the proxy for a different machine, with no shared localStorage) still sees them — while reference/SKELETON.html contains zero browser-storage calls.
Run: bash tests/test-checkbox-portability.sh
File: tests/test-checkbox-portability.sh
Targets: the JS IIFE in reference/SKELETON.html
Key cases: grep finds 0 occurrences of localStorage, STORAGE_KEY, saveState, and restoreState; updateProgress is still defined.
File: tests/fixtures/checkbox-portability/fixture.html exercised by test-checkbox-portability.sh
Targets: the fixture's <input ... checked> criteria and .step-card.completed markers
Key cases: the checked attributes and completed class are present in the raw file; a fresh read (no prior browser session) returns the same marks, proving portability.
Acceptance Criteria
Verification
Generate a fresh plan from the updated skeleton, then have the agent mark one step done (add .completed) and tick one acceptance criterion (add the checked attribute). Copy the .html file to a second location — or open it in a different browser profile, or commit and re-clone — and confirm the step and criterion render as marked, with the progress bar reflecting the real count on first paint. The marks must travel with the file, not with the browser. Finally run bash tests/test-checkbox-portability.sh and confirm it exits 0, and grep -c localStorage reference/SKELETON.html returns 0.
Completion Checklist
These items are computed automatically from step and criteria state — they cannot be ticked manually.
Completion Report
No items to report — all requirements met.
Team Review (2026-06-08 00:04:12 UTC)
Executive Summary
The plan is sound with revisions. All seven reviewers endorse the core architecture (attribute-based persistence replacing localStorage), but unanimously flagged that the plan file itself still contained the localStorage code it proposes to remove — a self-referential inconsistency now fixed in this pass. Secondary concerns center on test path conventions (resolved: concern-based naming), completion-checklist ambiguity (resolved: documented as computed-only), and accessibility gaps (resolved: prefers-reduced-motion, aria-labels). No reviewers recommended rejection.
Architecture
Architecturally sound. Correctly identifies the single-source-of-truth violation and aligns with existing .completed class pattern. Recommended clarifying completion-list as computed-only and noting step ordering dependencies — both applied.
Completeness
Well-scoped and specific with exact functions, grep commands, and file paths named. Gaps: test directory scaffolding (mkdir) not explicit, Step 2 verify references fixture created in Step 4. Mitigated by adding step ordering note; mkdir is implicit in file creation tools.
Testability
Clear objective-verification test and sub-tests. Gap: shell grep tests cannot verify rendered DOM behavior (progress bar on first paint). The plan’s Step 2 verify requires manual browser confirmation; this is acceptable given the repo’s shell-test convention. A Playwright E2E test is noted as a future enhancement in Next Steps.
Risk
Risk level: low. No breaking API changes, no shared state, no concurrency hazards. Main risk is orphaning existing localStorage ticks during backfill — scoped to a next-step with appropriate warning. Rollback is trivial (revert one commit).
Conventions
Good fit with project patterns. Test paths corrected from tests/plan-agent/ (plugin-scoped, new pattern) to tests/test-checkbox-portability.sh and tests/fixtures/checkbox-portability/ (concern-based, existing pattern).
UX
Readable and well-structured for developer audiences. Completion checklist “disabled” state now has an explanatory note. Browser ticks being ephemeral is documented in Context. Progress label clarified as tracking acceptance criteria specifically.
Accessibility
Largely WCAG 2.1 AA compliant. Fixes applied: prefers-reduced-motion gate for pulse-dot animation, aria-label on status badge, aria-description on disabled completion items. Remaining low-severity items (touch target size, details/summary toggle text) noted for skeleton-level fix.
Agreements
- Confirmed (7/7): Plan’s own script had localStorage — now removed.
- Confirmed (4/7): Step ordering dependencies should be explicit — note added.
- Confirmed (3/7): Completion-list computed-only needs documentation — AC #7 + note added.
- Confirmed (3/7): Grep-only tests don’t cover rendered behavior — acknowledged; browser verify is manual per convention.
Conflicts
None. All recommendations are additive and compatible.
Highest-Risk Issues (resolved)
- Self-referential localStorage in plan script (fixed this pass)
- No browser-level test for first-paint rendering (accepted: shell-test convention; Playwright noted as next-step)
- Completion-list persistence ambiguity (fixed: documented as computed-only, AC #7 added)