donna.cost.tool_request_repository¶
donna.cost.tool_request_repository
¶
Async aiosqlite repository for tool_request (slice 22).
CRUD + dedup-on-open + snooze for the §7 tool-gap protocol's storage
layer. Mirrors the raw-aiosqlite pattern used by
:class:donna.cost.escalation_repository.EscalationRepository —
single shared connection, no SQLAlchemy ORM at runtime.
Realizes docs/superpowers/specs/manual-escalation.md §7, §8.
ToolRequestRow
dataclass
¶
ToolRequestRow(id: int, user_id: str, tool_name: str, proposed_signature: dict[str, Any] | None, rationale: str | None, blocking_capability_id: str | None, priority: int, status: str, severity: str, detection_point: str | None, snoozed_until: datetime | None, first_seen_at: datetime, last_seen_at: datetime, created_at: datetime, resolved_at: datetime | None, resolved_branch: str | None, escalation_request_id: int | None, last_pinged_at: datetime | None)
In-memory projection of a tool_request row.
All datetimes are timezone-aware (UTC). Optional columns default
to None so future migrations can extend without breaking
callers.
RecordResult
dataclass
¶
ToolRequestRepository
¶
CRUD for the tool_request table.
Source code in src/donna/cost/tool_request_repository.py
record
async
¶
Upsert a gap.
If an open row exists for (user_id, tool_name): bump
priority to max(existing, new), refresh rationale /
severity / detection_point / last_seen_at, do not
clear snoozed_until (re-emission while snoozed is silent).
Otherwise: insert a fresh row in status='open'.
Source code in src/donna/cost/tool_request_repository.py
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 | |
snooze
async
¶
Set snoozed_until = now + seconds if row is open.
Returns True if the row was updated, False otherwise (already resolved / snoozed beyond requested deadline / unknown id).
Source code in src/donna/cost/tool_request_repository.py
mark_in_progress
async
¶
mark_in_progress(request_id: int, *, escalation_request_id: int, now: datetime | None = None) -> bool
Source code in src/donna/cost/tool_request_repository.py
mark_completed
async
¶
Source code in src/donna/cost/tool_request_repository.py
mark_rejected
async
¶
Source code in src/donna/cost/tool_request_repository.py
mark_pinged
async
¶
Stamp last_pinged_at after a successful Discord post.
Used by the surfacer to rate-limit re-pings on dedup hits.
Source code in src/donna/cost/tool_request_repository.py
get
async
¶
Source code in src/donna/cost/tool_request_repository.py
find_open
async
¶
Source code in src/donna/cost/tool_request_repository.py
list_open_speculative
async
¶
list_open_speculative(*, exclude_snoozed: bool = True, now: datetime | None = None) -> list[ToolRequestRow]
Open + speculative + (optionally) not currently snoozed.
Used by :class:donna.notifications.digest.MorningDigest to
render the daily tool-gap aggregation. High-severity rows are
excluded — they already pinged in real time.
Source code in src/donna/cost/tool_request_repository.py
list_open_by_status
async
¶
list_open_by_status(statuses: tuple[str, ...] = (STATUS_OPEN, STATUS_IN_PROGRESS)) -> list[ToolRequestRow]
Source code in src/donna/cost/tool_request_repository.py
list_completed_resolved_before
async
¶
Return status='completed' rows resolved before cutoff.
Slice 24 (spec §10.5 row 1) feeds this into the
RequiresRebuildNagger: a tool that's been merged for
longer than the nag threshold but hasn't appeared in the
orchestrator's ToolRegistry since reboot still needs a
rebuild. We sort by resolved_at ASC so the oldest stuck
rows nag first.