hamsa software
← All articles
AI tooling7 min read

Give the coding assistant one complete behaviour to finish

A filter-retention brief becomes reviewable when Back, reload, copied links and account switches have explicit expected outcomes.

Two crossing orbital rings surround a central circle, suggesting interface state returning through a navigation journey.

“Remember the filter” leaves the hard decision unwritten

Open invoice I17 from the overdue list, then go back: the filter has returned to all. “Remember the filter” sounds like a small enough instruction for a coding assistant, but component state, history state, session storage and a URL would all be plausible replies. They remember different things for different lengths of time. Before judging the patch, the brief needs to say which return journey it is meant to repair and where else that remembered choice should follow the person.

Does the filter need to survive a component remount, browser Back, a full reload or a copied link opened in another tab? Should it carry into a different account? Is the remembered value an innocuous display choice, a private search term or an account identifier? These questions determine the state boundary. Asking for a smaller task is useful only if the smaller task contains a complete behaviour contract rather than a shorter but equally ambiguous sentence.

The illustrative brief here is precise. Preserve one non-sensitive status filter when returning to the list, reloading it or opening its copied URL. Permit all, open and overdue. Reset the list to all on an explicit account switch. Keep account identity and authorisation outside the shareable filter URL. Do not change invoice calculations, data permissions, pagination or the component's visual design. The desired change is small enough to review because the accepted state and its lifetime are explicit.

Choose the memory that matches the journey

Component-local state is a good fit for a choice that lasts as long as its component. It is less helpful when navigating away discards that instance and the new list starts at all. Calling a save helper before navigation does not show that anything reads the value on return. The acceptance check needs to reach the moment the person needs overdue again. That is where this representation either keeps the promised behaviour or reveals that the helper successfully saved something nobody restores.

History-entry state can preserve navigation-specific information without making it part of a copied URL. The History API manipulates session history and supports navigation between entries.[1] It can therefore be relevant to returning from detail to list. But a colleague opening a copied address does not receive the original tab's whole history entry. If copied-link behaviour belongs in the brief, history state alone does not express that part of the requirement. Different lifetimes should not be collapsed into remembered.

Session storage is another candidate. MDN documents its origin-and-tab partition and that it survives reloads within the page session.[2] That can be useful for temporary, non-shareable state. It does not make a filter appear in an independently copied URL, and account changes within one origin still need an application policy. A global storage key named invoiceFilter can also apply stale preferences in a new account unless the application deliberately resets or scopes it. Persistence is not automatically the same thing as correct persistence.

For this particular brief, the URL is a suitable home for the harmless status choice: /invoices?status=overdue. A new list instance parses the same allow-listed value regardless of how it was reached. Unknown values fall back to all under the proposed contract. The account used to fetch data still comes from authenticated application context, so a status parameter never grants access to invoices. If the filter instead contained private text, the shareable-URL requirement would need reconsideration rather than mechanically copying this representation.

Put the contract into inspectable examples

The downloadable Node checks use the standard URL interface to construct and parse the address.[3] Reading the saved address again or copying it preserves overdue; an unknown status and the account-switch URL both resolve to all. The component-local counterexample loses overdue on a modelled remount. Those executed representation checks establish the state values to integrate with the real router. They do not navigate a browser. The next review therefore has a specific target: whether the displayed control and authenticated request use the parsed value when the person returns.

A router can preserve the address while another part of initialisation overwrites the recovered filter. The list then looks as though it remembered the journey until its request fetches every invoice. Follow the parsed value through to both outputs: the visible control and the query sent for data. This is the point a real browser check needs to observe. A correct parsing result should become one agreed input to those components, rather than compete with a second default that runs later and silently wins.

The history interaction still needs a decision. Selecting a filter can add an entry if Back should traverse previous choices, or replace the current entry if it should not. Opening a detail must preserve the intended list address. Without an agreed owner, the URL can say overdue while the control shows all, or the control can say overdue while the request fetches everything. One source of truth is useful only when the update and restoration rules make all three parts tell the same story.

A focused brief should name existing conventions too. If the application's router already parses query parameters and handles navigation events, use its documented mechanism rather than adding an unrelated History API layer. The principle is not to replace every state library with manual URL parsing. It is to select a representation whose behaviour matches the stated lifetime, then integrate it where that behaviour already belongs. A small patch can become harder to reason about when it introduces a second owner for the same navigation state.

Which state lifetime does the brief actually require?

