Files
VianoBugAnalysis/docs/investigation-tools-v1.md

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.