feat: implement append_note_content function and update related tests

This commit is contained in:
bearsyankees
2026-03-23 20:36:45 -04:00
parent 7a06df8e05
commit 21f89dd6bd
7 changed files with 71 additions and 15 deletions

View File

@@ -738,7 +738,7 @@ def _parse_name_status_z(raw_output: bytes) -> list[DiffEntry]:
# Backward-compat fallback if output is tab-delimited unexpectedly. # Backward-compat fallback if output is tab-delimited unexpectedly.
status_fallback, has_tab, first_path = token.partition("\t") status_fallback, has_tab, first_path = token.partition("\t")
if not has_tab: if not has_tab:
break break
fallback_code = status_fallback[:1] fallback_code = status_fallback[:1]
fallback_similarity: int | None = None fallback_similarity: int | None = None
if len(status_fallback) > 1 and status_fallback[1:].isdigit(): if len(status_fallback) > 1 and status_fallback[1:].isdigit():

View File

@@ -113,7 +113,7 @@ class LLM:
ordered_skills.append(f"scan_modes/{self.config.scan_mode}") ordered_skills.append(f"scan_modes/{self.config.scan_mode}")
if self.config.is_whitebox: if self.config.is_whitebox:
ordered_skills.append("coordination/source_aware_whitebox") ordered_skills.append("coordination/source_aware_whitebox")
ordered_skills.append("source_aware_sast") ordered_skills.append("custom/source_aware_sast")
deduped: list[str] = [] deduped: list[str] = []
seen: set[str] = set() seen: set[str] = set()

View File

@@ -36,10 +36,13 @@ mkdir -p "$ART"
semgrep scan --config p/default --config p/golang --config p/secrets \ semgrep scan --config p/default --config p/golang --config p/secrets \
--metrics=off --json --output "$ART/semgrep.json" . --metrics=off --json --output "$ART/semgrep.json" .
sg scan --json . > "$ART/ast-grep.json" # Ruleless AST pass (works without sgconfig.yml/rules project setup)
sg run --pattern '$F($$$ARGS)' --json=stream . > "$ART/ast-grep.json" 2> "$ART/ast-grep.log" || true
gitleaks detect --source . --report-format json --report-path "$ART/gitleaks.json" || true gitleaks detect --source . --report-format json --report-path "$ART/gitleaks.json" || true
trufflehog filesystem --no-update --json --no-verification . > "$ART/trufflehog.json" || true trufflehog filesystem --no-update --json --no-verification . > "$ART/trufflehog.json" || true
trivy fs --format json --output "$ART/trivy-fs.json" . # Keep trivy focused on vuln/misconfig (secrets already covered above) and increase timeout for large repos
trivy fs --scanners vuln,misconfig --timeout 30m --offline-scan \
--format json --output "$ART/trivy-fs.json" . || true
``` ```
If one tool is skipped or fails, record that in the shared wiki note along with the reason. If one tool is skipped or fails, record that in the shared wiki note along with the reason.
@@ -64,7 +67,8 @@ If diff scope is active, restrict to changed files first, then expand only when
Use `sg` for structure-aware code hunting: Use `sg` for structure-aware code hunting:
```bash ```bash
sg scan --json . > /workspace/.strix-source-aware/ast-grep.json # Ruleless one-off structural pass (no sgconfig.yml required)
sg run --pattern '$F($$$ARGS)' --json=stream . > /workspace/.strix-source-aware/ast-grep.json 2> /workspace/.strix-source-aware/ast-grep.log || true
``` ```
Target high-value patterns such as: Target high-value patterns such as:
@@ -95,7 +99,8 @@ trufflehog filesystem --json . > /workspace/.strix-source-aware/trufflehog.json
Run repository-wide dependency and config checks: Run repository-wide dependency and config checks:
```bash ```bash
trivy fs --format json --output /workspace/.strix-source-aware/trivy-fs.json . trivy fs --scanners vuln,misconfig --timeout 30m --offline-scan \
--format json --output /workspace/.strix-source-aware/trivy-fs.json . || true
``` ```
## Converting Static Signals Into Exploits ## Converting Static Signals Into Exploits

View File

