132 lines
4.4 KiB
Markdown
132 lines
4.4 KiB
Markdown
# Task 3 Report: Safe Blocker Resolution Workflow
|
|
|
|
## Status
|
|
|
|
Implemented Task 3 only. The old hidden-context blocker action was replaced by a structured, server-rendered, CSRF-protected resolution workflow. The JSON control-panel route now uses the same validated transaction service and rejects arbitrary context text.
|
|
|
|
## RED
|
|
|
|
Created `central/tests/test_blocker_resolution_panel.py` before production implementation.
|
|
|
|
Initial focused command:
|
|
|
|
```text
|
|
python -m pytest central/tests/test_blocker_resolution_panel.py -q
|
|
```
|
|
|
|
Expected RED result:
|
|
|
|
```text
|
|
ModuleNotFoundError: No module named 'central.app.blocker_resolution'
|
|
```
|
|
|
|
The failure occurred during collection because the required production module did not yet exist.
|
|
|
|
## Implementation
|
|
|
|
- Added `central/app/blocker_resolution.py` with structured input validation through `validate_entry`, workflow-scope restrictions, exact PRODUCT scope-key enforcement, persisted installation/company context derivation, context upsert, bounded `REPROCESS_PRODUCTS` job creation, sanitized audit recording, and one commit with rollback on failure.
|
|
- Added `GET /blockers/{blocker_id}` with installation-aware blocker detail and context form.
|
|
- Added `POST /blockers/{blocker_id}/resolve-context` with human authentication and CSRF enforcement.
|
|
- Updated `/v1/blockers/{blocker_id}/resolve-human` and added `/v1/blockers/{blocker_id}/resolve-context` to call the same service; neither accepts arbitrary context text.
|
|
- Removed the blocker-list hidden fixed-context form and `data-confirm` action.
|
|
- Added focused coverage for enum validation, unknown keys, unsupported NCM scope, empty reason, missing PRODUCT scope key, authentication, CSRF, context/job/audit persistence, exact product scoping, and secret redaction.
|
|
|
|
## GREEN
|
|
|
|
Focused command:
|
|
|
|
```text
|
|
python -m pytest central/tests/test_blocker_resolution_panel.py -q
|
|
```
|
|
|
|
Result:
|
|
|
|
```text
|
|
5 passed, 1 warning
|
|
```
|
|
|
|
Central suite:
|
|
|
|
```text
|
|
python -m pytest central/tests -q
|
|
```
|
|
|
|
Result:
|
|
|
|
```text
|
|
261 passed, 1 warning
|
|
```
|
|
|
|
The warnings are existing third-party test-environment warnings from `pytest_asyncio` configuration and `openpyxl` print-area parsing.
|
|
|
|
## Safety Review
|
|
|
|
- No Firebird, AI, SQL execution, shell, credential, or remote-write path was added.
|
|
- The workflow accepts only `scope`, `scope_key`, `key`, `value`, and `reason`; context keys and enum values are validated against existing shared rules.
|
|
- Company context is derived from persisted blocker/installation data, not arbitrary client `company_key` input.
|
|
- Jobs contain exactly the blocked product code.
|
|
- Audit context passes through existing redaction before persistence.
|
|
|
|
## Concerns
|
|
|
|
- Live PostgreSQL and agent execution were not exercised; verification used the repository's offline SQLite test fixtures.
|
|
- The blocker remains open until the agent's normal reprocess completion path reports resolution; this prevents marking it resolved before the bounded job succeeds.
|
|
|
|
## Review Fixes
|
|
|
|
### RED
|
|
|
|
Added regression tests before changing production code for the four review findings:
|
|
|
|
- Legacy blockers with `installation_id=NULL` must fail closed without selecting an Agent or creating work.
|
|
- Cross-tenant installation and Agent pointers must fail before context, job, or audit mutation.
|
|
- Oversized values/reasons and non-string fields must be rejected cleanly.
|
|
- Successful web resolution must redirect to the blocker detail page.
|
|
|
|
Focused command before the fixes:
|
|
|
|
```text
|
|
python -m pytest central/tests/test_blocker_resolution_panel.py -q
|
|
```
|
|
|
|
RED result:
|
|
|
|
```text
|
|
4 failed, 5 passed
|
|
```
|
|
|
|
The failures reproduced the legacy Agent fallback, cross-tenant acceptance, unbounded/malformed input, and `/blockers` redirect behavior.
|
|
|
|
### GREEN
|
|
|
|
- Resolution now requires a non-null exact installation and verifies both installation and Agent tenant ownership before mutation.
|
|
- `value` is required to be a string of at most 512 characters; `reason` is required to be a string of at most 2000 characters.
|
|
- Boundary-sized audit data retains all required audit fields without whole-context truncation.
|
|
- Web success redirects to `/blockers/{blocker_id}`.
|
|
|
|
Focused command:
|
|
|
|
```text
|
|
python -m pytest central/tests/test_blocker_resolution_panel.py -q
|
|
```
|
|
|
|
Result:
|
|
|
|
```text
|
|
9 passed, 1 warning
|
|
```
|
|
|
|
Full central command:
|
|
|
|
```text
|
|
python -m pytest central/tests -q
|
|
```
|
|
|
|
Result:
|
|
|
|
```text
|
|
265 passed, 1 warning
|
|
```
|
|
|
|
The existing `pytest_asyncio` configuration and `openpyxl` print-area warnings remain unchanged.
|