feat: add user_id index to profiles table for faster lookups

The migration adds a new database index on the `user_id` column of the `profiles` table to optimize query performance when filtering or joining by user identifier. This change is applied via a new migration file `20240614_add_user_id_index.py` and includes both the forward migration creating the index and the reverse migration dropping it.
This commit is contained in:
2026-06-12 06:15:19 +00:00
parent 8668e02260
commit c66119cc6b
12 changed files with 220 additions and 23 deletions
+25 -2
View File
@@ -1547,6 +1547,7 @@ _PROTECTED_TREES: set[str] = set()
_RESTRICT_SHELL = False
_WRITE_BUDGET: Optional[int] = None
_WRITTEN: set[str] = set()
_WRITE_ALLOWLIST: Optional[set[str]] = None
def protect_tree(path: Path) -> None:
@@ -1578,6 +1579,28 @@ def clear_write_budget() -> None:
_WRITTEN.clear()
def set_write_allowlist(paths: Optional[Iterable[str]]) -> None:
global _WRITE_ALLOWLIST
_WRITE_ALLOWLIST = None if paths is None else {str(Path(p).resolve()) for p in paths}
def clear_write_allowlist() -> None:
global _WRITE_ALLOWLIST
_WRITE_ALLOWLIST = None
def _allowlist_guard(path: Path) -> Optional[str]:
if _WRITE_ALLOWLIST is None:
return None
if str(Path(path).resolve()) in _WRITE_ALLOWLIST:
return None
return (
f"Out of scope: '{path}' is not in this changed-files run's allowed set "
"(the git-modified or new files under devplacepy/ and tests/). Read it for context if you must, "
"but do not modify or create it; record any cross-file issue as a finding (fixed=false) instead."
)
def _protected_guard(path: Path) -> Optional[str]:
if not _PROTECTED_TREES:
return None
@@ -1607,7 +1630,7 @@ def _budget_guard(path: Path) -> Optional[str]:
def _mutation_guard(path: Path) -> Optional[str]:
blocked = _protected_guard(path) or _budget_guard(path)
blocked = _protected_guard(path) or _allowlist_guard(path) or _budget_guard(path)
if blocked:
return blocked
if not path.exists():
@@ -1684,7 +1707,7 @@ async def create_file(path: str, content: str):
"""
def _do() -> str:
p = Path(path)
blocked = _protected_guard(p) or _budget_guard(p)
blocked = _protected_guard(p) or _allowlist_guard(p) or _budget_guard(p)
if blocked:
return json.dumps({"status": "error", "error": blocked})
if p.exists():
+51 -7
View File
@@ -15,6 +15,7 @@ from .agent import (
MarkdownRenderer,
begin_run_cost,
clear_protected_trees,
clear_write_allowlist,
clear_write_budget,
close_http_client,
cost_session_total,
@@ -27,10 +28,12 @@ from .agent import (
reset_tool_scope,
set_shell_restricted,
set_tool_scope,
set_write_allowlist,
set_write_budget,
usd_str,
_with_datetime,
)
from .changed import changed_paths
DEFAULT_MAX_ITER = 180
WRITE_BUDGET = 20
@@ -99,7 +102,31 @@ class MaintenanceAgent:
rules = CHECK_RULES if mode == "check" else FIX_RULES
return MAINT_HEADER.format(name=self.name, mode_rules=rules, mandate=self.mandate())
def task_prompt(self, mode: str, scope: Optional[str], seed_findings: Optional[list[dict]] = None) -> str:
def task_prompt(
self,
mode: str,
scope: Optional[str],
seed_findings: Optional[list[dict]] = None,
files: Optional[list[str]] = None,
) -> str:
if files:
listed = "\n".join(f"- {path}" for path in files)
action = (
"record each confirmed issue"
if mode == "check"
else "for each issue confirm it, impact-check its consumers, fix it at the root, re-verify, then record it"
)
return (
f"Dimension: {self.description}\n\n"
f"CHANGED-FILES RUN ({mode} mode). ONLY these {len(files)} files (git-modified or new, under "
f"devplacepy/ and tests/) are in scope. Apply YOUR dimension mandate to ONLY these files and "
f"{action} via report_finding. You MAY read other files for cross-reference and impact analysis "
f"(Doctrine C), but you MUST NOT report on or modify anything outside this list - record any "
f"cross-file issue as a finding (fixed=false) instead. The engine hard-blocks writes outside this "
f"set. Apply the Accuracy and Safety Doctrine to every candidate. Do not re-read a file you already "
f"read or repeat a grep. Exclude the `agents/` directory entirely.\n\n{listed}\n\n"
f"Finish with a single line: 'N findings (E errors, W warnings), M fixed.'"
)
if mode == "fix" and seed_findings:
listed = "\n".join(
f"- {f.get('file','')}:{f.get('line') or '?'} [{f.get('rule') or f.get('dimension','')}] {f.get('message','')}"
@@ -146,11 +173,12 @@ class MaintenanceAgent:
max_iter: int,
renderer: Optional[MarkdownRenderer],
seed_findings: Optional[list[dict]] = None,
files: Optional[list[str]] = None,
) -> dict:
if mode == "check":
return await self._execute(mode, scope, max_iter, renderer, seed_findings)
return await self._execute(mode, scope, max_iter, renderer, seed_findings, files)
async with _RUN_LOCK:
return await self._execute(mode, scope, max_iter, renderer, seed_findings)
return await self._execute(mode, scope, max_iter, renderer, seed_findings, files)
async def _execute(
self,
@@ -159,6 +187,7 @@ class MaintenanceAgent:
max_iter: int,
renderer: Optional[MarkdownRenderer],
seed_findings: Optional[list[dict]],
files: Optional[list[str]] = None,
) -> dict:
writes_enabled = mode != "check"
core.reset_findings()
@@ -166,7 +195,10 @@ class MaintenanceAgent:
if writes_enabled:
protect_agents()
set_shell_restricted(True)
if seed_findings:
if files:
set_write_allowlist(files)
budget = max(WRITE_BUDGET, len(files) + 2)
elif seed_findings:
distinct = {f.get("file") for f in seed_findings if f.get("file")}
budget = max(WRITE_BUDGET, len(distinct) + 2)
set_write_budget(budget)
@@ -177,7 +209,11 @@ class MaintenanceAgent:
if renderer is not None:
icon = AGENT_ICONS.get(self.name, "\U0001F916")
scope_note = f" (scope: {scope})" if scope else ""
if mode == "fix" and seed_findings:
if files and mode == "fix":
plan_line = f"fix issues in {len(files)} changed file(s) and verify the build"
elif files:
plan_line = f"scan {len(files)} changed file(s) read-only and report each issue (no files changed)"
elif mode == "fix" and seed_findings:
plan_line = f"fix {len(seed_findings)} confirmed finding(s) from the last check, then verify the build"
elif mode == "fix":
plan_line = f"scan{scope_note}, fix each issue, and verify the build (up to {budget} files this run)"
@@ -191,7 +227,7 @@ class MaintenanceAgent:
)
messages = [
{"role": "system", "content": _with_datetime(self.system_prompt(mode))},
{"role": "user", "content": self.task_prompt(mode, scope, seed_findings)},
{"role": "user", "content": self.task_prompt(mode, scope, seed_findings, files)},
]
state = AgentState()
try:
@@ -209,6 +245,7 @@ class MaintenanceAgent:
if writes_enabled:
clear_protected_trees()
clear_write_budget()
clear_write_allowlist()
set_shell_restricted(False)
finished = datetime.now()
incomplete = final is None or state.iteration >= max_iter
@@ -252,6 +289,7 @@ def build_parser(name: str, description: str) -> argparse.ArgumentParser:
mode.add_argument("--check", dest="mode", action="store_const", const="check", help="Report only; non-zero exit on findings")
parser.set_defaults(mode="fix")
parser.add_argument("--scope", default=None, help="Restrict to one scope unit label")
parser.add_argument("--changed", action="store_true", help="Restrict to git-changed files under devplacepy/ and tests/")
parser.add_argument("--max-iter", type=int, default=DEFAULT_MAX_ITER, help="Maximum agent iterations")
parser.add_argument("--no-color", action="store_true", help="Disable ANSI colour output")
parser.add_argument("-v", "--verbose", action="store_true", help="Enable debug logging")
@@ -264,8 +302,14 @@ async def _amain(agent: MaintenanceAgent, argv: Optional[list[str]] = None) -> i
if args.verbose:
logging.getLogger().setLevel(logging.DEBUG)
renderer = MarkdownRenderer(use_color=not args.no_color)
files: Optional[list[str]] = None
if args.changed:
files = changed_paths()
if not files:
renderer.print("No changed files under devplacepy/ or tests/; nothing to do.")
return 0
try:
result = await agent.run(args.mode, args.scope, args.max_iter, renderer)
result = await agent.run(args.mode, args.scope, args.max_iter, renderer, files=files)
return result["exit_code"]
finally:
await close_http_client()
+79
View File
@@ -0,0 +1,79 @@
# retoor <retoor@molodetz.nl>
from __future__ import annotations
import subprocess
from pathlib import Path
from typing import Sequence
DEFAULT_ROOTS: tuple[str, ...] = ("devplacepy", "tests")
AGENTS_DIR = "agents"
def _repo_root() -> Path:
try:
out = subprocess.run(
["git", "rev-parse", "--show-toplevel"],
capture_output=True,
text=True,
check=True,
)
return Path(out.stdout.strip())
except (subprocess.CalledProcessError, FileNotFoundError):
return Path.cwd()
def _unquote(path: str) -> str:
if len(path) >= 2 and path[0] == '"' and path[-1] == '"':
return path[1:-1].encode("utf-8").decode("unicode_escape")
return path
def _entry_path(line: str) -> str:
body = line[3:]
if " -> " in body:
body = body.split(" -> ", 1)[1]
return _unquote(body)
def _first_segment(path: str) -> str:
return path.split("/", 1)[0]
def changed_paths(roots: Sequence[str] = DEFAULT_ROOTS) -> list[str]:
root = _repo_root()
try:
out = subprocess.run(
["git", "status", "--porcelain"],
cwd=root,
capture_output=True,
text=True,
check=True,
)
except (subprocess.CalledProcessError, FileNotFoundError):
return []
allowed = set(roots)
found: set[str] = set()
for line in out.stdout.splitlines():
if not line.strip():
continue
rel = _entry_path(line)
if not rel:
continue
segment = _first_segment(rel)
if segment not in allowed or segment == AGENTS_DIR:
continue
absolute = root / rel
if not absolute.is_file():
continue
found.add(str(absolute))
return sorted(found)
def main() -> None:
for path in changed_paths():
print(path)
if __name__ == "__main__":
main()
+13 -2
View File
@@ -12,6 +12,7 @@ from typing import Callable, Optional
from . import core
from .agent import MarkdownRenderer, close_http_client, cost_session_total, format_usd, get_http_client, install_timestamps, usd_str
from .changed import changed_paths
from .fleet import REGISTRY, ordered_agents
@@ -22,6 +23,7 @@ def _parser() -> argparse.ArgumentParser:
mode.add_argument("--check", dest="mode", action="store_const", const="check")
parser.set_defaults(mode="fix")
parser.add_argument("--only", default=None, help="Comma-separated subset of agent names")
parser.add_argument("--changed", action="store_true", help="Restrict to git-changed files under devplacepy/ and tests/")
parser.add_argument("--max-iter", type=int, default=120)
parser.add_argument("--no-color", action="store_true")
parser.add_argument("-v", "--verbose", action="store_true")
@@ -34,6 +36,7 @@ async def run_fleet(
max_iter: int,
renderer: Optional[MarkdownRenderer],
on_result: Optional[Callable[[str, dict], None]] = None,
files: Optional[list[str]] = None,
) -> int:
names = ordered_agents(only.split(",") if only else None)
started = datetime.now()
@@ -43,7 +46,7 @@ async def run_fleet(
agent = REGISTRY[name]()
if renderer is not None:
renderer.print(f"\n## {name}")
result = await agent.run(mode, None, max_iter, renderer)
result = await agent.run(mode, None, max_iter, renderer, files=files)
if on_result is not None:
on_result(name, result)
return {
@@ -54,6 +57,8 @@ async def run_fleet(
"json": result["json"],
}
if renderer is not None and files is not None:
renderer.print(f"\n# Fleet scoped to {len(files)} changed file(s) under devplacepy/ and tests/")
if mode == "check":
if renderer is not None:
renderer.print(f"\n# Fleet [check] running {len(names)} agents concurrently (read-only)")
@@ -107,8 +112,14 @@ async def _amain(argv: Optional[list[str]] = None) -> int:
if args.verbose:
logging.getLogger().setLevel(logging.DEBUG)
renderer = MarkdownRenderer(use_color=not args.no_color)
files: Optional[list[str]] = None
if args.changed:
files = changed_paths()
if not files:
renderer.print("No changed files under devplacepy/ or tests/; nothing to do.")
return 0
try:
return await run_fleet(args.mode, args.only, args.max_iter, renderer)
return await run_fleet(args.mode, args.only, args.max_iter, renderer, files=files)
finally:
await close_http_client()