The approval list and detail return decided_by exactly as stored, and the
decided_by filter matches that same value. Rows decided before the column
existed show "Legacy" and match no decided_by filter; their decision_source
stays in its own column and nothing is rewritten.
UI: a "Decided by" column (Human, Rule, Model, System) and a matching filter
beside the decision-source filter, including "AI-approved only" for model.
Types and the API query carry decided_by the same way decision_source does.
Add intercept_pending.decided_by (human | rule | model | system; empty while
pending or for rows decided before the column existed). Approval semantics are
unchanged: only the actor is recorded.
- rule-matched and model-judged rows written by CreateDecidedIntercept record
rule or model;
- ResolveIntercept takes the actor explicitly and rejects any other value
before touching the database;
- Decide (human allow/deny) records human; timeouts and work-cancelled
denials record system. Work-cancelled is a system refusal, not a human one.
- decided_by is exposed on the pending and approval records and filterable
with decided_by=human|rule|model|system (anything else is a 400).
- Legacy rows show decision_source as their actor when decided_by is empty.
The filter uses the stored column only, so decided_by=model cannot match
older human decisions or timeouts.
- Archive restore fills decided_by for archives that predate the column, and
marks a restored pending row as system.
Since e233c02 the judge writer prefixes its reason with "[model]" while
interceptSource, the approvalDecisionSource SQL fallback and the archive
restore checked "[Model]", so model-decided rows were inserted with
decision_source='unknown' and missed by the decision_source=model filter.
- db: add ModelReasonPrefix ("[model]") and isModelReason, which also
accepts the pre-e233c02 "[模型]" prefix; readers compare
case-insensitively (lower(reason) LIKE in SQL).
- intercept: the writer builds its reason through JudgeReason, using the
shared constant.
- schema.sql: idempotent backfill of 'unknown' rows without a rule whose
reason carries either prefix.
- tests: un-skip TestInterceptFilterHTTP (it failed only because of
this), make the db filter fixture use the constant, and add a
Postgres-free test pinning the writer output to interceptSource.
Fixes#156
Comments, doc comments, UI labels, log and error messages, LLM prompt
strings, markdown docs, test data and assertions. Identifiers, enum
keys, routes, placeholders and code structure are unchanged.
Notes:
- docs/漏洞流量证据.md renamed to docs/vulnerability-traffic-evidence.md
- tests that exercised CJK specific handling (GBK zip names, Chinese
full text search) now use ASCII equivalents; go build passes and the
test suite matches the pre translation baseline (remaining failures
need a PostgreSQL DSN and fail identically on the original)
- frontend mention syntax now uses English kinds (finding, asset, ...)
matching the backend, chat mentions tests pass