Set up document management for Stirling Engine (#6476)

# Description of Changes
Change Stirling Engine to support deleting documents automatically. This
happens both on user logout and after an amount of time specified by the
Java when ingesting a document (allowing for personal documents to have
short lifetimes but org documents to be left in the db with no expiry
date). Also sets up an [ACL
policy](https://en.wikipedia.org/wiki/Access-control_list) for the
documents so the database knows which users have access to which
documents. This is not fully implemented in the Java, so currently all
docs are treated as having a single owner, the uploader, but
theoretically when we need to support org storage, we shouldn't need to
change the db schema.
This commit is contained in:
James Brunton
2026-06-03 11:52:11 +00:00
committed by GitHub
parent 71633861d0
commit 1264f4cfed
48 changed files with 2039 additions and 411 deletions
@@ -17,9 +17,11 @@ from stirling.contracts.contradiction import (
ContradictionReport,
ContradictionSeverity,
)
from stirling.models import FileId
from stirling.models import FileId, PrincipalId
from stirling.services.runtime import AppRuntime
PRINCIPALS = [PrincipalId("test-user")]
def _file(file_id: str, name: str) -> AiFile:
return AiFile(id=FileId(file_id), name=name)
@@ -58,7 +60,7 @@ async def test_find_contradictions_returns_formatted_text(runtime: AppRuntime) -
canned = _canned_report()
detector.detect = AsyncMock(return_value=canned)
capability = ContradictionCapability(detector=detector, files=[_file("doc-a", "a.pdf")])
capability = ContradictionCapability(detector=detector, files=[_file("doc-a", "a.pdf")], principals=PRINCIPALS)
result = await capability._find_contradictions("are there inconsistent deadlines?")
detector.detect.assert_awaited_once()
@@ -84,6 +86,7 @@ async def test_budget_gate_hides_tool_after_first_audit(runtime: AppRuntime) ->
capability = ContradictionCapability(
detector=detector,
files=[_file("doc-a", "a.pdf")],
principals=PRINCIPALS,
max_audits=1,
)
# A real, minimal ToolDefinition — the prepare callback returns this
@@ -107,7 +110,7 @@ async def test_budget_gate_hides_tool_after_first_audit(runtime: AppRuntime) ->
async def test_find_contradictions_with_no_files_returns_message(runtime: AppRuntime) -> None:
detector = ContradictionDetector(runtime)
detector.detect = AsyncMock(return_value=_canned_report())
capability = ContradictionCapability(detector=detector, files=[])
capability = ContradictionCapability(detector=detector, files=[], principals=PRINCIPALS)
result = await capability._find_contradictions("anything")
@@ -120,6 +123,7 @@ def test_instructions_mention_attached_files(runtime: AppRuntime) -> None:
capability = ContradictionCapability(
detector=detector,
files=[_file("doc-a", "alpha.pdf"), _file("doc-b", "beta.pdf")],
principals=PRINCIPALS,
)
text = capability.instructions
@@ -149,6 +153,7 @@ def test_instructions_escape_filename_injection_attempt(runtime: AppRuntime) ->
capability = ContradictionCapability(
detector=detector,
files=[_file("doc-evil", evil_name)],
principals=PRINCIPALS,
)
text = capability.instructions