Receives instructions, documents, notes, and a campaign. It loads the current questionnaire and schema,
builds evidence, edits an in-memory JSON snapshot with update_json_value, validates each write,
and persists once through Postgres in testing or Sherpas PATCH in production.
How QEdit, Flex Entry, Research Agents, Statements, Reconciliation, and Evals Work
This page documents the current local implementation of the questionnaire-edit and flexible-entry systems. It emphasizes the deterministic Go code wrapped around faster models, especially Gemini Flash, so model judgment can be used without letting transcription mistakes, partial extractions, or malformed tool output reach production persistence.
1. Executive Overview
QEdit is a schema-aware questionnaire editor. Flex Entry is the companion system for facts that should become flexible financial event cards instead of being forced into the questionnaire. Research agents are read-only document investigators. Statement extraction is the deterministic-heavy path for accounts and holdings.
Receives overflow from QEdit and research memos, then creates FinancialEvent cards. Batch prefill
keeps partial cards and marks them with incomplete_summary. Chat mode asks users for missing
minimum fields or proposes estimates at finish time.
Reads VFS-mounted documents using line-bounded file tools. It returns Markdown memos with findings, sources, confidence, and flex candidates. It never writes questionnaire data and is frequently spawned in parallel.
2. Mental Model
The system uses models for semantic judgment and Go code for mechanical invariants. A model may decide which account a memo refers to, whether a fact belongs in the fixed questionnaire or flex, and which conflict needs a human. Go code handles path validation, schema sanitation, source chips, instrument IDs, portfolio rendering, statement-position integrity, conflict indexing, eval assertions, and persistence boundaries.
The same facts move through three states
The state is an in-memory questionnaire plus an ETL lifecycle row. The model maps evidence into schema fields and resolves ambiguous conflicts. The pipeline persists only after validation and merge.
Facts QEdit cannot represent are folded into an in-memory event list inside flexbrain, then persisted as
etl_result.flex_events. Missing minimums become orange cards, not dropped facts.
Chat uses Scylla mda_state and append-only mda_messages. It asks users for missing
fields, proposes estimates, summarizes sections, and routes form edits back through the QEdit port.
3. QEdit Request and Job Lifecycle
QEdit has two HTTP surfaces. The production surface is asynchronous and authenticated. The testing surface is
gated by SOMAR_QEDIT_TESTING=on and returns fuller ETL snapshots for debugging.
internal/api/qedit/production.go
POST /qedit/jobs creates a background ETL run and calls qeditpipeline.RunProduction.
GET /qedit/jobs/{jobId}/status reads the ETL row and maps internal states to the poller contract:
running, completed, or failed. Production reads the current questionnaire
through the repo and persists through Sherpas campaign PATCH.
internal/api/qedit/handler.go
POST /qedit/testing/jobs is mounted only when SOMAR_QEDIT_TESTING=on. It calls
qeditpipeline.Run and writes through qeditrepo.Repository.SaveQuestionnaireData.
GET /qedit/testing/jobs/{jobId} returns a fuller ETL snapshot for local debugging.
Production modes
questionnaire_prefill
General document/note prefill. It requires at least one document or note, runs evidence collection,
applies deterministic statement prefill, calls QEdit if needed, repairs statement positions, and routes
overflow into flexbrain.
questionnaire_scoped_patch
One-row update mode, usually for a portfolio row from a single statement. It requires exactly one document
plus writeTarget.arrayKey and rowId, resolves a concrete AllowedWriteRoot,
and rejects out-of-root writes.
Job lifecycle
- Validate request body and mode. Bad UUIDs, unsupported buckets, missing documents, or invalid scoped targets fail before work starts.
- Create an ETL row with type
questionnaire_edit, statusrunning, target kind, campaign id, opportunity id, and target ref. - Return
{jobId}immediately. - Run the pipeline in a detached goroutine with a 30 minute timeout and panic recovery.
- Complete the ETL row with
etl.Result, or fail it with sanitized error status. Statement extraction results can be stored separately inextraction_result.
When the production pipeline fails after building a partial result, the production handler can still store that
partial etl_result alongside the failed status. That preserves useful diagnostics and any structured
evidence/result envelope available at the point of failure.
Agent core
qeditagent.NewAgent creates the Caravan agent named qedit. The default model in the current
local code is google/gemini-3.5-flash, with structured final output validation, a high iteration cap,
and conditional tools. Write tools use ctx.StateMu; read-only tools can run in parallel.
4. Evidence Pipeline
The production pipeline no longer asks QEdit to read raw documents directly for prefill. It first converts documents into typed evidence: statement extractions, research memos, typed claims, and conflicts.
Evidence types
The evidence envelope in pipeline/qedit/evidence.go is the editor's prepared world model. It contains
statement extractions, research memos, typed claims, and code-detected conflicts. Evidence.IsEmpty,
Evidence.HasConflicts, and Evidence.TotalDocuments let the pipeline decide whether QEdit
needs to run or whether deterministic prefill is enough.
StatementEvidence
A document source plus statementholdings.StructuredStatementResult: accounts, positions,
balances, holder names, account masks, and extracted source metadata.
DocumentMemo
A research memo with source, findings, confidence, flex candidates, and parsed typed Claims.
Claim
Structured facts, especially balances and goal amounts, used by conflict detection and QEdit magnitude guards.
Conflict
A code-detected disagreement. Optional triage marks it noise, auto_resolve,
needs_agent, or needs_advisor.
Classification
Document classification lives under internal/questionnaire/docs and is defensive by design. The
classifier uses existing metadata and source mappings before calling a model. If it does call a model, it uses
temperature zero, strict response format, enum validation, and fallback to OTHER. Low-confidence
statement classification is downgraded so a weak model does not route a planning export into holdings extraction.
looksLikeInvestmentStatement.
Collection dispatch
Statements are special because they run two lanes: a structured holdings extraction for account data and a statement research memo for non-holdings planning facts printed on the same pages. Transcripts, notes, tax docs, credit reports, and generic files use specialized research objectives that all return the same memo shape.
CollectEvidence processes groups concurrently and returns partial evidence even when individual
document workers fail. That keeps a single bad note or noisy extraction from throwing away usable evidence
from other documents.
5. Statements, Holdings, and Deterministic Account Writing
Investment statements are the highest-risk area because they contain long arrays of accounts, positions, and lots. The current architecture deliberately removes as much transcription as possible from the editor model.
Structured extraction result
internal/agents/extractions/statementholdings/result.go defines the output schema. Numeric fields
are pointers so unknown remains null rather than accidentally becoming zero. Positions explicitly
say not to invent instrument UUIDs. The pipeline stamps source documents rather than trusting the model to
produce provenance chips.
Extraction postprocessing
appendExplicitZeroCashPositionspreserves explicit zero-cash rows when documents state them.sanitizePositionInstrumentIDsremoves invented or malformed instrument IDs.reconcileAccountAmountsreconciles account totals against extracted positions.resolveStatementInstrumentsmaps known tickers/names to canonical instrument IDs.sanitizeAccountBankIDsandresolveAccountCustodiansclean account identity metadata.normalizeAccountsstabilizes the final extracted account shape.
Retrying partial extraction
statement_extraction_retry.go detects suspicious extraction outputs. A statement with holdings
signals should not produce zero accounts. An account with priced positions covering less than half the stated
balance is likely under-transcribed. The pipeline retries once and keeps the better extraction based on priced
position amount and account count.
Deterministic prefill
statement_prefill.go can build portfolios[] wrappers directly from statement evidence.
It writes source-document chips, account masks, account metadata, and aggregated positions. For statement-only,
conflict-free FH_PLUS or PORTFOLIO runs, Evidence.IsStatementOnly allows the pipeline to skip QEdit
entirely.
Post-agent gap fill
Statement handling also runs after the editor. PrefillStatementPortfolioGaps appends statement accounts
the model missed, while EnforceStatementIntegrity can append unmatched extracted accounts that still
have positions. This gives the model room to make judgment calls without making account coverage dependent on
perfect model recall.
Field-scoped conflict policy
noise
False-positive conflicts do not block prefill. The account flows normally.
auto_resolve
When triage finds a clear winner, deterministic prefill uses that winner.
needs_agent
The wrapper and positions are still useful, so they flow; the ambiguous meta amount is omitted for QEdit judgment.
Statement integrity repair
EnforceStatementIntegrity runs after the agent and normalization, before persistence. It matches
wrappers to statement accounts by source-document chip, account identity, or type plus amount. If positions are
under-counted, amountless, or sum-mismatched, it replaces positions from extraction using the same renderer as
deterministic prefill. If an extracted account with positions has no wrapper, it appends the wrapper.
6. Research Agents
Research agents are read-only subagents. They are used both inside QEdit and in dynamic miniagent workflows. Their job is targeted document discovery, not persistence.
QEdit research agent
The package internal/agents/qedit/research exposes research.Run. The tool payload is
Args{Objective, PathHints}. Dependencies are a document VFS and FillTarget, which contains
the editor instruction, questionnaire schema, and current questionnaire.
list_files
Shows the VFS inventory with mime/name metadata. It intentionally does not include document bodies.
read_file
Reads an exact path with a 1-based line range, capped at 500 lines per call.
read_files_batch
Reads up to 10 exact path/range slices in parallel for side-by-side comparison.
search_files
Regex-searches VFS files with context, capped at 50 files, 20 matches, 2 context lines, and 16KB output.
Research output contract
The QEdit researcher must produce Markdown sections: ### Findings, ### Sources,
### Confidence, and ### Flex Candidates. Findings should tag schema paths in backticks,
cite VFS paths and line ranges, and avoid duplicating a fact in both Findings and Flex Candidates.
Spawn tool in QEdit
spawn_research_agent is conditionally registered. It is unavailable in scoped patch mode, unavailable
when the preprocessed evidence context already exists, and unavailable when there are no documents. The parent
QEdit agent never gets direct file read/search tools in normal prefill; it delegates narrow document work.
Evidence-collection research
In production prefill, research usually happens before the editor sees anything. CollectEvidence
builds objectives for statement research, transcript research, notes, tax docs, credit reports, and generic docs.
runResearchAgent retries once when a memo looks degenerate, for example too short, leaked tool strings,
or missing Markdown headings.
Claims and flex candidates
Research memos can carry a trailing ### Claims JSON block. parseMemoClaims extracts those
claims, while malformed or absent claims degrade to prose-only evidence. Goal claim amounts feed the QEdit goal
magnitude guard. Flex candidate sections feed flexbrain through FlexCandidatesFromEvidence.
Dynamic miniagent research
internal/agents/flexoutput/dynamic_miniagents/research mirrors the same file tool shape for dynamic
miniagents. It uses MemoryFileSystem, outputs Findings/Sources/Confidence/Ambiguity Notes, and is
registered by internal/api/dynamic_miniagents/tools.go for manifests that require focused file
investigation. Its search caps differ slightly from QEdit research: dynamic miniagent research searches up to
80 files rather than QEdit research's 50 file cap.
7. Write Integrity and Questionnaire Mutation
The main QEdit write tool is intentionally narrow. The model asks for a path and value; Go decides whether that write is legal, how to normalize it, what to sanitize, and whether it can be committed to in-memory state.
update_json_value flow
Important guards
$evidence expansion
agents/qedit/evidence_refs.go and pipeline/qedit/evidence_refs.go let the model
point at stable evidence IDs while Go copies exact account and position payloads.
goal_magnitude_guard.go blocks clean 10x/100x slips such as copying $45,000 as $450,000.
instrument_id_guard.go allows only IDs already present, seeded from evidence, or returned by
resolve_instrument.
coverage_guard.go rejects final outputs that skip evidence only because required fields are missing.
scoped.go and tools.go keep scoped patch jobs inside the resolved subtree.
questionnaire/sanitize/businessrules.go removes duplicates introduced by the current edit while preserving legacy duplicates.
Path-scoped validation
The validator evaluates the document but only blocks on errors under the edited path. Pre-existing unrelated questionnaire problems do not prevent a valid local edit from being recorded. The edit result records capture status as full, partial, or none, and includes sanitized-field or business-rule details for downstream reporting.
Provenance
QEdit writes source attribution through citation IDs. Prompt context shows citation_id=... for
documents. UpdateJSONValueArgs and ReplaceStringArgs accept citation_ids.
ProjectProvenanceToQuestionnaire maps those citations into extraData.sourceDocuments
where the schema supports chips, while loose fields retain provenance in State.Provenance and ETL
references.
8. Conflicts, Reconciliation, and Resolution
Conflict detection is mostly deterministic. The model is used for triage and final judgment only when the code cannot safely decide.
Conflict sources
- Statement vs statement: same account with different balances, account types, holder names, positions, quantities, or amounts.
- Research claim vs statement: typed memo balance claims compared against statement accounts.
- Account identity: masks win over names where available; same-source pairs are skipped to avoid treating two accounts in one consolidated PDF as one conflict.
False-positive controls
- Claim matching emits at most one conflict per memo claim.
- Ambiguous multi-match emits no conflict rather than quarantining likely-good data.
- Approximate memo amounts get a wider tolerance.
- Conflicts with both masks present but different masks are skipped as different accounts.
Triage
triageConflicts runs a single temperature-zero strict-JSON model call over all conflicts when
TriageModel is configured. It can mark conflicts as noise, auto-resolve, needs agent, or needs advisor.
Any triage failure degrades to original conflicts and does not fail the run.
Reconciliation in flex chat
Flex chat reconciles against state, not chat transcript memory. FinishConversation returns saved
events as source of truth. ListFinancialEvents exposes current cards. Delete is deterministic.
Edit and resolve signals inject internal instructions, then post-run validation checks whether the tool actually
updated the card.
9. Flex Prefill: QEdit Overflow to Cards
Flex prefill is the batch path for facts that QEdit could not or should not place in the fixed questionnaire. It shares the flex event schema with chat but has a different gap policy: partial facts are kept as orange cards.
Overflow sources
AggregateOverflow and FormatOverflowSummary convert semantic rejections,
no-matching-schema notes, business-rule removals, and failed edits into a structured <overflow> block.
FlexCandidatesFromCoverage converts skipped, blocked, or unhandled coverage items with no
questionnaire field into <qedit_not_represented>.
FlexCandidatesFromEvidence converts research ### Flex Candidates sections into
<flex_investigation> for flexbrain.
Flex event schema
flexentry.FinancialEvent carries event_id, category_id, subcategory_id,
data, title, description, chat_references, optional
origin_event_id, timestamps, and incomplete_summary.
Embedded catalog
internal/flexentry/schema embeds category schema JSON. LoadSection, LoadSubcategory,
and Categories provide deterministic access. FieldDefinition.Requirement marks minimum,
default, or addition fields. Conditional minimums are skipped by hard validation because applicability is
model-judged.
Batch gap policy
In batch prefill, the model cannot ask the user. The add_financial_event tool includes
unsourced_minimums. If the model declares a minimum field unsourced, Go deletes any placeholder value
for that key so MissingMinimums sees a real absence. If a minimum is neither filled nor declared,
the tool soft-retries once. After that, the partial card is accepted and annotateIncomplete writes
incomplete_summary.
10. Flex Chat and MDA
The MDA API adapts frontend chat requests onto flexchatpipeline.RunStream. It is stateful, streams
SSE events, hydrates from prefill once, and persists shared campaign state after the stream drains.
Auth and storage
MDA is a machine-to-machine API like production QEdit. Routes are registered with X-Api-Key middleware
backed by INTERNAL_API_KEY. Identity such as user type, client id, and advisor id arrives in the request
body from the trusted upstream caller.
Conversation history and state are separate. mda_messages stores append-only message history per
session. mda_state stores the shared campaign state keyed by campaign/questionnaire id. On the first
chat turn, prepareState hydrates mda_state once from the latest succeeded QEdit prefill
etl_result.flex_events. Before that hydration exists, the section events read endpoint can fall back
to prefill cards from ETL so the UI can display prefilled flex events immediately.
API surface
POST /mda/campaigns/{campaignID}/sections/{sectionID}/chat
Streams one chat turn through SSE. Requires session id, user type, questionnaire type, and client id.
GET /mda/sessions/{sessionID}/history
Returns persisted append-only message history.
DELETE /mda/sessions/{sessionID}/history
Clears MDA messages only; state and cards survive.
GET /mda/campaigns/{campaignID}/state
Returns current shared MDA state.
GET /mda/campaigns/{campaignID}/sections/{sectionID}/events
Returns flattened cards with completion metadata and missing fields.
Signals
pipeline/mda/run.go accepts pre-translated metadata.signal or the frontend-native
convention where an internal turn has the signal as message text. It maps:
Deterministic signal behavior
acceptMaterializes the pending proposal into a saved card, removes the proposal, then runs the model to summarize and call generate_summary.skipDiscards the pending proposal. Declined estimates are not kept as orange cards. The model runs only to finalize summary.deleteRuns no model. Go removes the card, emits mda/financial_event_deleted, then emits terminal Complete.editDoes not mutate card data directly. It injects edit context requiring update_financial_event, then retries if data is unchanged.resolveInjects incomplete-card context and requires real values before updating. Signal turns skip stale-incomplete post-run retry.finishUses the finish gate. If nothing changed since last summary, Go emits terminal Complete and skips the model.Tools
Flex chat exposes card CRUD tools, schema tools, route-to behavior, resources, questionnaire edit/schema tools,
questionnaire diff, portfolio positions, deferred ask_user_input, proposals, finish conversation, and
summary generation. Tools validate event data before writing state. edit_questionnaire_field serializes
within a turn so concurrent read-modify-write calls cannot clobber each other.
History and prompt robustness
HistoryTrimmingStore gives the model a filtered, compacted view while preserving full persisted
transcript. Old large tool returns are compressed, noisy messages are filtered, and truncation avoids orphan tool
results. On prompt-too-long 400 errors, the pipeline retries once with emergency history trimming and compact
questionnaire context.
11. Failure Behavior
Failure behavior is intentionally layered. Some errors fail the job. Some degrade to partial evidence. Some become tool feedback to the model. Some are salvaged when all valid writes already happened.
| Failure | Behavior |
|---|---|
| Bad production request | Return HTTP 400 before creating ETL row. |
| ETL row create failure | Return HTTP 500; no detached job starts. |
| Pipeline panic | Recovered in background goroutine and stored as failed ETL row. |
| Evidence worker fails for one document | Collect partial evidence from the rest; do not throw away all work. |
| Conflict triage model fails | Keep original conflicts; QEdit sees them as unresolved. |
| Research memo is degenerate | Retry once. If still bad, record document-level error or continue with other evidence. |
| Statement extraction empty/partial despite holdings signals | Retry once and keep the better extraction. |
| QEdit final structured output parse fails after writes | salvageAgentRunResult can keep already-applied in-memory edits and produce fallback summary. |
| QEdit max iterations after writes | Same salvage path when the max-iterations error matches the canonical pattern. |
| Flexbrain failure after QEdit succeeds | Non-fatal; questionnaire edits can still persist and flex events are absent. |
| MDA prompt too long | Retry once with emergency history trimming and compact questionnaire prompt. |
| MDA deferred tool pauses | Suppress post-run retries and emit explicit Complete so frontend stops loading and renders the widget. |
| Status polling an internal error | Handlers sanitize stored error to processing failed. |
12. How Deterministic Code Reduces Wrong Behavior From Faster Models
The current production qedit variant uses Gemini Flash for QEdit, structured extraction, research, and classification. The code compensates by narrowing tasks, constraining outputs, and moving high-cardinality transcription into deterministic Go.
Patterns
OTHER; low-confidence statement classification falls back to generic research instead of brittle extraction.parseMemoClaims and detectClaimConflicts compare structured claims rather than scraping arbitrary memo prose.{"$evidence":"statement-1-account-1"} and PrefillStatementPortfolios let Go copy long account and holding payloads.EnforceStatementIntegrity catches under-transcribed positions, while path-scoped validation and sanitizers keep writes legal.validateCoverageDispositions and flex missing-minimum retries prevent models from skipping partial facts or forgetting gap declarations.Current active model variants
productionGemini Flash for QEdit, structured extraction, researcher, and classifier; Sonnet for flex.baseline-sonnetSonnet for flexbrain.productionGPT-5.4-mini for isolated research routing evals.13. Evals and What They Test
somar/cmd/eval runs three suites: qedit, flex, and research. evalcore builds the
case-by-variant matrix, runs repeats with timeouts and parallelism, writes artifacts, computes metrics, and
fails the CLI when quality gates fail.
The suite is intentionally weighted toward QEdit because that is where production persistence, statement extraction, document routing, and write integrity meet. Research evals isolate the memo router. Flex currently has a focused overflow regression around complete and incomplete cards.
Runner artifacts
| Suite | Artifacts |
|---|---|
| QEdit | questionnaire.final.json, patch.payload.json, evidence.json, optional edit-snapshots.json, result.json, spans.json. |
| Flex | events.json, result.json, spans.json. |
| Research | memos.json, result.json, spans.json. |
Assertion families
Generic
JSONPath exists/absent/equals/length/sum/set/find, unchanged checks, file equality, metric upper bounds, and duplicate detection.
QEdit
Success, persisted, minimum edit count, tool present/absent, write-root-only, patch exists, plus domain metrics such as qedit ms, extraction ms, research ms, conflict count, update calls, and resolve calls.
Flex and research
Flex counts complete/incomplete cards and subcategory presence. Research checks memo count, finding/flex contents, flex non-empty, and schema-field tags.
QEdit production-safety cases
| Case | What it protects |
|---|---|
fh-income-inheritance-rental | Income chip routing, partial capture, rental-property auto-link, insurance partials, and reduced flex leakage. |
fh-plus-400-holdings-statement | Large holdings scale, no per-holding resolver churn, source provenance, FH_PLUS portfolio destination. |
fh-plus-duplicate-source | Same account in statement and meeting summary should become one account, not duplicates. |
fh-plus-existing-account-conflict | Existing account update should preserve row identity and avoid duplicate append. |
fh-plus-meeting-statement-conflict | Statement exact balance should win over approximate meeting balance while meeting planning facts still apply. |
fh-plus-multi-account-statement | One consolidated PDF should split IRA, 401k, joint brokerage, and cash sweep accounts correctly. |
fh-plus-partial-data | Capture available account data without hallucinating unavailable details. |
fh-plus-same-custodian-multidoc | Same custodian documents remain separate when account identities/types differ. |
fh-plus-stale-newer-statement | Newer statement balance and provenance win. |
fh-plus-unresolved-instruments | Account balance is captured even when positions cannot resolve to canonical instruments. |
fh-routing-investments | FINANCIAL_HEALTH writes account-level balances to finances.investments[], not detailed portfolios. |
mixed-doc-transcript-statements | Mixed statements, meeting summary, and advisor note produce portfolios, goals, income, liabilities, and no duplicates. |
trace-012f-credit-report-regression | Credit-report liabilities and primary-home facts from production trace. |
trace-67s-kailey-roth-401k | Scoped PORTFOLIO patch: write root only, structured extraction, and Roth 401k fields/positions. |
trace-emoney-steadle-sherpas-data-transfer | eMoney plus meeting summary: DOBs, zip, primary home, salary, investment accounts. |
trace-emoney-wysni-sherpas-data-transfer | eMoney-only production regression with DOBs and FH investment accounts. |
trace-shupe-gd-lot-collapse | Schwab GD lots collapse into correct Roth and Traditional positions without duplicate ticker rows. |
trace-timothy-weah-mixed-docs | Large mixed-doc FH_PLUS trace covering portfolios, crypto, salary, liabilities, risk, and children facts. |
Flex eval case
mixed-complete-incomplete feeds overflow facts with no fixed questionnaire slot. It asserts at least
four events, presence of relocation, sabbatical, and parent-care events, and an incomplete start-business card.
Inheritance and rental income are intentionally not flex facts because QEdit routes them into questionnaire income.
Research eval cases
| Case | Routing being tested |
|---|---|
income-routing | Rental, inheritance, salary, and related facts must be Findings tagged to income[], not Flex. |
mixed-flex-routing | Salary to Findings; relocation, sabbatical, elder care, and side business to Flex only. |
multi-section-routing | Rental condo tags both income[] and finances.nonInvestments[], no Flex leakage. |
full-questionnaire-routing | Broad FINANCIAL_HEALTH note routes fixed-schema facts across personal data, goals, income, real estate, investments, liabilities, insurance, expenses, risk; sabbatical to Flex only. |
full-questionnaire-routing-fh-plus | Same broad coverage for FH_PLUS, with investments routed to portfolios[]; sabbatical remains Flex only. |
Quality warnings
QEdit eval warnings are non-gating unless strict portfolio checks promote them. They include empty statement accounts, no conflicts in mixed monetary evidence, duplicate summary lines, placeholder values, unknown portfolio types, holder mapping issues, missing source documents, position/account sum mismatch, high unresolved positions, invalid goal subtypes, unnecessary FH instrument resolution, statement-only client identity inference, high tool churn, final output not JSON, and statement extraction with no questionnaire edits.
14. File Map
somar/internal/agents/qedit/agent.go
somar/internal/agents/qedit/deps.go
somar/internal/agents/qedit/state.go
somar/internal/agents/qedit/tools.go
somar/internal/agents/qedit/context.go
somar/internal/agents/qedit/prompt.go
somar/internal/agents/qedit/scoped.go
somar/internal/agents/qedit/spawn.go
somar/internal/agents/qedit/structured_extract.go
somar/internal/agents/qedit/coverage_guard.go
somar/internal/agents/qedit/goal_magnitude_guard.go
somar/internal/agents/qedit/instrument_id_guard.go
somar/internal/agents/qedit/research/agent.go
somar/internal/agents/qedit/research/deps.go
somar/internal/agents/qedit/research/state.go
somar/internal/agents/qedit/research/tools.go
somar/internal/agents/qedit/research/prompt.go
somar/internal/agents/qedit/research/run.go
somar/internal/agents/qedit/research/fill_target.go
somar/internal/agents/qedit/research/types.go
somar/internal/pipeline/qedit/run.go
somar/internal/pipeline/qedit/production_run.go
somar/internal/pipeline/qedit/evidence_collector.go
somar/internal/pipeline/qedit/evidence_conflicts.go
somar/internal/pipeline/qedit/conflict_triage.go
somar/internal/pipeline/qedit/evidence_context.go
somar/internal/pipeline/qedit/statement_prefill.go
somar/internal/pipeline/qedit/statement_integrity.go
somar/internal/pipeline/qedit/salvage.go
somar/internal/pipeline/qedit/overflow.go
somar/internal/pipeline/qedit/flex_candidates.go
somar/internal/agents/extractions/statementholdings/result.go
somar/internal/pipeline/extractions/profile_statement_holdings.go
somar/internal/pipeline/extractions/profile_statement_accounts.go
somar/internal/questionnaire/normalize
somar/internal/questionnaire/sanitize
somar/internal/questionnaire/merge
somar/internal/questionnaire/schema
somar/internal/questionnaire/editresult
somar/internal/validator
somar/internal/flexentry
somar/internal/agents/flexbrain
somar/internal/pipeline/flexentry
somar/internal/agents/flexchat
somar/internal/pipeline/flexchat
somar/internal/pipeline/flexchat/prepare.go
somar/internal/pipeline/mda
somar/internal/api/mda
somar/internal/stores/etl
somar/internal/repos/qedit
somar/internal/repos/mdaprefill
somar/internal/repos/mdaresource
somar/internal/repos/mdainstruments
somar/internal/evalcore
somar/internal/evalsuite/qedit
somar/internal/evalsuite/flex
somar/internal/evalsuite/research
somar/evals/qedit
somar/evals/flex
somar/evals/research
somar/docs/qedit-agent.md
somar/docs/roadmap-flex-entry-agent.md
somar/docs/roadmap-mda-agent.md
somar/docs/qedit-write-integrity-refactor-plan.md
somar/docs/qedit-eval-remediation-plan.md
Operational commands
# Run standard tests
make test
# Run QEdit/Flex/Research evals
make eval SUITE=qedit
make eval SUITE=flex
make eval SUITE=research
# Local mock server
SOMAR_SCYLLA_ADAPTER=mock \
SOMAR_POSTGRES_ADAPTER=off \
SOMAR_SHERPAS_ADAPTER=mock \
SOMAR_FIRECRAWL_ADAPTER=off \
SOMAR_CLICKHOUSE_ADAPTER=off \
MOCK_FIXTURES_DIR=/workspace/somar/tests/fixtures/mock \
go run ./somar/cmd/agents