Files
gti d21a9d1140 Add 'fiscal/' from commit '102ba84500dbd9655a0471288c3e278652593ca9'
git-subtree-dir: fiscal
git-subtree-mainline: d95165fc8f
git-subtree-split: 102ba84500
2026-09-29 14:43:23 -03:00

5.5 KiB

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:

python -m pytest central/tests/test_blocker_resolution_panel.py -q

Expected RED result:

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:

python -m pytest central/tests/test_blocker_resolution_panel.py -q

Result:

5 passed, 1 warning

Central suite:

python -m pytest central/tests -q

Result:

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:

python -m pytest central/tests/test_blocker_resolution_panel.py -q

RED result:

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:

python -m pytest central/tests/test_blocker_resolution_panel.py -q

Result:

9 passed, 1 warning

Full central command:

python -m pytest central/tests -q

Result:

265 passed, 1 warning

The existing pytest_asyncio configuration and openpyxl print-area warnings remain unchanged.

UTF-8 Byte-Length Re-Review Fix

RED

Added test_resolution_input_rejects_multibyte_utf8_at_ascii_character_limits before changing production code. It submits é repeated 512 times as value and 2000 times as reason; both are within the existing character counts but exceed the corresponding UTF-8 byte budgets.

Focused command:

python -m pytest central/tests/test_blocker_resolution_panel.py -q

RED result:

1 failed, 9 passed

The failure confirmed character-count validation accepted the multibyte inputs.

GREEN

  • value and reason limits now use len(value.encode("utf-8")) and len(reason.encode("utf-8")).
  • Existing ASCII boundary inputs remain accepted.
  • Multibyte inputs fail closed with explicit UTF-8 byte-limit validation errors, before persistence or audit sanitization.
  • Redaction and remote-write boundaries were not changed.

Focused result:

10 passed, 1 warning

Full central result:

266 passed, 1 warning

The existing pytest_asyncio configuration and openpyxl print-area warnings remain unchanged.