Agent web jobs now require human review before final RFIs/reports are issued:
Pipeline stops after Brain consolidation, persists a review queue (outputs/<job>/review/), and parks the job in needs_review
Blocking items: high/critical severity, low-confidence, and sensitive-category findings; non-blocking audit sample of clean clusters
Review API (GET /jobs/{id}/review, POST .../review-decisions, POST .../finalize-review) + frontend review queue UI with reason codes for rejections
Finalizer applies decisions (rejections suppressed with reason codes, not deleted), performs bounded targeted reruns for clarifications (degrade to visible analysis_gap), drafts RFIs only for kept issues, rebuilds final conflicts/counts from kept findings
Two-phase email: review-required on needs_review, final report only after finalization
Per-decision feedback labels; aggregate metrics redact source_text/images/comments by default
Restart recovery from job artifacts (status derivation + job.json hydration)
Plan + design spec included under docs/superpowers/.
Testing
65 non-LLM tests, all passing (pytest); review logic fully covered without PDFs/network/OpenRouter
Full-flow coverage: gate policy, queue build, store round-trip, runner gate, API state transitions, finalization (confirm/reject/unsure/clarify), restart recovery, email flow, metrics redaction
Notes
Manual browser check of the review UI still pending (JS verified by review only — no JS engine in dev env)
Review endpoints are state-changing and unauthenticated — do not expose beyond trusted LAN without reverse-proxy auth (documented in README)
Spec discrepancy recorded: spec said audit sample = 5 items total; implementation queues all non-blocking findings as audit items plus 5 clean clusters (strictly more transparent; final review recommended amending the spec, not the code)
## Summary
Agent web jobs now require human review before final RFIs/reports are issued:
- Pipeline stops after Brain consolidation, persists a review queue (`outputs/<job>/review/`), and parks the job in `needs_review`
- Blocking items: high/critical severity, low-confidence, and sensitive-category findings; non-blocking audit sample of clean clusters
- Review API (`GET /jobs/{id}/review`, `POST .../review-decisions`, `POST .../finalize-review`) + frontend review queue UI with reason codes for rejections
- Finalizer applies decisions (rejections suppressed with reason codes, not deleted), performs bounded targeted reruns for clarifications (degrade to visible `analysis_gap`), drafts RFIs only for kept issues, rebuilds final conflicts/counts from kept findings
- Two-phase email: review-required on `needs_review`, final report only after finalization
- Per-decision feedback labels; aggregate metrics redact source_text/images/comments by default
- Restart recovery from job artifacts (status derivation + job.json hydration)
- CLI `--no-review` bypass; `AGENT_REQUIRE_REVIEW` / `AGENT_REVIEW_AUDIT_SAMPLE` / `REVIEW_AGGREGATE_INCLUDE_TEXT` env knobs
- Classic pipeline unchanged
Plan + design spec included under `docs/superpowers/`.
## Testing
- 65 non-LLM tests, all passing (`pytest`); review logic fully covered without PDFs/network/OpenRouter
- Full-flow coverage: gate policy, queue build, store round-trip, runner gate, API state transitions, finalization (confirm/reject/unsure/clarify), restart recovery, email flow, metrics redaction
## Notes
- Manual browser check of the review UI still pending (JS verified by review only — no JS engine in dev env)
- Review endpoints are state-changing and unauthenticated — do not expose beyond trusted LAN without reverse-proxy auth (documented in README)
- Spec discrepancy recorded: spec said audit sample = 5 items total; implementation queues all non-blocking findings as audit items plus 5 clean clusters (strictly more transparent; final review recommended amending the spec, not the code)
Wire specialist waves, Brain consolidation, and Classic-compatible reports so Agent mode can run end-to-end via OpenRouter without changing the default Classic path.
Co-authored-by: Cursor <cursoragent@cursor.com>
Publish agent-mode pushes as :agent-mode (and :sha-<commit>) so the experimental fork is available from the registry without overwriting the Classic :latest image.
Co-authored-by: Cursor <cursoragent@cursor.com>
Agent web jobs now stop after Brain consolidation and enter needs_review
with a persisted review queue (blocking: high-severity, low-confidence,
sensitive-category findings; audit sample of clean clusters). Humans
decide confirm/reject/unsure/needs_clarification via new review API and
frontend queue; a finalizer applies decisions (rejections suppressed with
reason codes), performs bounded targeted reruns for clarifications,
drafts RFIs only for kept issues, and only then marks the job done and
sends the final email. Two-phase email (review-required, then final
report), per-decision feedback labels with redacted aggregate metrics,
restart recovery from job artifacts, and CLI --no-review bypass.
Classic pipeline unchanged. 65 non-LLM tests.
APP_BASE_URL default (config, .env.example, both compose files) is now
https://conchecker.scoutitsystems.com with no port, so review-required
and final-report email links use the public site. CI bakes the short
commit SHA into the image as APP_BUILD via a Docker build-arg; /health
returns version+build and the site header shows the build so it's easy
to confirm which image is deployed. Local runs default to 'dev'.
- Job logs: each job's stdout/stderr is teed into outputs/<id>/job.log
(survives restarts) and served at GET /jobs/{id}/log as text/plain, so
full run logs can be shared for debugging and refinement.
- Model picker: GET /models proxies OpenRouter's public model list with
per-1M-token pricing (1h cache, 502 on failure); the UI shows a model
dropdown with costs when OpenRouter compute is selected, and the pick
overrides vision+text models for that job (Classic and Agent modes).
- Conflicts in the report view are grouped by discipline pair
(collapsible sections, severity-ordered within groups) instead of one
flat severity-only list.
- llm.py: set_model_overrides(vision, text) replaces the single job override;
UI picks still beat per-call agent model args, but never name the hybrid
local model (avoids main's hybrid footgun); local->cloud fallback uses the
text pick.
- jobs.py: timestamped line-split tee (job_log.py), in-memory log + log_tail
polls, full log on terminal states (done/error/needs_review/finalization_error),
log-only disk recovery, error email links to the run log, and failed runs now
append the full traceback to job.log. Keeps pipeline_mode, job.json, and the
review gate.
- models.py: vision/text split via architecture modalities, pricing kept;
/models returns {vision, text, defaults}; /check takes vision_model/text_model
(replacing model); /health adds text_model. models_catalog.py dropped.
- UI: two priced dropdowns (OpenRouter compute only) + live run-log panel.
- Tests updated for dual overrides and the /models shape; new coverage for
traceback capture and local-model immunity.
- [LLM] line per call: stage, model, prompt size, output size, cost, parsed item counts
- LLM_RAW_DUMP: full prompt/response JSON per call under outputs/<job>/llm_raw/
- Cost block at tail of job.log (per-stage, per-model, cached vs live)
- Agent mode: reset llm cost counters per job; review finalization now teed into job.log + dumps
Job 98194fa8d215 showed every extract call hitting the 32k cap with only
~20k chars visible despite reasoning effort=low - Gemini 2.5 Pro still
burned ~25k thinking tokens per sheet.
- EXTRACT_MAX_TOKENS default 32768 -> 65536 (model output ceiling)
- new EXTRACT_REASONING_MAX_TOKENS (default 2048): OpenRouter reasoning
max_tokens / Gemini thinking_budget; takes precedence over effort
- log per-call reasoning token counts (usage.completion_tokens_details)
and include thinking count in the finish_reason=length marker
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
Agent web jobs now require human review before final RFIs/reports are issued:
outputs/<job>/review/), and parks the job inneeds_reviewGET /jobs/{id}/review,POST .../review-decisions,POST .../finalize-review) + frontend review queue UI with reason codes for rejectionsanalysis_gap), drafts RFIs only for kept issues, rebuilds final conflicts/counts from kept findingsneeds_review, final report only after finalization--no-reviewbypass;AGENT_REQUIRE_REVIEW/AGENT_REVIEW_AUDIT_SAMPLE/REVIEW_AGGREGATE_INCLUDE_TEXTenv knobsPlan + design spec included under
docs/superpowers/.Testing
pytest); review logic fully covered without PDFs/network/OpenRouterNotes
- Job logs: each job's stdout/stderr is teed into outputs/<id>/job.log (survives restarts) and served at GET /jobs/{id}/log as text/plain, so full run logs can be shared for debugging and refinement. - Model picker: GET /models proxies OpenRouter's public model list with per-1M-token pricing (1h cache, 502 on failure); the UI shows a model dropdown with costs when OpenRouter compute is selected, and the pick overrides vision+text models for that job (Classic and Agent modes). - Conflicts in the report view are grouped by discipline pair (collapsible sections, severity-ordered within groups) instead of one flat severity-only list.- llm.py: set_model_overrides(vision, text) replaces the single job override; UI picks still beat per-call agent model args, but never name the hybrid local model (avoids main's hybrid footgun); local->cloud fallback uses the text pick. - jobs.py: timestamped line-split tee (job_log.py), in-memory log + log_tail polls, full log on terminal states (done/error/needs_review/finalization_error), log-only disk recovery, error email links to the run log, and failed runs now append the full traceback to job.log. Keeps pipeline_mode, job.json, and the review gate. - models.py: vision/text split via architecture modalities, pricing kept; /models returns {vision, text, defaults}; /check takes vision_model/text_model (replacing model); /health adds text_model. models_catalog.py dropped. - UI: two priced dropdowns (OpenRouter compute only) + live run-log panel. - Tests updated for dual overrides and the /models shape; new coverage for traceback capture and local-model immunity.- backend/text_layer.py: PyMuPDF text-layer extraction, fuzzy evidence bbox matching, 300-DPI crop rendering, coverage-gap signal - extractor (classic + agent): TEXT LAYER block appended at call sites; grounding guard gains text-layer rescue tier (grounding=text_layer stamp) - verifier: {text_layer} oracle excerpt + evidence-located hi-DPI crops replacing full-page images (fallback preserved, I2 guard intact) - coverage gaps: text-bearing pages with zero extraction -> failed-scope gap findings (agent) / log-only (classic) - config knobs: TEXT_LAYER_ENABLED/MIN_CHARS/MAX_CHARS, VERIFY_TEXT_MAX_CHARS, VERIFY_HI_DPI_CROPS, VERIFY_CROP_DPI, VERIFY_CROP_MARGIN_PTS - tests: 22 new (text_layer unit, grounding/render, runner-level flow) Spec: docs/superpowers/specs/2026-08-12-text-layer-grounding-design.mdView command line instructions
Checkout
From your project repository, check out a new branch and test the changes.