7.9 KiB
Investigation tools v1
This milestone provides the read-only Investigation Tool Router and initial source/database queries. The architecture is unchanged. No shell, arbitrary SQL, live database access, source edits, web UI, bot/client steering, EvidenceStore or autonomous Investigation Loop is introduced.
API and controller boundary
from viano_bug_analysis.tools import InvestigationToolRouter
router = InvestigationToolRouter()
result = router.query(package_path, {
"format": "viano-investigation-request", "format_version": 1,
"case_id": case_id, "package_id": package_id, "analysis_revision": revision,
"requests": [{
"request_id": "Q0001", "hypothesis_id": "H1", "action_id": "query-0001",
"operation": "source.find_text",
"arguments": {"source_refs": [source_ref], "text": "activate",
"case_sensitive": True, "offset": 0, "limit": 20},
}],
})
request_schema() exposes the bundled strict investigation-request-v1 schema
for future prompting. Unknown operations/fields, invalid arguments, duplicate
request IDs and repeated hypothesis/action pairs are rejected. The router checks
manifest identity/revision, known initial hypothesis IDs, active generation or
investigation phase, and a fresh canonical case. Blocked cases cannot query.
The entire batch is validated and preflighted before any query handler executes.
Preflight includes declared roles, bounded source reads, encoding, ranges,
database structure and known columns. A rejected batch returns no partial result.
Preflight itself necessarily reads the selected snapshots; it has no writes or
external effects. Runtime errors are controlled ToolError codes without raw
source data in diagnostic text.
Tools read only the ZIP snapshots declared by case.json in source_context and
database_context. Each read verifies its existing case hash and size. Arbitrary
package members, unknown/unclassified attachments, absolute server paths and
manifest pointers are not alternative query targets. Server path strings inside
wowsrc data remain metadata. Nothing is extracted or executed.
The router returns a versioned investigation-result-v1 document with manifest
identity, source package generation, canonical case ref/hash, batch hash,
per-request hash and typed operation-specific results. It does not update the
manifest, write checkpoints, charge/refund credits or persist evidence.
The future trusted controller must reserve budget before dispatch, persist
the request and result, verify that the case generation has not changed, and
apply BudgetManager result/settlement rules. Retries of these deterministic
snapshot queries have no tool side effects; billing idempotence remains in the
controller/BudgetManager. Future controller hypothesis eligibility, reservations
and updates require their own full validation; a known birth ID alone does not
authorize spending against a terminal account.
Initial allowlist
| Operation | Arguments and behavior |
|---|---|
source.find_text |
Explicit source_refs, literal text, case_sensitive, offset, limit; matches lines, not regex |
source.find_symbol |
Explicit source_refs, one identifier symbol, offset, limit; token-boundary occurrences |
source.read_context |
source_ref, inclusive 1-based start_line and end_line |
database.describe |
source_ref selecting a verified schema/snapshot descriptor |
database.lookup |
source_ref, nonempty filters map of exact scalar equality, projection columns, offset, limit |
database.find_references |
source_ref, explicit search_columns, scalar value, projection columns, offset, limit |
Source results include actual source SHA-256, 1-based line numbers and line text. Symbol matches can be uses, declarations, definitions, comments or strings: this is not a language parser or verified definition/registration resolver. Text search is literal and may explicitly ignore case. Source encoding is UTF-8 with optional BOM; binary/NUL or undecodable sources reject the batch.
Database results include original source SHA-256 and an actual JSON Pointer to
each original row (for example /rows/2 or /2). Projection never changes the
pointer. Equality does not coerce booleans, strings or numeric types; true, 1,
"1" and 1.0 remain different typed values. Reference search only finds equal
values in specified columns; it does not assert foreign-key relationships.
SQL-like text in a scalar value is ordinary data, never executable SQL.
All searches expose checked sources, total matches, truncated and a stable
next_offset. Sorting follows requested file order and original row/line order.
Individual source lines have separate text_truncated markers. Limits reject
excessive input/output rather than quietly dropping queried files. A no-match
result applies only to the selected packaged snapshot; it proves neither absence
from an entire repository nor absence from the current server/database.
Database snapshot contract and profile adapter
The neutral database-snapshot-v1 format is:
{
"format": "viano-database-snapshot", "format_version": 1,
"table": "switches",
"columns": [{"name": "id", "type": "integer"}],
"rows": [{"id": 42}], "coverage": "partial"
}
Coverage is explicit: partial, complete_snapshot, unknown or schema_only.
Even a complete snapshot is a packaged point-in-time claim, not a live query.
Duplicate column names, rows containing undeclared columns and schema-only
descriptors containing rows are rejected. Raw arrays of row objects can be
queried without inventing table identity or schema: their coverage is unknown,
and columns must occur in stored rows. An empty raw array cannot validate a
column name; querying unknown columns rejects. A schema-only descriptor may have
known columns and no rows, but that absence is not evidence of a missing DB row.
wowsrc's native {table, columns:[{column_name, column_type, ...}], ...} descriptors
are normalized explicitly in profiles/vcore/db_tools.py:
from viano_bug_analysis.profiles.vcore.db_tools import normalize_schema_descriptor
from viano_bug_analysis.tools.database_query import DatabaseQueryTool
router = InvestigationToolRouter(
database_tool=DatabaseQueryTool(normalize_schema_descriptor),
)
This mapping reports schema_only. It does not infer table ownership from file
names, numeric IDs, ambiguous artifact rankings or schema_table_matches. Other
projects can supply their own trusted descriptor adapter or use the neutral
format. Live DB connectors, symbol parsers, registration discovery, timeline/log
queries and actual external research providers are later extensions.
Limits and asynchronous testing
ToolLimits provides adjustable operational defaults, not permanent v1 format
caps: request 64 KiB, batch 16 requests, 64 distinct selected files, 8 MiB per file,
32 MiB combined selected bytes, 100,000 database rows, 100 returned matches per
query, 200 context lines, 4,096 characters per returned source line and 2 MiB per
result batch. Existing package and CaseBuilder limits apply separately when
validating the package and canonical case inventory.
Asynchronous human/automated tests remain in the separate controller handoff described in async-tests-v1.md. They are deliberately not executed by this snapshot query router. The future Investigation Loop can combine both interfaces: fast read-only query results and persisted external test waits. No deadline, waiting state or budget preservation behavior changes here.
Tests cover generic snapshots, allowlist and path safety, whole-batch preflight, type distinctions, pagination, provenance, input/output limits, blocked/stale contexts and byte-for-byte read-only behavior. The actual BUG-20 regression reads three lines of packaged source, its quest table schema using the Vcore adapter, and the actual stored quest row through an explicit ID filter; it does not inject a known fix or claim to test model reasoning.