Exploring, Fixing, Refactoring, and Using Git
Here Task Notes becomes a small codebase. You will learn to orient yourself without reading everything, to distinguish a correction from a refactoring and to use Git as a tool for understanding and recovery.
What you will tackle, step by step
Build a map before editing
Faced with a new codebase, reading everything is almost always the slowest way to understand. You need a map of the relevant path: where the data comes in, where it is transformed, where it is stored, and where it appears.
Exploring does not mean opening every file. Start from the structure, identify the entry point, follow the data from the input boundary to the output and find the tests that describe the behavior. This map reduces context and limits assumptions.
Ask the agent to cite the files and symbols that support its explanation. If the bug appears in the interface but originates in the parser, changing the view hides the symptom without fixing the cause.
In the CSV the priority is `high`, but Task Notes shows it as `normal`.
- Start from the import command and identify `import_csv()` as the input, without opening the entire project yet.
- Follow the value in `parser.py`: the column is read correctly and passed to the validator.
- In the validator you find a list that accepts only English values; `high` then falls to the default value before reaching the store.
- Compare the tests and documentation of the format to understand whether the contract requires Italian, English or both before proposing the patch.
Result: The view is not affected: you have located the first point where the data changes and you have also identified the product decision to clarify.
From symptom to hypothesis
The symptom is what you observe; the hypothesis is your provisional explanation. Keeping them separate is essential: you can be certain of the symptom and still be wrong about the cause.
Good reproduction contains input, action and observed outcome. Make it a failing test before the patch when practical: the test prevents the solution from chasing an abstract idea and becomes future protection.
State a single primary hypothesis and describe what the test should observe if it were true. If the experiment disproves it, update the map; do not enlarge the modification immediately.
- Play.
- Locate the responsible boundary.
- Write the hypothesis.
- Fail a targeted test.
- Apply minimal patch and rerun.
A user reports that when they double-click “Save,” two identical tasks sometimes appear.
- Describe the minimal case: title `Pay bill`, two quick clicks, two records observed instead of one.
- Assume that the button remains active while the first request is in progress; therefore expect two almost simultaneous requests in the log.
- The interaction test simulates two clicks and fails after showing two calls. At this point you are no longer guessing: the test shows the behavior you predicted.
- You disable the button while saving, rerun the same test, and also check that an error re-enables the button for another attempt. If a duplicate would have serious consequences, also add protection on the server: the same request must not create two records after a network retry.
Result: The test protects both the absence of the duplicate and the possibility of trying again. The patch responds to observed behavior, not a general guess about the network.
Bug fixes and refactoring are not synonymous
A fix and a refactoring may touch the same function, but they answer different questions. Correction changes bad behavior; refactoring changes the structure without changing the protected behavior.
The bug fix intentionally changes incorrect behavior. Refactoring improves the structure while maintaining the verified behavior. Separating them makes the diff readable and allows you to attribute any failure to the right change.
If a feature is difficult to fix, apply the minimal patch first. After the tests pass, evaluate a second structural change with unchanged tests. Two commits better describe two intentions.
`total(100, 20)` returns `99.8` because the function subtracts `20 / 100` instead of applying 20 percent to the price. Here we isolate the formula; in a real app, we would use whole cents or a suitable decimal type for money.
- Add test `total(100, 20) == 80` and run it on current version: fails with observed value.
- In the first change just replace the formula with `price * (1 - discount / 100)` and rerun the existing cases.
- Check and record a minimal diff containing test and formula, without moving the function or renaming the entire module.
- In a second change you introduce names like `discount_percentage` and separate the validation, keeping the same tested results unchanged.
Result: You can release or undo the fix regardless of the structural cleanup, and each review has a specific question to answer.
Git as a readable safety net
Git is useful when it tells an understandable story: what was there before, what you changed and why. It's not a license to blindly delete, especially when there is work in the same folder that doesn't belong to your mission.
`status` shows the state of the working tree, `diff` tells about the change, and an atomic commit preserves an understandable point. Before restoring something, check if there are user changes that do not belong to the task.
Prefer recoverable operations. A revert adds a correction to the shared story; rewriting or erasing history can only be appropriate in a controlled context and with clear authority.
You fixed `parser.py`, but `git status` also shows changes in `theme.css` and a new `notes-private.txt` file that you didn't create.
- You don't restore or add everything. You examine the diff of `parser.py` and confirm that it contains only the agreed upon patch and test.
- You don't know where `theme.css` and the untracked file come from: you treat them as work that isn't yours, you leave them intact and report that they were already there.
- Explicitly prepare only the fix files and check `git diff --staged` before committing.
- Run the relevant test and create a message that describes the correct behavior, not a vague message like “miscellaneous updates”.
Result: The commit contains only one intent and the extraneous work remains in the working tree. The safety net does not become a source of data loss.
A poorly applied discount
A paid task's total treats the percentage as an absolute value.
# Bug
def total(price, discount):
return price - discount / 100
# Minimal patch
def total(price, discount):
return price * (1 - discount / 100)