# C-CARE v1.0 — Release candidate gate audit

| | |
| --- | --- |
| Baseline | `4cc36d4` — test: complete final operational UAT hardening (`master`) |
| Date | 2026-10-05 |
| Auditor | Independent release-gate audit (evidence re-derived; prior claims not trusted) |
| Result | **BLOCKED FOR RELEASE CANDIDATE REVIEW** |

No production code, test, migration, setting or release identifier was changed by
this audit. Nothing was staged, committed, pushed, tagged or deployed. This document
is the only repository change and is uncommitted for review.

---

## 1. Findings summary

| ID | Severity | Release impact | Finding |
| --- | --- | --- | --- |
| RC-01 | **BLOCKER** | Gate fails: full audit not green | `audit_project --full`: 2,552 tests, **4 failures**, 0 errors. Deterministic. Introduced by `a5e25da` (Phase 6A). Frozen RC1 navigation/shell regression guards were made stale by the Phase 6A navigation restructure and never updated |
| RC-02 | **MEDIUM** | Fails gate item 9 (error rendering depends on authenticated context); needs a production code change | A request with an unknown/raw-IP `Host` header returns **500** instead of **400**; the secondary exception is not logged |
| RC-03 | LOW | Must be resolved before tagging | Release identity is still `1.0.0-rc1` although Phases 6A–6F landed after RC1 |
| RC-04 | LOW (cosmetic) | Not blocking | 6F-03: organization `__str__` renders a literal `?` between code and name |
| RC-05 | LOW (process) | Contributing cause of RC-01 | Phase 6 targeted regression manifests omitted the RC1 guard suites |
| RC-06 | LOW (wording) | Not blocking | `500.html` states "Nothing was changed by this attempt"; `ATOMIC_REQUESTS` is not enabled, so this is not guaranteed |
| RC-07 | INFO | Not blocking | Pre-existing databases `audit_273c…`, `audit_f872…` and `test_ccare_dev` need operator review (RC1 recorded only one `audit_*`). None was touched |
| RC-08 | INFO | Local hygiene | During this audit a shell parsing error echoed the local **development** `DJANGO_SECRET_KEY` from the untracked `.env` into the audit session output. It is not tracked, not a production secret and not reproduced here. Rotate the local development key |

### RC-01 — full audit failures (BLOCKER)

| Test | Assertion |
| --- | --- |
| `tests.test_shell_capabilities.NavigationVisibilityTests.test_permitted_navigation_is_visible` | `('Service operations', 'Service cases')` not found in `{('Operations', 'Service cases')}` |
| `tests.test_shell_capabilities.NavigationVisibilityTests.test_company_scope_does_not_unlock_center_only_navigation` | `('Customers & devices', 'Customers')` not found in `{('Operations', 'Customers')}` |
| `tests.test_shell_capabilities.ShellQueryProfileTests.test_shell_resolves_each_capability_at_most_once_per_request` | `[] is not true : the shell resolved no capability at all` |
| `tests.test_reporting_round_trips.ConfigurationVisibility.test_sla_policy_manager_sees_sla_policies_and_setup_in_the_shell` | `'SLA policies' not found in []` (no `Configuration` group) |

**Determinism.** Re-running only these four tests in an isolated test database
reproduced the identical four assertions.

**Attribution.** The same four tests were run against `git archive` exports of each
commit (no checkout of the working tree):

| Commit | Result |
| --- | --- |
| `e2dc076` release: C-CARE v1.0.0 RC1 | 4 OK |
| `a5e25da` feat: enhance operational workspace UI | **4 FAIL** |
| `391b039`, `4457f0f`, `e6d4d8a`, `49eb2e2`, `4cc36d4` | 4 FAIL |

`a5e25da` renamed and regrouped the navigation shell (`Service operations` and
`Customers & devices` → `Operations`; `Communications`/`Reports` → `Monitoring`;
configuration links and `Setup` → `System`). It also moved the shell to a single bulk
`capability_map` call.

**Behavioral probe** (synthetic fixture, isolated test DB, scratch module outside the
repository):

| Persona | Current navigation |
| --- | --- |
| Center-scoped case reader | `Operations: [Service cases]` |
| Company-scoped customers-only | `Operations: [Customers]`. Still **no** Service cases or Walk-in queue |
| SLA policy manager | `System: [SLA policies, Administration / Settings (Configuration center)]`; both destinations HTTP 200 |
| Dashboard resolver calls | `capable()`: 0, `capability_map()`: 1 |

**Classification.** This is a stale regression-guard failure, not an observed
authorization or disclosure regression. Permitted navigation remains visible,
center-only navigation is still not unlocked by company scope, and capability
resolution is a single bulk call. However:

- the committed tree's full regression suite is red, which fails the gate's
  explicit PASS rule;