Proposed architecture comparison plus executed Node URL/state checks. Actual browser/router behaviour was not tested.

Scroll sideways to see all columns.

Journey / propertyLocal component stateHistory-entry stateChosen status URL
List remountLost unless separately restoredNeeds restoration integrationParse overdue from address
Browser BackNot established by local stateCan represent prior entryPrior list address retains status
Full reloadRequires another sourceApplication/browser behaviour mattersParse same allowed status
Independent copied linkNo selection conveyedEntry state not conveyedoverdue conveyed
Account switchExplicit reset neededExplicit reset neededReplace with /invoices; all
Data authorityNot authorisationNot authorisationServer account remains independent
The Node checks recover the allowed status from an address. Actual Back navigation, the visible control and the authenticated data request still need browser verification.

Review the behaviour rather than the assistant's chosen helper

The acceptance path starts at the list, selects overdue, opens I17 and goes back. The visible selection and the fetched list must both be overdue. Reloading the list must produce the same result. Opening the copied address in a fresh authorised context must preserve the status while using that context's legitimate account. Switching accounts through the application resets to all by the chosen policy. An unrecognised status must not silently become an arbitrary backend query parameter.

A spy can confirm that the helper ran while the journey remains broken. Its key may never be read, navigation may discard the query, or initialisation may overwrite the recovered value. Observe the filter where it is needed, together with the resulting list. Internal-call tests can still protect important implementation boundaries, but they need a clear relationship to the promised behaviour. A passing helper assertion and a restored overdue list are different pieces of evidence, however similar their test names look.

The exclusions keep the review proportionate. If an assistant proposes a table redesign, a new state-management dependency or a change to invoice permissions, ask which accepted journey requires it. Sometimes existing architecture makes a broader prerequisite necessary; then explain that dependency and review it deliberately. The original brief is not a prohibition on understanding the surrounding code. It is a way to stop incidental improvements from obscuring whether the requested filter behaviour was delivered.

There are trade-offs even in this narrow choice. A shareable status URL is reproducible, but a copied link can outlive a renamed filter value, and the server must continue treating it as untrusted input. Resetting on account switch sacrifices a convenience some users may prefer. The contract chooses those outcomes rather than assuming they are universal best practice. A later product change can revise them, with corresponding examples, instead of treating an old persistence mechanism as the requirement itself.

A URL and the visible filter need one agreed owner

The implementation discussion goes beyond selecting storage for /invoices?status=overdue.

Follow the labelled arrows. On a narrow screen, scroll sideways if needed.

A URL and the visible filter need one agreed ownerSelect overdue. Choose whether the router adds a history entry or replaces the current one for /invoices?status=overdue. Open I17, then return. The restored list address must drive both the visible filter control and the overdue data request. Switch account: reset to all. The proposed policy changes the list address; account authority still comes from authenticated context. From Select overdue to Open I17, then return: Carry the agreed address state. From Open I17, then return to Switch account: reset to all: Reset under the account-switch rule.Carry theagreed addressstateReset undertheaccount-switchruleSelect overdueChoose whether the routeradds a history entry orreplaces the current one for/invoices?status=overdue.Open I17, then returnThe restored list addressmust drive both the visiblefilter control and theoverdue data request.Switch account: resetto allThe proposed policy changesthe list address; accountauthority still comes fromauthenticated context.
The router must make the restored address drive both the displayed choice and data request. A second initialisation default can make them disagree.

Finish one complete promise

The final diff need not be predicted in the brief. The expected journey does: return and reload keep overdue, a copied URL carries the harmless status into the recipient’s authorised account, and an explicit account switch resets it. Those choices are small enough to explain and concrete enough to test. If the product later changes them, change the examples too. The storage helper can then remain an implementation choice, rather than becoming the accidental definition of what “remember” meant.

Working on a workflow like this? Hamsa helps finance and operations teams connect their tools and handle the exceptions, starting with one workflow.

See the Payment Operations Pilot

References & further reading

Primary sources behind this article. Living documentation may change.

  1. MDN: History API and session history
  2. MDN: sessionStorage lifetime and partition
  3. MDN: URL parsing and construction
Hamsa Jama

Practical engineering. A human perspective.

More about Hamsa Software and the person behind it. Editorial notes.

A few more things
worth a read.

Back to the reading room
AI tooling7 min read

The prompt did not change. The system did.

Version the resolved configuration around an AI response so comparisons, investigations and rollback refer to the system that actually ran.