@@ -123,7 +123,7 @@ def _append_wiki_update_on_finish(
return return
try: try:
from strix.tools.notes.notes_actions import update_note from strix.tools.notes.notes_actions import append_note_content
note = _load_primary_wiki_note(agent_state) note = _load_primary_wiki_note(agent_state)
if not note: if not note:
@@ -133,7 +133,6 @@ def _append_wiki_update_on_finish(
if not isinstance(note_id, str) or not note_id: if not isinstance(note_id, str) or not note_id:
return return
existing_content = str(note.get("content") or "")
timestamp = datetime.now(UTC).isoformat() timestamp = datetime.now(UTC).isoformat()
summary = " ".join(str(result_summary).split()) summary = " ".join(str(result_summary).split())
if len(summary) > 1200: if len(summary) > 1200:
@@ -151,8 +150,7 @@ def _append_wiki_update_on_finish(
"Recommendations:\n" "Recommendations:\n"
f"{recommendation_lines}\n" f"{recommendation_lines}\n"
) )
updated_content = f"{existing_content.rstrip()}{delta}" append_note_content(note_id=note_id, delta=delta)
update_note(note_id=note_id, content=updated_content)
except Exception: except Exception:
# Best-effort update; never block agent completion on note persistence. # Best-effort update; never block agent completion on note persistence.
return return

View File

@@ -364,6 +364,26 @@ def get_note(note_id: str) -> dict[str, Any]:
return {"success": True, "note": note_with_id} return {"success": True, "note": note_with_id}
def append_note_content(note_id: str, delta: str) -> dict[str, Any]:
with _notes_lock:
try:
_ensure_notes_loaded()
if note_id not in _notes_storage:
return {"success": False, "error": f"Note with ID '{note_id}' not found"}
if not isinstance(delta, str):
return {"success": False, "error": "Delta must be a string"}
note = _notes_storage[note_id]
existing_content = str(note.get("content") or "")
updated_content = f"{existing_content.rstrip()}{delta}"
return update_note(note_id=note_id, content=updated_content)
except (ValueError, TypeError) as e:
return {"success": False, "error": f"Failed to append note content: {e}"}
@register_tool(sandbox_execution=False) @register_tool(sandbox_execution=False)
def update_note( def update_note(
note_id: str, note_id: str,

View File

@@ -164,14 +164,14 @@ def test_agent_finish_appends_wiki_update_for_whitebox(monkeypatch) -> None:
}, },
} }
def fake_update_note(note_id: str, content: str): def fake_append_note_content(note_id: str, delta: str):
captured["note_id"] = note_id captured["note_id"] = note_id
captured["content"] = content captured["delta"] = delta
return {"success": True, "note_id": note_id} return {"success": True, "note_id": note_id}
monkeypatch.setattr("strix.tools.notes.notes_actions.list_notes", fake_list_notes) monkeypatch.setattr("strix.tools.notes.notes_actions.list_notes", fake_list_notes)
monkeypatch.setattr("strix.tools.notes.notes_actions.get_note", fake_get_note) monkeypatch.setattr("strix.tools.notes.notes_actions.get_note", fake_get_note)
monkeypatch.setattr("strix.tools.notes.notes_actions.update_note", fake_update_note) monkeypatch.setattr("strix.tools.notes.notes_actions.append_note_content", fake_append_note_content)
state = SimpleNamespace(agent_id=child_id, parent_id=parent_id) state = SimpleNamespace(agent_id=child_id, parent_id=parent_id)
result = agents_graph_actions.agent_finish( result = agents_graph_actions.agent_finish(
@@ -185,8 +185,8 @@ def test_agent_finish_appends_wiki_update_for_whitebox(monkeypatch) -> None:
assert result["agent_completed"] is True assert result["agent_completed"] is True
assert captured_get["note_id"] == "wiki-note-1" assert captured_get["note_id"] == "wiki-note-1"
assert captured["note_id"] == "wiki-note-1" assert captured["note_id"] == "wiki-note-1"
assert "Agent Update: Child" in captured["content"] assert "Agent Update: Child" in captured["delta"]
assert "AST pass completed" in captured["content"] assert "AST pass completed" in captured["delta"]
def test_run_agent_in_thread_injects_shared_wiki_context_in_whitebox(monkeypatch) -> None: def test_run_agent_in_thread_injects_shared_wiki_context_in_whitebox(monkeypatch) -> None:

View File

@@ -137,3 +137,36 @@ def test_get_note_returns_full_note(tmp_path: Path, monkeypatch) -> None:
finally: finally:
_reset_notes_state() _reset_notes_state()
set_global_tracer(previous_tracer) # type: ignore[arg-type] set_global_tracer(previous_tracer) # type: ignore[arg-type]
def test_append_note_content_appends_delta(tmp_path: Path, monkeypatch) -> None:
monkeypatch.chdir(tmp_path)
_reset_notes_state()
previous_tracer = get_global_tracer()
tracer = Tracer("append-note-run")
set_global_tracer(tracer)
try:
created = notes_actions.create_note(
title="Repo wiki",
content="base",
category="wiki",
tags=["repo:demo"],
)
assert created["success"] is True
note_id = created["note_id"]
assert isinstance(note_id, str)
appended = notes_actions.append_note_content(
note_id=note_id,
delta="\n\n## Agent Update: worker\nSummary: done",
)
assert appended["success"] is True
loaded = notes_actions.get_note(note_id=note_id)
assert loaded["success"] is True
assert loaded["note"]["content"] == "base\n\n## Agent Update: worker\nSummary: done"
finally:
_reset_notes_state()
set_global_tracer(previous_tracer) # type: ignore[arg-type]