Lifestyle
Workshop: maintaining an existing project
Establish a baseline, implement one change and deliver traceable regression checks and handoff notes.
About 15 min read · Practice 30 min

Advanced · Desktop / CLI / VS Code / JetBrains / cloud
Before you start
On this page
Back to the Codex learning hubCodex learning hub: tutorial directoryA planned 60-lesson, ten-unit Codex curriculum, from setup and your first task to MD instructions and advanced integrations. Find your next lesson by experience, platform, goal or command; unpublished entries show their status.Read the full article
Goal and starting point
Lessons and resources mentioned here: exercise materials · Codebase mappingUnderstanding an existing codebaseUse a read-only workflow to locate entry points, data flow and tests, with file-backed explanations.Read the full article · Git recoveryGit, branches, diffs and recoveryGit stores file history, branches organize changes and diffs show what changed. Codex can help, but you must verify that the changes belong to the task. Restoring an old conversation does not restore Git files.Read the full article
Step 1: Record the baseline before rewriting
Open maintenance-lab and confirm the five code files: index.html, style.css, app.js, core.mjs and core.test.mjs. Run node --test core.test.mjs and expect three passing tests; record Node version, date and folder. If results differ, check whether you copied start or broken before attributing existing failures to the refactor. Create a local Git baseline commit or retain a complete baseline copy before editing so this change can be restored.
Also establish the UI baseline before editing: from this folder use the platform preview command in the UI regression section below, with a dedicated practice browser profile and origin. Select All tasks under Show and confirm there is no existing data; preserve any existing data and switch to another fresh practice profile for an empty baseline. Add Read and Build, complete Read and confirm 1 active / 2 total. In browser developer tools, under Application/Storage → Local Storage, save the fictional JSON for mokaair-codex-todo-v1, including both IDs and completion states. Keep these tasks, origin and browser profile for the after-edit comparison. Then open maintenance-lab in the desktop app and create a task, or run codex from a second terminal in this folder before submitting the next request.
Ask Codex to read the #count update in app.js and explain whether it counts all tasks or only filtered results. The baseline counts all tasks, even under Completed. Confirm storage key mokaair-codex-todo-v1, version 1, IDs and boolean completed fields. This refactor moves counting responsibility without changing the data format or adding a migration. Reading before editing prevents apparently duplicate code from being combined in ways that change behavior.
Step 2: Define a checkable change boundary
| Item | Requested change | Invariant |
|---|---|---|
| core.mjs | Add pure countTasks(tasks) | No input mutation, DOM or storage |
| app.js | Call countTasks for #count | Same text and all-task counts |
| maintenance.test.mjs | Add count/immutability cases | Preserve original core.test.mjs |
| HTML/CSS | No change needed | Keep controls, layout and focus style |
| Stored data | No migration | Preserve key, version, IDs and states |
A small scope still needs acceptance; it helps connect failures to a few changes.
Step 3: Add tests that initially fail
Save maintenance.test.mjs beside the original tests. Run both before implementation: the new three fail because the function is missing, while the original three pass. Retain this expected failure to show the tests detect missing behavior. Do not mistake it for a defective fixture or let Codex delete tests to get green. Inputs satisfy the existing Task contract; this does not expand the function into an arbitrary data-cleaning tool.
import test from 'node:test';
import assert from 'node:assert/strict';
import * as core from './core.mjs';
test('empty list counts are zero', () => {
assert.deepEqual(core.countTasks([]), { active: 0, total: 0 });
});
test('count all tasks without deduplicating equal titles', () => {
const tasks = [
{ id: 'a', title: 'Read', completed: false },
{ id: 'b', title: 'Read', completed: true },
{ id: 'c', title: 'Build', completed: false },
];
assert.deepEqual(core.countTasks(tasks), { active: 2, total: 3 });
});
test('counting preserves frozen task data and storage compatibility', () => {
const task = Object.freeze({ id: 'a', title: 'Read', completed: true });
const tasks = Object.freeze([task]);
const before = core.encodeTasks(tasks);
assert.deepEqual(core.countTasks(tasks), { active: 0, total: 1 });
assert.equal(core.encodeTasks(tasks), before);
assert.deepEqual(core.decodeTasks(before), tasks);
});
node --test core.test.mjs maintenance.test.mjs
Step 4: Implement and inspect the diff
Add countTasks(tasks) to core.mjs, returning {active, total} for the full list.
Use it in app.js when updating #count; keep the existing visible text.
Preserve storage format, key, IDs, task behavior and original tests.
Do not edit index.html, style.css or the new test expectations.
Run node --test core.test.mjs maintenance.test.mjs.
Report changed files, test results and remaining browser verification.
Expect six passing tests afterward. The reference helper is below. app.js imports it and counts tasks in render before composing the unchanged text. Check that it receives tasks rather than shown, the filtered array, which would display wrong totals under Completed. Inspect git diff or editor changes: original tests, HTML, CSS and storage behavior should be unchanged. Passing pure-function tests does not replace checking the UI connection.
export function countTasks(tasks) {
return {
active: tasks.filter((task) => !task.completed).length,
total: tasks.length,
};
}
Add countTasks to app.js's existing import, retaining its six functions. Replace only render's #count.textContent assignment with the second fragment. Keep the rest of render, including the earlier shown filter. These are fragments for specific locations, not a complete app.js.
import { countTasks, addTask, toggleTask, removeTask, visibleTasks, decodeTasks, encodeTasks } from "./core.mjs";
const counts = countTasks(tasks);
document.querySelector("#count").textContent = `${counts.active} active / ${counts.total} total`;
Check the argument is tasks. With Read completed and Build active, Completed's shown subset has one item. Passing shown would display 0 active / 1 total instead of 1 active / 2 total. The six core tests can still pass because they test countTasks's contract, so retain this separate wiring and page check.
Step 5: Recheck the UI and saved data
If the same pre-edit practice server is still running, reload its URL instead of starting a second server. Use the following command only if it has stopped. Start preview in this folder with py -3 -m http.server 4173 --bind 127.0.0.1 on Windows or python3 -m http.server 4173 --bind 127.0.0.1 on macOS/Linux, then open http://127.0.0.1:4173. If occupied, identify the existing service before stopping it. Use the same origin and fictional tasks before/after; a different port has different localStorage and can look like data loss. Keep the pre-edit Read and Build tasks without resetting or adding them again. Compare their stored IDs and completion states with the baseline, then select All tasks, Active and Completed. All three must show 1 active / 2 total.
Reload and confirm both tasks, IDs and completion states survive. Using these same two tasks, check filters and counts at both 390px and desktop width first. Then select All tasks and delete Read once, expecting 1 active / 1 total; use Tab to check visible focus. These are regression checks, without a new design. If counts change with filtering, inspect app.js arguments. If the helper itself is wrong, use the new failing case to locate it. Mark browser checks passed only after observing them, and label untested systems separately.
Recovery and handoff
To revert, save the current diff, restore only this change's core.mjs and app.js, and move aside your new maintenance.test.mjs. Rerun the original three tests and count UI to regain the baseline. Do not force-reset the entire project and erase others' edits. Handoff records the baseline, extracted responsibility, unchanged data contract, six tests, observed UI checks and recovery location; unrun tests are not passes. Give the next maintenance request its own scope using Context handoffContext and task handoffLong work needs durable decisions and evidence, not just a long conversation. README explains use, design documents explain choices, handoffs record current progress and AGENTS.md holds ongoing instructions. Do not turn all temporary progress into permanent rules.Read the full article, rather than expanding this refactor into storage or framework replacement. Stop your own preview server with Ctrl+C in its terminal after checking, and retain the baseline JSON and acceptance notes.
Back to the Codex learning hubCodex learning hub: tutorial directoryA planned 60-lesson, ten-unit Codex curriculum, from setup and your first task to MD instructions and advanced integrations. Find your next lesson by experience, platform, goal or command; unpublished entries show their status.Read the full article
Read the full description
Three numbered stages: identify the starting point, perform the exercise, and verify the result. Original illustration, not a product screenshot.
Lifestyle
Codex learning hub: tutorial directory
A planned 60-lesson, ten-unit Codex curriculum, from setup and your first task to MD instructions and advanced integrations. Find your next lesson by experience, platform, goal or command; unpublished entries show their status.
Lifestyle
Worktrees and isolated tasks
A Git worktree gives one repository multiple working directories on different branches. It isolates file edits, but databases, ports and external services may still be shared. File isolation is not full resource isolation.
Lifestyle
Workshop: build a small website
Plan and build the Small Steps task website from brief.md, with adding, completing, deleting, filtering and local persistence. Separate HTML, CSS, data functions, UI events and tests, verify with Node and browser checks, and document restart and recovery steps.
Lifestyle
Usage and efficiency: reducing rework
Record task conditions, model options, time and outcomes to reduce unnecessary retries and excess context.
Articles that cite this one
Latest travel guides