- some negative assertions now test labels that no longer exist and pass
  vacuously, so those guards are currently weaker than at RC1;
- the per-request memoization guard watches `capable()`, which the shell no longer
  calls.

**Required before RC approval:** an explicitly approved re-baseline of these RC1
guards to the Phase 6A navigation contract. It must restore, not weaken, both the
positive and negative assertions under the current labels, and keep an equivalent
"resolved once per request" guard on `capability_map`. A single fresh
`audit_project --full` must then pass.

### RC-02 — Host-header error path returns 500 (MEDIUM)

| Request | Observed |
| --- | --- |
| HTTPS, `Host: 203.0.113.7` (not in `ALLOWED_HOSTS`) | **500** "Service error" page |
| HTTP, same Host (SSL-redirect path) | **500** |
| Logged | Only `django.security.DisallowedHost` at ERROR; the secondary `AttributeError` is **not** logged |
| Disclosure | None. Body is the static 500 page; no traceback, SQL, path or secret |

**Mechanism.** `DisallowedHost` is raised by `SecurityMiddleware` or
`CommonMiddleware` before `AuthenticationMiddleware` sets `request.user`. Django's
`bad_request` handler renders `400.html` **with the request**, so context processors
run. `apps/operations/context.py:73` (`shell`, introduced in `ad8c5f5`, present at
RC1) reads `request.user.is_authenticated`, raises `AttributeError`, and Django
escalates to `server_error`. The error-template comments ("Rendered … with an EMPTY
context: no request … no context processors") are inaccurate for 400/403/404.

**Why tests missed it.** `tests/test_production_surface.py` raises its 400/403
exceptions inside views, after the authentication middleware. No test covers an
exception raised before it.

**Impact.** Scanners and raw-IP requests that reach the application produce 5xx
responses, which pollutes 5xx alerting and hides the real cause. There is no data,
authorization or disclosure impact. A reverse proxy that rejects unknown Host
values (nginx `default_server` returning 444) mitigates it at the edge, but the
application gate criterion still fails.

**Release impact.** Fails gate item 9. The fix is a production code change, which
is out of scope for this audit. Remediate, or record an explicit operator acceptance
with the proxy mitigation, before RC approval.

### RC-03 — release identity

`config/settings/base.py`: `RELEASE_NAME = "C-CARE"`, `RELEASE_VERSION = "1.0.0-rc1"`.
`tests/test_production_surface.py` pins `"1.0.0-rc1"`. The identity is deterministic
and deliberately not exposed by `/health/` or `/ready/` (verified). It does not
appear in the UI. `PRODUCTION_DEPLOYMENT.md`, `OPERATIONS_RUNBOOK.md` and
`RELEASE_CHECKLIST.md` also name RC1 and baseline `1a952d0`. No git tag exists.
Before tagging, a small coordinated bump is needed: setting, pinned test and
runbook references.

---

## 2. Gate evidence

### Baseline and scope

- `master`, HEAD `4cc36d4`, working tree clean before the audit. The expected
  sequence `4cc36d4 … e2dc076` is present.
- 515 tracked files. 22 apps cover the declared scope: accounts, organization,
  access (RBAC/assignments), catalog, service_catalog, customers, devices
  (registry, identifiers, ownership, warranty), service (intake, assignment,
  diagnosis, repair, QC, handover/closure), parts, inventory, commercial
  (quotation, invoice, payment, reversal, due release, clearance), reporting,
  frontdesk, communications, sla, operations (dashboard/search/workspaces) and
  configuration.
- Since RC1, no test file was modified or deleted. Six test modules were added.

### Runtime and dependencies

| Item | Value |
| --- | --- |
| Python | 3.14.4 (documented support 3.10–3.14) |
| Django | 5.2.17 (pinned) |
| psycopg | 3.3.6 `[binary]` (pinned) |
| python-dotenv | 1.2.3 (pinned) |
| Transitive | asgiref 3.12.1, sqlparse 0.6.0, tzdata 2026.4 (unpinned; known deferral 14.7) |
| PostgreSQL | 18.6 |
| `pip check` | No broken requirements |
| Production requirements | `production.txt` = `-r base.txt` only; no test or browser tooling installed or required. The Phase 6F headless Edge renders are not a project dependency |

### Django core and migrations

| Check | Result |
| --- | --- |
| `manage.py check` | No issues |
| `makemigrations --check` | No changes detected |
| `showmigrations` | 59 applied, 0 unapplied |
| `MigrationLoader.detect_conflicts()` | `{}` |
| `check_consistent_history` | OK |
| Disk vs applied | 59 / 59; none missing or orphaned |
| Migration files changed since RC1 | 0 |
| Historical rewrites | 0. Two `--follow` hits are git copy-detection artifacts; each file has exactly one commit |

### Deployment check (`config.settings.production`, generated throwaway secret)

| Warning | Classification |
| --- | --- |
| `security.W005` HSTS include-subdomains off | Expected / configuration-dependent (`DJANGO_HSTS_INCLUDE_SUBDOMAINS` opt-in) |
| `security.W021` HSTS preload off | Expected / configuration-dependent (`DJANGO_HSTS_PRELOAD` opt-in) |

With the opt-ins and proxy header set: **0 issues**. Verified settings: `DEBUG=False`;
`SECURE_SSL_REDIRECT`, `SESSION_COOKIE_SECURE/HTTPONLY` and `CSRF_COOKIE_SECURE` all
on; HSTS 31,536,000 s; `X_FRAME_OPTIONS=DENY`; nosniff; referrer policy validated;
proxy SSL header opt-in enum. These fail closed at import: a weak or
`django-insecure-` secret, a wildcard `ALLOWED_HOSTS`, and an invalid proxy header
(each verified).

### Secret audit

| Check | Result |
| --- | --- |
| Tracked `.env`, keys, certificates, dumps | Only `.env.example` (placeholders: `replace-me`, blanks); `.env` is git-ignored and never committed |
| High-confidence token patterns (AWS, private key, GitHub, Slack, OpenAI, Google, SendGrid) | 0 hits, all tracked files |
| Literal credential assignments | 3, all synthetic test sentinels (`SECRET-SYNTHETIC`, `sensitive-test-sentinel`, `replace-me`) on `.invalid` domains |
| Verdict | **Safe** |

### Static files

`collectstatic --noinput --clear` under production settings into a temporary
`STATIC_ROOT` collected **130 files** (RC1: 129; +1 is the Phase 6
`operations/receipt.css`). `operations/workspace.css`, `operations/workspace.js` and
`operations/receipt.css` are present. Every project `{% static %}` reference
resolves, and there are no hard-coded `/static/` paths. The temporary output was
removed; no `staticfiles/` exists in the repository.

### Error pages

Under production settings: 404 via the full stack, plus direct 400/403/404/500
handler rendering with exception sentinels. The CSRF 403 uses the Django default.
**No traceback, SQL, path, database name, secret or version is disclosed.**
Standalone rendering **fails** for exceptions raised before authentication
middleware (see RC-02).

### Health and readiness

| Probe | Result |
| --- | --- |
| `GET /health/` | 200 `{"status": "ok"}`, `no-cache, no-store`, 0 SQL |
| `GET /ready/` | 200 `{"status": "ready"}`; SQL is exactly `SAVEPOINT`, `SET LOCAL statement_timeout = 2000`, `SELECT 1`, `RELEASE SAVEPOINT` |
| `POST` either | 405 |
| `HEAD` either | 200 |
| HTTP | 301 → HTTPS |
| Database unreachable (`DB_PORT=1`) | `/ready/` 503 `{"status": "not_ready"}`; `/health/` 200 |
| Disclosure | None; no writes |

### Integrity and reconciliation

`ccare_dev` contains **0 business rows** (only one superuser login). The read-only
`audit_inventory` therefore has no authorized locations, and
`check_installation_readiness` correctly reports an unconfigured installation
(exit 1, expected). `monitor_service_sla` records escalations, so it was not run
against `ccare_dev`. Domain integrity and reconciliation evidence therefore comes
from the full suite. Organization, service, inventory ledger/projection, commercial
and SLA reconciliation tests (Phase 3A–3D audits, UAT journeys) **all passed**.
None of the 4 failures is an integrity test.

### Backup / restore drill

| Step | Result |
| --- | --- |
| Method | `pg_dump -Fc --no-owner --no-privileges` → `pg_restore --no-owner --no-privileges` |
| Drill A | `ccare_dev` → `ccare_rcdrill_restore_20261005110514`: 114/114 tables, 59/59 migrations, identical per-table row counts (602 rows); `check` clean, no drift, 0 unapplied, `/ready/` 200 against the restored DB |
| Drill B (data-bearing) | Scratch DB migrated and seeded with `seed_demo_data --confirm-development` → dump → `ccare_rcdrill_seeddst_20261005110559`: identical per-table counts (659 rows; organization, customer, device, identifiers, service case, warranty snapshot, SLA); `check` clean, no drift |
| Cleanup | Only the three `ccare_rcdrill_*` databases and the two dumps created by this run were removed. The database set equals the pre-drill snapshot; `ccare_dev` was not modified (59 migrations) |

### Communications

- **Email.** Fully environment-driven. Requires `COMMUNICATIONS_PRODUCTION`,
  `COMMUNICATIONS_ALLOW_EXTERNAL=True`, the SMTP backend, a valid sender, host and
  port, `0 < EMAIL_TIMEOUT ≤ 60` (configured 10 s), TLS XOR SSL, and paired
  credentials. Otherwise it raises `ImproperlyConfigured`. The in-memory backend is
  refused in production.
- **SMS.** SMS provider integration infrastructure is ready; production SMS remains
  disabled pending provider configuration. Only the abstract `SmsProvider` and
  `FakeSmsProvider` (`production_ready=False`, refused in production) exist.
- No email or SMS was sent.

### Security, financial, inventory and reporting freezes

All of the following passed in the full run.

- **Security.** About 290 named tests cover CSRF, stale/replay/idempotency,
  cross-company/center/department/location scope, identifier and commercial
  disclosure, QC independence (`test_qc_independence_and_current_state_filters`),
  administrative access, and direct URL/POST denial.
- **Financial.** `test_unpaid_customer_blocks_delivery`,
  `test_partial_and_multiple_methods_exact_settlement`,
  `test_release_is_not_settlement_and_cannot_be_replayed`,
  `test_due_release_retains_outstanding_and_history`,
  `test_clearance_matrix_and_due_release_do_not_rewrite_debt`,
  `test_reversal_restores_due_preserves_receipt_and_allocation`,
  `test_overview_metrics_are_record_counts_not_revenue`,
  `test_payment_reversal_restores_the_outstanding_balance`.
- **Inventory.** About 220 receiving/reservation/custody/consumption/return/
  recovery/transfer/count/adjustment/ledger tests, including
  `test_unused_return_does_not_reverse_consumption`.
- **Reporting.** `test_null_cause_explicit_label_without_synthetic_record`,
  `test_unknown_cause_is_reporting_label_without_master`, co-occurrence
  deduplication and labelling, `test_due_release_delivery_retains_debt`,
  `test_unused_return_does_not_reduce_consumption`,
  `test_intake_complaint_uses_device_category_not_applicability`,
  `test_current_queue_ignores_event_period`.

The failing RC1 guards (RC-01) belong to the authorization/navigation surface. No
authorization or disclosure regression was observed, but the guards themselves
must be restored.

### Query budgets

- The frozen Phase 3D budget tests (`apps/reporting/tests/test_reports.py`,
  `tests/test_phase3d_audit.py`) have one commit each (`9043846`): never
  re-baselined. Both passed.
- Each Phase 6A–6F budget-bearing module (`test_ui`, `test_workspaces`,
  `test_inventory_workspace`, `test_commercial_workspace`,
  `configuration.test_workspace`, `uat.test_final`) has exactly one commit, so no
  budget was raised. All of them passed.
- Phase 5D bulk-authorization budgets (`test_authorization_bulk`,
  `test_reporting_round_trips` budget tests) passed.
- **Exception:** `test_shell_resolves_each_capability_at_most_once_per_request` now
  monitors an unused path (RC-01). It is a stale guard; the query budget itself is
  not exceeded.

### Full audit (single run)

| Item | Value |
| --- | --- |
| Command | `python manage.py audit_project --full` (run once, uninterrupted) |
| Prechecks | PASS: Django system check, migration drift, unapplied migrations, git whitespace, `.env` not tracked |
| Discovered | **2,552** tests |
| Passed / failed / errors / skips | **2,548 / 4 / 0 / 0** |
| Test runtime | 5,499.768 s |
| Audit elapsed | 5,543.278 s (wall clock 5,547 s; 11:07:26 – 12:39:53) |
| Exit | 1, `CommandError: Audit failed: Full regression` |

### Post-audit state

`git status --porcelain` and `git diff --check` were both clean after the audit.
The full audit destroyed its own test database. Temporary static output, export
directories, probe modules, dumps and drill databases created by this audit were
removed. The database set equals the pre-audit snapshot.

### Operational UAT evidence (`docs/FINAL_OPERATIONAL_UAT.md`)

The document records: nine personas and the security matrix; all 22 frozen UAT
journeys plus the new operational-form journey; 197 screens × 3 widths = 591
rendered checks; an accessibility smoke scan; 6F-01 HIGH and 6F-02 MEDIUM resolved;
6F-03 LOW deferred; and known limitations. Its 563-test targeted manifest did not
include `tests.test_shell_capabilities`, `tests.test_reporting_round_trips` or
`tests.test_authorization_bulk` (RC-05). 6F-03 causes no identification ambiguity,
since both code and name remain readable.

---

## 3. Production environment checklist

| Item | Status |
| --- | --- |
| Python 3.10–3.14 runtime | VERIFIED LOCALLY (3.14.4); REQUIRES TARGET SERVER |
| PostgreSQL ≥ 14 | VERIFIED LOCALLY (18.6); REQUIRES TARGET SERVER |
| Dependencies from `requirements/production.txt`, `pip check` | VERIFIED LOCALLY; REQUIRES TARGET SERVER |
| Environment variables via service manager | REQUIRES TARGET SERVER |
| `DJANGO_SECRET_KEY` (generated, ≥ 50 chars) | MANUAL SECRET/PROVIDER STEP (guard verified locally) |
| `DEBUG=False` | VERIFIED LOCALLY (unconditional in production settings) |
| `DJANGO_ALLOWED_HOSTS` | MANUAL (wildcard/empty refusal verified locally) |
| `DJANGO_CSRF_TRUSTED_ORIGINS` if cross-origin | REQUIRES TARGET SERVER |
| HTTPS / TLS certificate | REQUIRES TARGET SERVER |
| Secure session/CSRF cookies | VERIFIED LOCALLY |
| HSTS subdomain/preload decision | MANUAL (W005/W021 until decided) |
| `DJANGO_TRUSTED_PROXY_HEADER` + proxy strips client headers | REQUIRES TARGET SERVER |
| Proxy rejects unknown Host values (mitigates RC-02) | REQUIRES TARGET SERVER |
| DB credentials, non-superuser app role, separate backup role | MANUAL SECRET/PROVIDER STEP |
| `migrate --noinput` after a fresh backup | REQUIRES TARGET SERVER (local: 59/59 applied) |
| `collectstatic` | VERIFIED LOCALLY (temp root); REQUIRES TARGET SERVER |
| Writable `staticfiles/`, `media/`; `umask 027` | REQUIRES TARGET SERVER |
| Logging to stdout + host rotation | VERIFIED LOCALLY (config); REQUIRES TARGET SERVER (rotation) |
| Off-host, encrypted backup destination | MANUAL |
| Restore procedure | VERIFIED LOCALLY (drills A and B); REQUIRES TARGET SERVER |
| SMTP | MANUAL SECRET/PROVIDER STEP (validation verified locally) |
| SMS | Disabled pending provider configuration (DEFERRED) |
| `/health/` and `/ready/` monitoring | VERIFIED LOCALLY; REQUIRES TARGET SERVER |
| WSGI server + reverse proxy | REQUIRES TARGET SERVER |
| Service start/restart (systemd) | REQUIRES TARGET SERVER |
| Rollback procedure | Documented; REQUIRES TARGET SERVER |
| `check_installation_readiness` on target | REQUIRES TARGET SERVER |
| Full regression green on the release commit | **BLOCKER (RC-01)** |
| Host-header error path | **BLOCKER-pending decision (RC-02)** |
| Release identity bumped before tag | MANUAL (RC-03) |

---

## 4. Release blocker matrix

| # | Question | Answer |
| --- | --- | --- |
| 1 | Git baseline clean? | Yes |
| 2 | Migration drift? | No |
| 3 | Migration conflicts? | No |
| 4 | Django check passed? | Yes |
| 5 | Deployment-check blockers? | No (W005/W021 configuration-dependent) |
| 6 | Tracked secret detected? | No |
| 7 | Static collection passed? | Yes |
| 8 | Error pages safe? | **No disclosure, but not standalone: bad-Host 400 → 500 (RC-02)** |
| 9 | Health/readiness passed? | Yes |
| 10 | Integrity/reconciliation passed? | Yes (suite evidence; dev DB has no business data) |
| 11 | Backup/restore drill passed? | Yes |
| 12 | Security smoke coverage passed? | Yes (quick smoke and security suites green) |
| 13 | Full audit passed? | **No** |
| 14 | Test failures? | **4** |
| 15 | Test errors? | 0 |
| 16 | Frozen query-budget regression? | No budget exceeded; one RC1 memo guard is stale (RC-01) |
| 17 | Financial-integrity regression? | No |
| 18 | Inventory-integrity regression? | No |
| 19 | Authorization/disclosure regression? | None observed; RC1 navigation guards are stale and partly vacuous (RC-01) |
| 20 | Unresolved BLOCKER/HIGH finding? | **Yes: RC-01 (BLOCKER)**; RC-02 MEDIUM needs a decision |

## 5. Recommendation

**BLOCKED FOR RELEASE CANDIDATE REVIEW.**

The frozen domain semantics are healthy: 2,548 of 2,552 tests pass, including every
financial, inventory, reporting, integrity, query-budget and security test outside
the four stale navigation guards. The gate nonetheless cannot pass while the
committed full suite is red. To unblock:

1. Approve and apply an equivalent re-baseline of the four RC1 guards to the
   Phase 6A navigation contract (RC-01), keeping negative assertions meaningful.
2. Fix RC-02 (make the shell context processor tolerate a request without `user`,
   and add a pre-authentication-middleware error-path test), or record an explicit
   operator acceptance with the edge mitigation.
3. Bump the release identity and runbook references (RC-03).
4. Run exactly one fresh `audit_project --full` on the resulting commit.


---

## 6. RC remediation (separate from the independent audit)

The original audit above is preserved verbatim: **4cc36d4 = BLOCKED**.
This section records subsequent, uncommitted remediation, not a replacement
full-audit result. The current candidate identity is `1.0.0-rc2`; a fresh full
release audit is still required after review. No full audit was run here.

### RC-01: restore the four navigation guards

No navigation, capability resolver, permission or endpoint implementation changed.
The guards now follow the existing Phase 6A bulk-resolution architecture.

| Guard | Old guard / protected invariant | Current architecture and new guard | Negative and direct-access evidence |
| --- | --- | --- | --- |
| `test_permitted_navigation_is_visible` | Center-scoped case reader sees Service cases under Service operations | Real `capability_map` called once; Operations / Service cases resolves to the case listing; authorized reader can list and open a real case | A device-only actor has no case link, gets an empty scoped list, cannot GET that case (404), and cannot POST to the read-only detail (405) |
| `test_company_scope_does_not_unlock_center_only_navigation` | Customer permission at company scope shows Customers but does not imply case/queue permissions | Real bulk map called once; Operations / Customers resolves to the customer listing | Current Service cases and Walk-in queue labels are absent; direct case list is empty, center desk GET denied (404), read-only desk POST denied (405). Company scope is not inherently forbidden from queues; the missing permissions are what matters |
| `test_shell_resolves_each_capability_at_most_once_per_request` | Each capability was checked at most once through `capable` | Spy on the actual `capability_map`: exactly one call, correct actor, all distinct declared permissions, both requested scope modes, repeat shell calls reuse the same request result | Allowed case capability/link and denied invoice capability/link asserted; direct endpoint checks live in the first guard and operational security suites. No obsolete resolver spy or query-budget relaxation |
| `test_sla_policy_manager_sees_sla_policies_and_setup_in_the_shell` | Company SLA manager sees policies and configuration entry under Configuration / Setup | Real bulk map called once; System contains exact policy and Administration / Settings URLs, both accessible | Same permission at center scope and an ungranted actor expose neither URL. A real policy is visible/editable to its manager; denied actors see an empty policy list, cannot GET/POST its edit URL (404), and cannot open configuration (403). Anonymous workspace remains absent |

All four exercise `capability_map` and include meaningful current negative
assertions. The adjacent denied-inventory guard also uses the current Parts &
inventory group, including Inventory overview. Old-label absence is not used as
security evidence. Listing routes that intentionally return empty scoped 200
responses retain that contract; authorization is not changed to force 403s.

### RC-02: pre-authentication error rendering

Before correction, disallowed raw-IP Host requests over HTTP and HTTPS returned
500. The captured exception was `AttributeError: WSGIRequest has no attribute
user` in `apps.operations.context.shell`: SecurityMiddleware (HTTP redirect) or
CommonMiddleware (HTTPS) rejected Host before AuthenticationMiddleware. Rendering
400.html then ran the shell context processor and raised the secondary exception.

The shell now reads the optional user with `getattr` and returns no workspace
when absent. It does not fabricate a user, weaken ALLOWED_HOSTS, change middleware,
catch arbitrary exceptions or alter authenticated capability resolution. Comments
in 400/403/404 templates now accurately describe request/context-processor use;
their rendered content is unchanged.

Focused tests cover unknown and disallowed IP hosts over both HTTP and HTTPS,
controlled 400 responses, no secondary exception or sensitive response content,
allowed-host HTTP redirect and HTTPS success, absent-user zero capability calls,
and ordinary authenticated workspace rendering. Existing unexpected-500 tests
continue to assert the `django.request` ERROR logging path. Logging configuration
is unchanged; no additional noisy error handler was introduced.

### RC-03 and RC-06

The authoritative setting and pinned release test now identify `1.0.0-rc2`.
Current deployment and operations runbooks match RC2. Historical RC1 release
checklist, phase documentation and the original audit text remain unchanged.
The deployment runbook explicitly distinguishes the pending RC2 gate from RC1.
No tag was created.

The standalone 500 page now says the request could not be completed and asks the
user to check its status before retrying, or report persistent failures. It no
longer promises that nothing changed or that every error was recorded. The
existing genuine-unexpected-error logging test remains in force.

### Boundaries and remaining work

RC-04 remains unchanged. RC-05 is addressed by including the formerly omitted
shell, reporting round-trip and bulk authorization suites in focused verification;
these guards must remain part of future navigation regression selections.
No pre-existing audit/test database was modified or removed (RC-07). Only fresh,
uniquely named test databases are used and disposed of by the Django runner.
No business database writes, migrations, secret rotation or secret inspection
were performed. RC-08 remains a manual user action. No staging, commit, push,
tag, deployment or full audit is part of this remediation.


### Focused verification results

| Run | Result | Runtime |
| --- | --- | --- |
| Final complete `tests.test_shell_capabilities`, `tests.test_reporting_round_trips`, `tests.test_production_surface` | 82 passed, zero failures/errors | 329.479 s |
| Shell/configuration guards, bulk authorization, authorization audit, operational UI/workspaces and selected QC restrictions | 122 passed, zero failures/errors | 634.578 s |
| Direct four-guard run on final assertions | 4 passed | 10.756 s |
| Temporary deny-all capability-map probe | All four guards rejected the mutation with assertion failures, zero errors | 3.528 s |
| Temporary allow-all capability-map probe | All four guards rejected the mutation with assertion failures, zero errors | 17.656 s |

These selections overlap; they are not a unique aggregate test count. The 122-test
selection was: `tests.test_shell_capabilities`,
`tests.test_reporting_round_trips.ConfigurationVisibility`,
`tests.test_authorization_bulk`, `apps.access.test_authorization_audit`,
`apps.operations.test_ui`, `apps.operations.tests`,
`apps.operations.test_workspaces.JobWorkspaceTests`, and the two
`TechnicalWorkspaceTests` named `test_qc_independence_and_current_state_filters`
and `test_qc_failure_history_does_not_bypass_inspector_scope`.

An initial 82-test development run took 518.970 seconds and had one failure in
new test coverage: it incorrectly expected the denied SLA list to return 403.
The test was corrected to the established scoped-empty-200 contract, with a real
policy and explicit edit GET/POST denials; no production endpoint was changed.
The final complete 82-test run above is green. An initial mutation probe exposed
a missing-group KeyError; explicit group/link presence assertions now produce
meaningful assertion failures. All temporary patches were process-local and
restored. No probe files or code changes remain in the repository.

Django `check` passed with zero issues; `makemigrations --check` reported no
changes; `git diff --check` and the untracked audit document whitespace scan
passed. Existing error-template rendering checks passed; JavaScript was not
changed. Query ceilings were unchanged, including the operational UI and reporting
round-trip checks. Security coverage retained company/center/department isolation,
identifier and commercial disclosure restrictions, QC independence, configuration
access boundaries, CSRF and direct GET/POST restrictions.

No full-audit result is claimed. This working tree is ready for remediation
review, followed by the separately authorized release-candidate gate.


## 7. RC2 remediation freeze review

Baseline remains master / 4cc36d4. The complete remediation diff was reviewed.
One documentation omission was corrected under RC-03: README still called RC1
the current candidate. It now names RC2, links to this pending candidate gate,
and retains the historical RC1 checklist link. No production or test correction
was needed during this review.

The repository-wide case-insensitive RC1 search classifies the remaining matches
as historical release-checklist and operational-UI records, original independent
audit evidence, explicit references distinguishing RC1 from RC2, and a synthetic
production-settings test sentinel whose name is not a release identifier.
These references are retained. Current identity, README and runbooks match RC2.

SLA correction classification: A, an incorrect NEW remediation assertion.
In ConfigurationVisibility.test_sla_policy_manager_sees_sla_policies_and_setup_in_the_shell,
the initial remediation added an expectation that denied GETs to both policy
list and configuration returned 403. HEAD's original test contains no such
assertion: it checks navigation and anonymous workspace only. The existing,
unchanged policies view is login-protected and filters SlaPolicy through
queries.companies, which uses authorized_queryset for sla.manage_slapolicy on
Company. An authenticated actor with no visible policies therefore receives
200 with an empty queryset. The corrected new assertions verify that empty
result and no policy-name disclosure; configuration still returns 403, and the
real policy edit GET/POST returns 404. No frozen denial or SLA behavior changed.

All four guards protect current links and permission requirements, exercise the
bulk resolver, and preserve direct-access checks. The capability guard checks
exactly one call, complete distinct permissions and repeated use of the same
request. The prior mutation log confirms four assertion failures for deny-all
and four for allow-all, zero errors, and restored process-local patches.
No mutation, probe or debug implementation remains in the repository.

The shell fix only handles absent authentication context. All four error pages
remain standalone, with accurate 400/403/404/500 wording and no dependency on
navigation. No exception, SQL, path, secret or version detail is rendered.
The 500 page promises no rollback or atomicity. Host restrictions remain intact.

Fresh compact review: 19 tests passed in 6.632 seconds, covering the four guards,
ErrorPageTests and ReleaseVersionTests, including SLA scoped-empty and record
denial, HTTP/HTTPS bad hosts, normal authenticated rendering and 500 logging.
Django check passed; makemigrations --check found no changes; whitespace checks
passed. The prior 82/122-test selections were reviewed, not repeated. No full
audit was run. Only this run's fresh test database was created and destroyed by
the runner; pre-existing databases and business data were not modified.

Freeze answers: meaningful guards YES; frozen security tests weakened NO;
SLA correction legitimate YES; invalid Host safely 400 YES; ALLOWED_HOSTS enforced
YES; standalone-safe error pages YES; consistent RC2 identity YES; historical RC1
preserved YES; authorization behavior changed NO; domain behavior changed NO;
query budget increased NO; migration drift NO; unexplained diff NO; blocker
before commit NO. Security and query boundaries remain unchanged.

RC-01/02/03/06 are resolved pending the fresh full audit. RC-04 is deferred,
RC-05 is documented, RC-07 databases remain untouched, and RC-08 rotation stays
manual. The original independent 4cc36d4 audit remains BLOCKED (2,552 discovered,
2,548 passed, four failed, zero errors/skips). Nothing was staged, committed,
pushed, tagged or deployed. Proposed subject: fix: remediate RC2 release blockers.

---

## 8. Post-remediation RC2 full audit

This section records the decisive full audit of the remediated candidate. Every
section above remains unchanged history. The original independent audit of
`4cc36d4` is still **BLOCKED** (2,552 discovered, 2,548 passed, four failures,
zero errors, zero skips), and the RC1 evidence stays historical.

| | |
| --- | --- |
| Release candidate | C-CARE v1.0.0-rc2 |
| Audited application baseline | `5ec64bf` — fix: remediate RC2 release blockers |
| Command | `python manage.py audit_project --full` (run manually by the user, once) |

### Prechecks

| Check | Result |
| --- | --- |
| Django system check | PASS |
| Migration drift | PASS |
| Unapplied migrations | PASS |
| Git whitespace | PASS |
| `.env` not tracked | PASS |

### Full regression

| Item | Value |
| --- | --- |
| Found | 2,556 tests |
| Ran | 2,556 tests |
| Failures | 0 |
| Errors | 0 |
| Result | OK |
| Test runtime | 4,922.755 s |
| Total audit runtime | 4,964.749 s |
| Final audit output | `PASS: Full regression`; `AUDIT CHECKS PASSED` |

After the audit, the user ran `git status --short`, `git diff --check` and
`git status --short` again. All three returned no output. The working tree stayed
clean, no whitespace problem was detected, and the audit left no repository
modification.

### Query and performance result

Every frozen query-budget and regression test passed as part of the complete
2,556-test suite. No budget was changed. Observed values:

| Surface | Observed |
| --- | --- |
| Reporting `cases` | 20 / budget 24 |
| Reporting `engineer_queue` | 20 / budget 24 |
| Reporting `complaints_complaint` | 9 / budget 10 |
| Reporting `diagnoses` | 9 / budget 10 |
| Reporting `positions` | 8 / budget 8 |
| Reporting `invoices` | 9 / budget 10 |
| Reporting `outstanding` | 9 / budget 10 |
| Phase 6A all-capabilities dashboard | 22 |
| Phase 6A scoped dashboard | 9 |
| Phase 6A navigation | 4 |
| Phase 6A search | 8 |
| Phase 6A job overview | 31, constant with assignment-history growth |
| Phase 6F.1 diagnosis, empty / populated remarks | 11 / 11 |
| Phase 6F.1 history, empty / populated remarks | 16 / 16 |

### Finding status after RC2

| ID | Severity | Status | Basis |
| --- | --- | --- | --- |
| RC-01 | BLOCKER | **RESOLVED** | The stale RC1 navigation and capability guards were re-baselined to meaningful current authorization behavior. The corrected guards passed in the full regression |
| RC-02 | MEDIUM | **RESOLVED** | Invalid, unknown and raw-IP `Host` rendering was corrected, so the standalone 400 path no longer depends on `request.user` being set. `ALLOWED_HOSTS` remains enforced |
| RC-03 | LOW | **RESOLVED** | The current release identity is `1.0.0-rc2`. Historical RC1 evidence remains historical |
| RC-04 | LOW | DEFERRED | Cosmetic organization formatting `CODE ? Name`. Not a release blocker |
| RC-05 | LOW | DOCUMENTED (process) | Earlier Phase 6 targeted manifests omitted the RC1 guard suites. The finding is retained |
| RC-06 | LOW | **RESOLVED** | The unsupported rollback / "nothing changed" guarantee was removed from the standalone 500 page |
| RC-07 | INFO | UNCHANGED | Pre-existing audit and test databases were observed earlier. They were not touched |
| RC-08 | MANUAL | OPEN (user action) | The local-development `DJANGO_SECRET_KEY` was exposed in an earlier agent session's output. The user must rotate the local development key manually. The secret was not read, printed, modified or regenerated while recording this section |

### Release decision

| Gate | Result |
| --- | --- |
| Application release gate | **PASSED** |
| Automated full regression | **PASSED** |
| RC2 full audit | **PASSED** |
| Unresolved BLOCKER | None |
| Unresolved HIGH | None |

C-CARE v1.0.0-rc2 is approved at the application-level release gate and is ready
to proceed to target-server production-readiness verification.

This is not a production deployment or production verification. The target-server,
manual-secret and provider items in section 3 remain pending, and nothing has been
pushed, tagged or deployed.
