Skip to content
TrendsWhat

Case Study · Building with AI

From AI suggestion to approved action: a StockNote case study

TrendsWhatAI-assisted editorial8 min read

Inspect how the operator’s expense assistant separates AI drafts from approved records. Reuse the approval worksheet and understand what source code can prove.

In this article

The useful AI feature stops before the final write

In the operator's StockNote project, the interesting AI design decision is easy to miss if you look only at the conversation. A message can become a structured expense draft, but the draft is not yet the final transaction. The application provides a separate approval operation that accepts the user's reviewed fields and commits the record.

That separation turns an abstract instruction, “keep a human in control,” into a concrete software boundary. The model proposes an interpretation. The application stores a draft. A user can review or revise the amount, currency, date, merchant, item, or category. A different request performs the final write. The distinction is visible in the inspected service, route, and repository code.

This is a source-backed case study of the operator's own project, inspected for TrendsWhat on September 15, 2026. It is not a production transaction test, a security audit, or evidence that every user reviews every field. We did not submit an expense, read customer records, or measure extraction accuracy. The lesson concerns the architecture present in the code and the review workflow it makes possible.

Natural-language input becomes a stored draft, then a user-reviewed approval request, then a committed record.

Original TrendsWhat architecture diagram, redrawn from the inspected code. It describes the AI draft path; the application also has a separate direct-entry path.

Why a preview is different from a final result

Suppose a person writes, “Lunch yesterday, twelve fifty.” This is an illustrative input, not a captured customer message. A system must decide a date, amount, currency, and category. Some context may already exist in the account or device. Other context may remain ambiguous. A natural-looking reply can hide those decisions unless the proposed fields are visible.

If an assistant responds “Recorded your lunch,” the user may reasonably believe the transaction is final. If the system instead shows a draft with a date, amount, and currency, the user has a concrete object to inspect. The approval action can then refer to that object rather than to an unstructured sentence whose interpretation is difficult to compare.

The project demonstrates the latter architecture for AI-generated expense drafts. That does not prove the interface is perfect or that every ambiguity is surfaced. It gives the application a place to put those checks. A design without a draft state has to add such a place later or rely on corrections after records have already been written.

What we inspected

The public operator portfolio identifies StockNote's financial recording work and describes user approval before AI-structured input becomes stored transaction data. We checked that description against the local AI CFO module instead of relying on the portfolio alone. Public descriptions can lag behind implementation, and this particular claim has a clear code path to inspect.

In createAiCfoChatResponse, the service builds a context pack and calls an extraction function. A plain response follows a conversation persistence path. A structured expense follows persistExpenseDraftChat, then returns an expense_draft response with the draft's fields and preview entries. The returned object includes message identifiers and a draft identifier.

The approval route requires an authenticated user subject, parses the draft identifier and optional field edits, and calls approveAiCfoExpenseDraft. That service delegates to the repository commit operation. The repository reads the account-scoped draft, checks that it is pending, creates the transaction, and updates the draft to committed inside a transaction wrapper. The draft lookup includes a row lock.

Trace the states, not just the screens

StageInspected behaviorReader-facing meaning
MessageBuilds context and requests extractionInput still needs interpretation
Plain replyPersists a conversation responseNo expense draft is returned
Expense draftStores proposed fields and preview entriesReviewable proposal exists
Approval requestAuthenticates user and accepts field editsUser chooses the final values
CommitChecks pending state and writes transactionProposal becomes a stored record
Repeated processingService maps processed draft to conflictSame draft is not treated as newly pending

The state distinction is more durable than a particular button label. You can redesign a mobile screen without changing the meaning of pending and committed. Conversely, a screen saying “Review” offers little protection if its backend has already performed the irreversible operation.

A useful architecture review therefore asks where the durable write happens. Follow the request into the service and repository. Check whether draft creation already invokes the same operation as approval. In the inspected AI path, those responsibilities are separated. The direct-entry path is different and should not be confused with an AI proposal awaiting review.

The small detail that makes corrections practical

The approval request can carry edited fields. That matters because rejecting a whole draft is a blunt response to one wrong detail. If the model chooses a plausible category but the amount is wrong, the user should be able to correct the amount while retaining useful work.

The code resolves approved values against the stored draft, builds the final record, and returns its identifier. This connects review to an actual persisted result. A review screen that lets a user edit values but ignores those edits during commit would break the contract, even if the screen itself looks correct.

