ouroboros/tests/test_query_code.py
Ouroboros 4007261500 feat(core): v6.34.0 — multi-task chat steering + resilience + v6.33.0 review carryover
Multi-task chat steering (WS1): a busy-chat decision turn STEERS the right running
task instead of spawning a duplicate. It sees the chat's running tasks as a structural
runtime fact (current_chat.running_tasks) and the new LLM-first steer_task(task_id,
message) delivers to that task's owner-mailbox — idempotent (client_message_id-derived
msg_id, no double-deliver), fail-visible on a stale target, generalizing to N concurrent
tasks (the agent's judgment picks the target — no keyword gate, BIBLE P5). A busy project
room routes to the same decision turn (project-scoped) rather than mechanically
auto-spawning; the 1:1 project auto-delivery is idempotent too.

Skill-dispatch resilience (WS2): the ctx calling-convention is decided on the RAW handler
(no spurious-ctx TypeError for keyword-only handlers). (The no-deps _execution_lock skip
fast path was withdrawn — it reopened a cross-skill dep leak; a reader/writer fast path is
deferred to a live-verifiable release. Head-of-line is mitigated by the pre-existing
out-of-process dispatch + the nested-finally lock release.)

Chat-lane wedge resilience (WS3): bridge intake is hoisted EARLY in the supervisor loop;
a dedicated per-generation liveness watchdog (off the serial loop) alerts the owner on a
supervisor-loop stall OR a heartbeat-silent in-process chat turn (read lock-free; +
/restart hint). In-process admission cannot be safely freed (the wedged turn holds
_chat_agent_lock), so out-of-process kill stays deferred; WS10 ephemeral turns keep the
chat responsive meanwhile.

WebSocket hardening (WS4): broadcasts fan out concurrently (asyncio.gather — one slow
client can't head-of-line); chat-history jsonl parses off the event loop (asyncio.to_thread).

v6.33.0 review carryover (WS5): the P3 scope-review floor is owner-only + audited (POST
/api/owner/scope-review-floor, merge-skipped from generic settings, guarded on shell/browser/
SAFETY channels). Max context mode is enforced at point-of-USE and point-of-BUILD (fail-closed
to Low when the active route — remote OR local n_ctx — no longer confirms >=1M, read-only;
USE_LOCAL_MAIN routes the gate to local n_ctx; switch_model refuses a sub-1M route while the
transcript is max-sized, fail-closed). A short ephemeral decision turn runs a DEFAULT-DENY
read/decision ALLOWLIST (no durable/control/review/skill/shell or extension·MCP tools) and
leaves no durable task record. The external-shell secret guard catches relative interpreter-
string paths. Ratified extras: op=structural code intelligence is polyglot via tree-sitter
(Go/Rust/Java/... + a visible structural_unavailable marker); an OpenAI-compatible /models
capability probe; Settings Max-save shares the capability-ack flow.

SYSTEM.md slim (WS6): the named-project how-to moves from the prompt into the
promote_chat_to_task tool description.

New surface: POST /api/owner/scope-review-floor, steer_task tool,
OUROBOROS_SUPERVISOR_LIVENESS_DEADLINE_SEC, OUROBOROS_PACING_INTERVAL_SEC.

Reviewed by the full triad (gpt-5.5 / gemini-3.5-flash / opus-4.8) + scope (gpt-5.5, 1M) +
claudexor (gpt-5.5) across 8 gauntlet rounds; converged with the sole remaining finding —
the WS2 no-deps reader/writer fast path — an owner-deferred, evidence-based decline.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 05:01:19 +03:00

177 lines
7.4 KiB
Python

import pathlib
from ouroboros.tools.query_code import _query_code
from ouroboros.tools.registry import ToolContext, ToolRegistry
def _repo(tmp_path: pathlib.Path) -> pathlib.Path:
repo = tmp_path / "repo"
(repo / "pkg").mkdir(parents=True)
(repo / "pkg" / "__init__.py").write_text("", encoding="utf-8")
(repo / "pkg" / "helper.py").write_text(
"def helper():\n"
" return 1\n",
encoding="utf-8",
)
(repo / "pkg" / "main.py").write_text(
"from .helper import helper\n\n"
"class Worker:\n"
" def run(self):\n"
" return helper()\n",
encoding="utf-8",
)
(repo / "web.js").write_text("import {x} from './x.js';\nfunction start(){ return x(); }\n", encoding="utf-8")
return repo
def test_query_code_symbols_references_callers_and_impact(tmp_path):
repo = _repo(tmp_path)
data = tmp_path / "data"
ctx = ToolContext(repo_dir=repo, drive_root=data)
symbols = _query_code(ctx, op="symbols", query="Worker")
assert "pkg/main.py" in symbols
assert "class Worker" in symbols
refs = _query_code(ctx, op="references", query="helper", path="pkg/helper.py")
assert "pkg/main.py" in refs
callers = _query_code(ctx, op="callers", query="helper", path="pkg/helper.py")
assert "pkg/main.py" in callers
assert "helper" in callers
callees = _query_code(ctx, op="callees", query="run", path="pkg/main.py")
assert "helper" in callees
impact = _query_code(ctx, op="impact", query="pkg/helper.py")
assert "pkg/main.py" in impact
def test_query_code_relevant_and_structural(tmp_path):
repo = _repo(tmp_path)
ctx = ToolContext(repo_dir=repo, drive_root=tmp_path / "data")
relevant = _query_code(ctx, op="relevant_files", query="worker run helper")
assert "pkg/main.py" in relevant
structural = _query_code(ctx, op="structural", query="FunctionDef", lang="python", path="pkg")
assert "pkg/helper.py" in structural or "pkg/main.py" in structural
js_symbols = _query_code(ctx, op="definition", query="start", path="web.js")
assert "web.js" in js_symbols
def test_query_code_structural_polyglot_go_rust_and_filter(tmp_path):
"""CW11 (v6.34.0): op=structural is polyglot via tree-sitter, lang-filtered, and
never literal-matches — a node-type query must not echo a matching comment."""
repo = _repo(tmp_path)
(repo / "svc.go").write_text(
"package main\n\n// function_declaration appears in this comment\n"
"func Serve() int { return 1 }\n",
encoding="utf-8",
)
(repo / "lib.rs").write_text("pub struct Widget { n: i32 }\n", encoding="utf-8")
ctx = ToolContext(repo_dir=repo, drive_root=tmp_path / "data")
go = _query_code(ctx, op="structural", query="function_declaration", lang="go")
assert "svc.go" in go # the Go function found via tree-sitter (node), not the comment
assert "appears in this comment" not in go # no literal/text fallback
assert "pkg/helper.py" not in go and "lib.rs" not in go # lang=go scopes to Go only
rs = _query_code(ctx, op="structural", query="struct_item", lang="rust")
assert "lib.rs" in rs # Rust struct_item via tree-sitter
py = _query_code(ctx, op="structural", query="FunctionDef", lang="python", path="pkg")
assert "pkg/helper.py" in py or "pkg/main.py" in py # Python ast path unchanged
def test_query_code_structural_unavailable_marker(tmp_path, monkeypatch):
"""A missing tree-sitter grammar surfaces a visible structural_unavailable marker,
never a silent text guess."""
repo = _repo(tmp_path)
(repo / "svc.go").write_text("func Serve() int { return 1 }\n", encoding="utf-8")
ctx = ToolContext(repo_dir=repo, drive_root=tmp_path / "data")
# Force the Go grammar to look unavailable (_structural imports _ts_parser from
# code_intelligence at call time, so patch it there).
monkeypatch.setattr("ouroboros.code_intelligence._ts_parser", lambda grammar: None)
out = _query_code(ctx, op="structural", query="function_declaration", lang="go")
assert "structural_unavailable:go" in out
def test_query_code_structural_schema_enum_is_polyglot():
from ouroboros.tools.query_code import get_tools
enum = get_tools()[0].schema["parameters"]["properties"]["lang"]["enum"]
for lang in ("go", "rust", "java", "ruby", "c", "cpp"):
assert lang in enum
def test_query_code_registered_and_policy_wired(tmp_path):
from ouroboros.safety import POLICY_SKIP, TOOL_POLICY
from ouroboros.tool_capabilities import (
ACTING_SUBAGENT_TOOL_NAMES,
CORE_TOOL_NAMES,
LOCAL_READONLY_SUBAGENT_TOOL_NAMES,
READ_ONLY_PARALLEL_TOOLS,
TOOL_RESULT_LIMITS,
)
registry = ToolRegistry(repo_dir=_repo(tmp_path), drive_root=tmp_path / "data")
names = {schema["function"]["name"] for schema in registry.schemas()}
assert "query_code" in names
assert TOOL_POLICY["query_code"] == POLICY_SKIP
assert "query_code" in CORE_TOOL_NAMES
assert "query_code" in LOCAL_READONLY_SUBAGENT_TOOL_NAMES
assert "query_code" in ACTING_SUBAGENT_TOOL_NAMES
assert "query_code" in READ_ONLY_PARALLEL_TOOLS
assert TOOL_RESULT_LIMITS["query_code"] == 80_000
def test_query_code_workspace_and_subagent_schema_roots(tmp_path):
from ouroboros.contracts.task_constraint import TaskConstraint
from ouroboros.tool_capabilities import ACTING_SUBAGENT_MODE, LOCAL_READONLY_SUBAGENT_MODE
repo = _repo(tmp_path)
registry = ToolRegistry(repo_dir=repo, drive_root=tmp_path / "data")
ctx = ToolContext(repo_dir=repo, drive_root=tmp_path / "data", workspace_root=repo, workspace_mode="external")
registry.set_context(ctx)
assert "query_code" in {schema["function"]["name"] for schema in registry.schemas()}
readonly = ToolContext(
repo_dir=repo,
drive_root=tmp_path / "data",
task_constraint=TaskConstraint(mode=LOCAL_READONLY_SUBAGENT_MODE),
)
registry.set_context(readonly)
schema = registry.get_schema_by_name("query_code")["function"]
assert schema["parameters"]["properties"]["root"]["enum"] == ["active_workspace", "system_repo"]
acting = ToolContext(
repo_dir=repo,
drive_root=tmp_path / "data",
task_constraint=TaskConstraint(mode=ACTING_SUBAGENT_MODE, surface="self_worktree"),
)
registry.set_context(acting)
schema = registry.get_schema_by_name("query_code")["function"]
assert schema["parameters"]["properties"]["root"]["enum"] == ["active_workspace"]
blocked = _query_code(acting, op="symbols", query="Worker", root="system_repo")
assert "TOOL_ACCESS_BLOCKED" in blocked
def test_query_code_hides_local_readonly_subagent_secret_paths(tmp_path):
from ouroboros.contracts.task_constraint import TaskConstraint
from ouroboros.tool_capabilities import LOCAL_READONLY_SUBAGENT_MODE
repo = _repo(tmp_path)
(repo / "auth").mkdir()
(repo / "auth" / "secret.py").write_text("def leak():\n return 'TOKEN_LEAK'\n", encoding="utf-8")
ctx = ToolContext(
repo_dir=repo,
drive_root=tmp_path / "data",
task_constraint=TaskConstraint(mode=LOCAL_READONLY_SUBAGENT_MODE),
)
result = _query_code(ctx, op="structural", query="leak", path="auth")
assert "TOKEN_LEAK" not in result
assert "auth/secret.py" not in result
cache_files = list((tmp_path / "data" / "state" / "code_intel").glob("*/inventory.json"))
assert cache_files == []