Persist plan-HTML checkbox state in HTML attributes

Medium completed
2026-06-07 agentics refactor Medium effort

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.

Implement Read and implement all steps in the plan at docs/plans/persist-checkbox-state-in-html-attributes.md — Persist plan-HTML checkbox state in HTML attributes. Verify against the plan's Tests, Verification, and Acceptance Criteria before reporting done. If everything passed, mark completion in docs/plans/persist-checkbox-state-in-html-attributes.md — tick each step's [x] marker and each criterion's - [x], set status: completed — and re-render the HTML from the spec. If any check failed, leave status: in-progress and say which.
More ways to run this plan — goal & workflow prompts, file path
Pursue as goal — optimize for the outcome, in parallel
Achieve this goal: Persist plan-HTML checkbox state in HTML attributes. The plan at docs/plans/persist-checkbox-state-in-html-attributes.md describes one approach — use it as reference, but optimize for the outcome. Fan out across parallel subagents where that serves the outcome. Verify against the plan's Tests, Verification, and Acceptance Criteria before reporting done. If everything passed, mark completion in docs/plans/persist-checkbox-state-in-html-attributes.md — tick each step's [x] marker and each criterion's - [x], set status: completed — and re-render the HTML from the spec. If any check failed, leave status: in-progress and say which.
Run as workflow — launch parallel subagents
Run a workflow to implement the plan at docs/plans/persist-checkbox-state-in-html-attributes.md — Persist plan-HTML checkbox state in HTML attributes. Brief subagents with the plan file at docs/plans/persist-checkbox-state-in-html-attributes.md. Reserve a final verification phase for the lead agent, not a subagent. Verify against the plan's Tests, Verification, and Acceptance Criteria before reporting done. If everything passed, mark completion in docs/plans/persist-checkbox-state-in-html-attributes.md — tick each step's [x] marker and each criterion's - [x], set status: completed — and re-render the HTML from the spec. If any check failed, leave status: in-progress and say which.
File persist-checkbox-state-in-html-attributes.html
Path docs/plans/persist-checkbox-state-in-html-attributes.html
Spec docs/plans/persist-checkbox-state-in-html-attributes.md
Definition of done 7 / 7 done

Context

The story behind this plan — what prompted the work and why it matters now.

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 that change

Every file this plan touches, and what happens to each one.

agentics/
  • kit/plugins/plan-agent/skills/implementation-plan/reference/SKELETON.html modified remove localStorage, drive state from attributes
  • kit/plugins/plan-agent/skills/implementation-plan/SKILL.md modified attributes as portable source of truth
  • kit/plugins/plan-agent/CHANGELOG.md modified changelog entry for the change
  • tests/test-checkbox-portability.sh new portability smoke test
  • tests/fixtures/checkbox-portability/fixture.html new plan with pre-marked attributes

Steps

The step-by-step work, in order — each step says what to do, why it matters, and how to check it worked.

1
done Strip the localStorage layer out of reference/SKELETON.html
Why
Delete 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.
2
done Drive progress and completion from on-load attribute state in SKELETON.html
Why
Confirm 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
Open 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.
3
done Document HTML attributes as the portable source of truth in SKILL.md
Why
Update Step 6 (Status) and the Step 8 acceptance-criteria and completion gates to state explicitly that marking a criterion means adding the 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 .
4
done Add a portability smoke test and fixture
Why
Create 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.
5
done Record the change in kit/plugins/plan-agent/CHANGELOG.md
Why
Add a CHANGELOG entry describing the 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
The top of CHANGELOG.md shows the new entry above prior entries; no version field in marketplace.json was hand-edited (CI owns bumps).

Tests

The tests that prove the change does what it promises.

Tier 1 — Code-touching plan
Objective Checked state survives a move across machines 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
Unit Skeleton carries no browser-storage APIs 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.
Integration Attribute state lives in the file on disk 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.

Definition of done

The plan counts as done when every statement below is true — check each one off as you verify it.

Final check

One last pass to confirm the whole change works end to end.

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.

Wrapping up

Three gates that must all pass before this plan is marked completed.

Required

Completion Report

No items to report — all requirements met.

Next steps

Follow-up ideas that came up along the way — none of them are required to finish this plan.

Backfill existing plans to drop localStorage

Paste this prompt into Claude to execute this follow-up:

Run a workflow to scan every .html plan under docs/plans/ (skip docs/plans/archive/) for the localStorage-based checkbox persistence emitted by the old SKELETON.html — specifically STORAGE_KEY, saveState(), and restoreState() in the page's script. For each plan that still contains them, remove those functions and their calls and relabel the "Progress bar + localStorage" comment, matching the updated reference/SKELETON.html, without altering any checked attributes or .completed classes already in the file. Report a table of files changed and any that were already clean.
Add a "Save state to file" affordance for browser users

Paste this prompt into Claude to execute this follow-up:

Design and implement an optional "Save" button in reference/SKELETON.html that lets a person ticking checkboxes in the browser write their state back into the .html file as checked attributes — using the File System Access API (showSaveFilePicker / writable stream) on Chromium, with a download-updated-copy fallback elsewhere. On save, serialize the live DOM so every ticked input gains a checked attribute and every .step-card.completed is reflected. Keep attributes as the source of truth; do not reintroduce localStorage. Update SKILL.md and the portability test accordingly.