For a reader designing an AI feature, the transferable question is: what exactly does approval approve? It should approve the visible version of the proposed action, including the fields that matter. If the proposal changes after review, the prior approval should not silently apply to an unseen interpretation. That versioning concern is a follow-up design requirement, not something we established for every interaction in this code inspection.

Why the trace record deserves its own boundary

The project also contains a dedicated model trace persistence function. It records provider, model, response identifier, token counts, and request and response JSON linked to the source chat message. That is separate from the final financial transaction representation. The separation makes it possible to investigate the interpretation step without treating the final record as the only evidence.

Traceability has a cost. Request and response logs can contain sensitive input, and retaining everything indefinitely is not automatically a good policy. The code inspection confirms fields and relationships, not a complete retention policy, access audit, or deletion guarantee. Those operational controls require additional evidence.

The design lesson is to decide what each record is for. The original message explains what the user supplied. The draft explains the proposed interpretation. The approval explains the reviewed action. The transaction explains what was committed. A trace may help explain how the model produced the proposal. Combining all of them into one mutable text field makes later questions harder to answer.

Reuse the pattern outside expense tracking

A similar boundary can help with an AI-assisted calendar, a content publishing workflow, or a customer reply tool. For a calendar, the draft contains attendees, time, timezone, and location. For a publication, it contains the actual text, destination, and scheduled time. For a reply, it contains recipients and attachments as well as the body.

The review should emphasize fields that change the consequence. A polished paragraph is easy to skim, while a wrong recipient may be more important than the wording. Put the action and its scope next to the approval control. Make pending status distinguishable from completion so the user knows whether anything has actually happened.

These are proposed adaptations, not claims that the operator implemented all three products with the same code. The inspected expense flow supplies one concrete example. The general pattern is to make an AI interpretation a durable, reviewable proposal before allowing the final operation.

Copy this approval-boundary worksheet

Design questionYour answer should name
What does AI propose?Explicit fields and allowed action types
What remains uncertain?Missing or ambiguous values
Where is the proposal stored?Draft identifier and state
What can the user edit?Consequential fields, not only prose
Who may approve?Authenticated owner or authorized role
What does approval commit?Exact reviewed values and destination
What happens twice?Repeated request behavior
What survives a failure?Draft, error, and recoverable state
What is retained for diagnosis?Minimum useful trace with access rules

Walk through this worksheet using one ordinary input and one ambiguous input. Then trace the backend operations. If a row cannot be answered without saying “the prompt tells the model,” the design may be relying on instruction where an application boundary would be more inspectable.

How we would validate the runtime next

A focused runtime test would create a permitted synthetic draft and confirm that no final transaction exists before approval. It would then approve edited values and verify that the stored transaction uses those values. It would repeat the approval request and check the documented conflict or idempotent behavior. It would also test a different account attempting to access the draft.

A separate failure test would interrupt the commit operation and inspect whether the transaction wrapper leaves a consistent state. A UI test would confirm that pending and committed are presented distinctly and that a network timeout does not make the user guess whether a write occurred. Those checks require an appropriate test environment and were not performed for this article.

This distinction keeps the case study honest. Source code can demonstrate an intended boundary and concrete mechanisms. It cannot, on its own, establish the behavior of the currently deployed service, every database constraint, or every concurrent request. Readers should resist converting architecture evidence into an unqualified claim of safety.

The experience worth transferring

The operator's project provides a useful example of building around the model rather than treating its text as the whole feature. The value lies in the proposal, review, persistence, and trace boundaries that connect language interpretation to a user-controlled record.

For your own AI work, draw the line between “the assistant suggested this” and “the application did this.” Give each side a visible state and a clear owner. If you cannot point to the exact operation that crosses the line, add that clarity before expanding the assistant's responsibilities.

The most capable interface may be the one that makes a modest promise precisely: here is the draft, here are the fields, and here is what approval will change. That promise is understandable to users and testable by the people building the system.

Sources, materials, and limits

  • Operator portfolio: public project attribution; checked against local code on September 15, 2026.
  • Source inspection notes: file locations, observed mechanisms, and validation limits.
  • Original diagram: TrendsWhat, based on the inspected AI draft path.
  • Evidence scope: local source inspection only; no live expense creation, private records, model accuracy benchmark, financial advice, or comprehensive security audit.
Browse all articles →