GuideTokyo
Where to Stay in Tokyo: Comparing Shinjuku, Ueno, Tokyo Station, Shibuya, Asakusa, Ikebukuro, and Ginza, Plus Airport Access, Accommodation Tax, and Luggage Delivery
Where should you stay in Tokyo? Compare Shinjuku, Ueno, Tokyo Station, Shibuya, Asakusa, Ikebukuro, and Ginza by the same criteria: access from Narita and Haneda, transit routes, nearby attractions, neighborhood character, and who each area suits. Includes a comparison table, a Yamanote Line diagram, Tokyo’s accommodation tax as verified in 2026/9 (changing to 3% in 2027/4), and Airport TA-Q-BIN luggage shipping rules.
- Budget
- Hotels

GuideTokyo
How to Choose Tokyo Transit Passes: Are Suica, Welcome Suica, the Tokyo Subway Ticket, and the JR Pass Worth It?
On a first Tokyo trip, start with an IC card and pay per ride (Welcome Suica has no deposit and is valid for 28 days). If you take four or more subway rides in a day, add a 72-hour Tokyo Subway Ticket for 2,000 yen; a JR Pass is never worthwhile if you stay in Tokyo and do not go to Kansai. See what TOURIST PASMO, Suica on iPhone, and the Tokyo Metro day pass do and do not cover, with a decision chart. Prices verified in September 2026.
- Transport
- Budget

GuideTokyo
Tokyo Disneyland and DisneySea Guide: Ticket Prices, Fantasy Springs, Disney Premier Access (DPA), Standby Pass, and Which Park to Choose for Your First Visit
Tokyo Disney one-day Passport prices vary: most weekdays in 9/2026 cost ¥9,900 and weekends ¥10,900. At 14:00 daily, tickets go on sale for the same date two months later. Free Priority Pass is no longer on the official service list; only paid Disney Premier Access (¥1,000–3,500 per person per use) shortens waits. Covers hours, the 25th anniversary, Standby Pass, Entry Request, Fantasy Springs access and first-visit park choice; checked on the official site in 9/2026.
- Itineraries
- Family
Sources
- Codex prompting · Checked:
- Git worktree guidance · Checked: