Designing Agentic Apps: Human-Controlled Automation Lessons from ValuScope
Where to automate and where to hand judgment back to humans: boundary design principles for agentic apps, drawn from the ValuScope implementation.

Where to automate and where to hand judgment back to humans: boundary design principles for agentic apps, drawn from the ValuScope implementation.
Designing Agentic Apps: Human-Controlled Automation Lessons from ValuScope
Version note: This article reflects the
valuscope-nextspecification, source code, and Git history as of July 12, 2026. The reviewed history spans April 18 through July 12, with 94 commits and 266 tracked files. I separate what the implementation demonstrably does from outcomes that still require production measurement.
Working on CVC deals has made me optimistic about AI and cautious about blurred accountability at the same time. The hardest ValuScope design question was not which model to use. It was where the agent must stop and return control to a person.
TL;DR: What You'll Learn
Here is the design in compact form.
| Theme | ValuScope's choice |
|---|---|
| Agent role | Research, organize, propose, and generate artifacts; do not make the investment decision |
| Human role | Review output and approve, reject, or rerun it |
| Automation unit | Six bounded agents instead of one universal assistant |
| State model | Track execution state separately from decision state |
| Confidentiality boundary | Separate public web research from internal document and fund-context analysis |
| Chaining | Advance stages and launch the next agent only after approval |
This is not a dramatic autonomous-investing system. In practice, I think that restraint is the foundation of trust.
Start With Workflow State, Not a Chat Window
The obvious first screen for an AI application is chat. I was tempted by that too. Once I decomposed the investment workflow, however, the center of the product was clearly state rather than conversation.
A ValuScope deal moves through SOURCED → SCREENING → REVIEW → IC → CLOSED / MONITORING / PASSED (stage names and the code below are simplified for this article). The required work changes along the way: public research, deck analysis, meeting preparation, follow-up questions, and an investment-committee draft. The current implementation therefore assigns six bounded responsibilities.
| Agent | Primary input | Primary output | Human review question |
|---|---|---|---|
| Pipeline ToDo | All active deals | Prioritized actions | Who should verify what now? |
| Web Research | Public company data | Sourced research | Are company claims separated from third-party evidence? |
| Deck Analysis | Stored documents | Thesis and missing information | Did the agent invent anything absent from the deck? |
| Meeting Agenda | Documents and history | Issues and questions | Will answers distinguish advance from pass? |
| Question List | Documents and notes | 20–30 follow-up questions | How would each answer change the decision? |
| IC Proposal | Documents and references | Committee memo outline | Are risks, falsifiers, and unknowns explicit? |
The key insight is that an agent is defined by its business responsibility, input boundary, artifact, and next decision, not by its model name. Models are replaceable. Blurred responsibilities remain blurred even after a model upgrade.
The Git history reinforces this sequence. The April 19 application centered on portfolio management, valuation, and document analysis. Deal sub-agents appeared on May 31. Fund context and comparable research followed on June 4. The inbox and approval workflow were integrated in July. Business data and calculations came first, agents came next, and the human control plane came last. In hindsight, that ordering mattered.
Use Separate State Machines for Execution and Judgment
The strongest design choice was refusing to compress “the job returned successfully” and “a person approved the result” into one status field.
ValuScope has a DealAgentRun for processing history and an ActionItem for a human decision. The former moves through GENERATING / SUCCEEDED / FAILED; the latter uses RUNNING / AWAITING_REVIEW / APPROVED / REJECTED / FAILED / SUPERSEDED / DISMISSED.
| State family | Question answered | Representative states |
|---|---|---|
| Execution | Did the technical process finish? | GENERATING, SUCCEEDED, FAILED |
| Decision | What did a person do with the artifact? | AWAITING_REVIEW, APPROVED, REJECTED |
| Lineage | How does this request relate to an older one? | SUPERSEDED |
A model can return valid text that is unusable for an investment decision. A reviewer can reject that text even though the job itself completed normally. Combining those facts damages both incident analysis and auditability.
Approval transitions are also encoded as a deterministic function, not buried in prompt prose. The essential shape is below.
type Plan = {
proposedStatus: 'SCREENING' | 'REVIEW' | 'IC' | null
nextAgentTypes: string[]
}
function nextOnApprove(agentType: string, currentStage: string, hasDocs: boolean): Plan {
const plan =
agentType === 'WEB_RESEARCH'
? {
proposedStatus: 'SCREENING' as const,
nextAgentTypes: hasDocs ? ['DECK_ANALYSIS'] : ['MEETING_AGENDA'],
}
: agentType === 'DECK_ANALYSIS' || agentType === 'MEETING_AGENDA'
? { proposedStatus: 'REVIEW' as const, nextAgentTypes: ['QUESTION_LIST'] }
: agentType === 'QUESTION_LIST'
? { proposedStatus: 'IC' as const, nextAgentTypes: ['IC_PROPOSAL'] }
: { proposedStatus: null, nextAgentTypes: [] }
return isForwardStage(currentStage, plan.proposedStatus)
? plan
: { ...plan, proposedStatus: null }
}
This function protects two boundaries. An agent result cannot advance a deal by itself, and approval cannot move the workflow backward. Real investment processes do contain exceptions, but treating an exception as an explicit manual decision produces a cleaner history than mixing it into the default path.
Let the Agent Propose Changes, Not Write Them Directly
The chat feature follows the same philosophy. When a user asks to move a company to REVIEW, the model does not update the database. It returns a structured proposal. The interface displays old and new values, and the user selects which fields to apply.
I initially thought a confirmation modal would be enough for a human-in-the-loop system. It is not. Another team member may modify a record between proposal generation and approval. ValuScope therefore reloads the current database value and skips any field whose value no longer matches the proposal's currentValue.
The responsibilities are deliberately layered:
- The model proposes changes only for whitelisted fields.
- JSON Schema constrains the output shape.
- The UI displays current and proposed values.
- A person selects fields to apply.
- The server revalidates authentication, types, enums, and required fields.
- Only conflict-free updates reach the database.
This appears to reduce the agent's freedom. I see the opposite effect: narrow write authority makes users more comfortable giving the assistant broad analytical tasks.
Do Not Put Web Search and Internal Documents in One Context
Confidentiality is not only an encryption question. It is also a routing question. ValuScope's WEB_RESEARCH uses web search but does not receive stored deal documents or the fund profile. Document-oriented agents can receive documents and fund strategy, but do not use web search.
That separation creates three distinct paths.
| Path | Information used | External tool | Primary risk |
|---|---|---|---|
| Public research | Company name, site, industry, public description | Web search + OpenAI API | Confidential data leaking into a query |
| Internal analysis | Decks, meeting notes, fund strategy | OpenAI API | Sharing authority, retention, personal data |
| Decision and update | Approval, stage, history | AppSync / database | Incorrect writes, conflicts, authorization gaps |
The UI also distinguishes web-search, confidential-provider, and automation risks and asks for confirmation before execution. OpenAI states that API data is not used for training by default, but retention and Zero Data Retention eligibility differ by endpoint and feature. “Not used for training” is not a complete confidentiality design.
Cross-border projects taught me how much time can disappear into document-sharing decisions. That friction is not accidental overhead. Productizing it makes later explanations possible.
Turn the Philosophy Into Five Design Principles
The ValuScope choices generalize into five questions that apply to other operational applications.
| Principle | Implementation question |
|---|---|
| Agent as proposer | Is the model proposing an action or directly performing an irreversible operation? |
| State before chat | Are workflow states and transitions defined before conversational behavior? |
| Separate run and decision | Can technical success and business approval be inspected independently? |
| Context minimization | Does each agent receive only the information required for its role? |
| Approval-gated chaining | Does a person approve before the next automated step begins? |
Reviewing every output will eventually become a bottleneck. That does not make future auto-approval wrong. A safer sequence is to build decision-state tracking first, collect evidence, and automate only low-risk, high-confidence cases. ValuScope retains AUTO_APPROVED in the schema for forward compatibility, while the current web application does not generate that state.
Gotchas: Philosophy Does Not Enforce Itself
Good principles still fail at implementation seams. Four issues stood out during the code review.
- Best-effort chaining: approval can be recorded even if the next-agent dispatch fails. A durable queue, alert, and compensation path would make this stronger.
- Client-side approval orchestration: the current stage update and next-step dispatch live in a React hook. A server-side command could provide tighter transactional auditing.
- Authorization granularity:
authenticated readdoes not automatically equal tenant isolation. Membership checks and model authorization need continuous testing together. - Prompt-policy confusion: telling a model not to put secrets in search queries is only one layer. Input filtering, path separation, and log review are also required.
AWS documents that asynchronous Lambda invocations may be delivered more than once and recommends idempotent handling. ValuScope has sourceKey and SUPERSEDED lineage, but idempotency across every external side effect remains an area to verify.
Conclusion: Agent Design Is Permission Design
Agent application architecture increasingly looks like permission architecture to me. Who can read which context, propose which transition, and perform which operation after whose approval? Once those boundaries are explicit, models and interfaces can evolve without redefining accountability.
ValuScope's history moved from calculations, documents, and deal management to agents, then to an inbox and approval flow. Avoiding full autonomy at the start made it possible to connect six agents to one understandable workflow.
Part two decomposes how this philosophy maps onto Next.js Route Handlers, Amplify Data, a Lambda worker, and TanStack Query polling. Part three covers the operating procedure and a measurement framework for actual effects.
- Part 2: How to Architect and Implement an Agentic Application
- Part 3: How to Operate an Agentic Application and Measure Its Effects
References
- OpenAI API Developer Quickstart
- OpenAI API Data Controls
- AWS Amplify Gen 2: Customize Authorization Rules
- AWS Lambda: Handling Errors in Asynchronous Invocation
- Next.js: Route Handlers
Next Issue Ideas
- Idea 1: Build an evaluation layer for operational agents — Capture approval rate, rerun rate, conflict rate, and stage dwell time so prompt quality and process quality can be evaluated separately.
- Idea 2: Harden agent execution with SQS and a DLQ — Replace Function URL fire-and-forget dispatch with a durable queue and implement duplicate handling, retries, and failed-event recovery.
- Idea 3: Strengthen organization-level authorization — Test how Amplify owner authorization and organization membership should combine in a multi-tenant application.
This article is for informational purposes only and does not recommend any security, product, investment, or securities action. Generative AI assisted the writing, and the content was checked against source code and official documentation. See the disclaimer for details.
































































