# SATE Audit Report v0

**Project:** `/home/amari/projects/akaunting`
**Generated:** 2026-05-23T16:39:18.770Z
**Schema:** 1 (SATE_AUDIT_REPORT_V0)

## Laravel Version Resolution
- **Resolved:** 10.50.2
- **Confidence:** MEDIUM
- **Authority Source:** LOCKED
- **SATE Mode:** GENERATION_SUPPORTED

Per-source values:
  - declared: 10.0.0
  - locked: 10.50.2
  - installed: (none)
  - runtime: (none)

**Drift findings:**
  - `LARAVEL_VENDOR_NOT_INSTALLED`: No vendor/laravel/framework installation found. Run `composer install` for higher-confidence detection.
  - `LARAVEL_VERSION_DRIFT_DECLARED`: composer.json declares 10.0.0 but locked reports 10.50.2 (declared constraint is looser than installed)

## Surface Enumeration
**Application surfaces** (what SATE audits):
- Total: 1131
- Proof-ready: 857
- Blocked: 274

**Test corpus** (what MANUAL stage gates — different counter):
- Test files on disk: 34
- MANUAL tests discovered: 0
- Files scanned during MANUAL discovery: 0

> APPLICATION SURFACES and TEST CORPUS are different counters — per MANUAL_STAGE_CONTRACT v1 they must not be folded. App surfaces are what SATE audits; test corpus is what MANUAL gates.

```
MANUAL_INPUT_SCOPE:
  source: discoverPhpTestFiles
  files_total: 0
  files_considered: 0
  tests_discovered: 0 (extracted test blocks; many files contain no tests)
```

## MANUAL Stage Verdict (v1 — SAFETY + QUALITY independent)
> SAFETY and QUALITY are INDEPENDENT gates per MANUAL_STAGE_CONTRACT v1. Never folded.

| Gate | Spec | Pass files |
|---|---|---:|
| SAFETY | v1 (refiner/validator.js) | 8 |
| QUALITY | v1 (v1, enabled=true) | 31 |
| KB_ELIGIBLE | binary v1 (YES if QUALITY passes; LIMITED reserved for v2) | 8 |
| RUNNER allowlist | file-level | 1 |

## Tier Classification (God Mode)
| Tier | Count |
|---|---:|
| A | 177 |
| B | 1 |
| C | 679 |
| FRAGILE | 0 |
| QUARANTINED | 0 |
| UNCLASSIFIED | 0 |

## Refusal Ledger
> Each refusal cites the specific signal that triggered it. No mystery refusals.

Total blocked surfaces: 274

| Reason | Count | Example surface |
|---|---:|---|
| `NO_EXPLICIT_RETURN` | 274 | `App\Abstracts\Http\ApiController@__construct` |

## Dead-Code Triage
Total triaged: 898

| Bucket | Count | Action | Example |
|---|---:|---|---|
| DELETE | 0 | Remove | — |
| WIRE | 0 | Suggest binding | — |
| PROMOTE | 0 | Suggest promotion path | — |
| DEFER | 898 | Flag, recheck next audit | `App\Abstracts\Http\ApiController@__construct` |

*Excluded 233 framework entry points from triage — see "Framework Entry Points" section below.*

## Framework Entry Points
Framework entry points are invoked by Laravel's runtime (schedulers, queue workers, HTTP kernel, event dispatcher, Filament/Livewire lifecycles, etc.). They have zero direct PHP callers by design — excluded from dead-code triage so the DEFER bucket reflects truly uncertain surfaces, not framework hooks.

Total excluded from dead-code triage: 233

| Kind | Count |
|---|---:|
| queued_job | 92 |
| form_request | 68 |
| event_listener | 58 |
| console_command | 15 |

## Duplication Map
Total clusters: 127; surfaces in clusters: 721

| Cluster | Canonical | Duplicates |
|---|---|---|
| cluster_001 | `App\Abstracts\Http\ApiController@__construct` | `App\Abstracts\Http\Controller@__construct`, `App\Console\Commands\BillReminder@handle`, `App\Console\Commands\CompanySeed@handle`, `App\Console\Commands\InstallRefresh@handle`, `App\Console\Commands\InvoiceReminder@handle`, `App\Console\Commands\SampleData@handle`, `App\Console\Commands\UserSeed@handle`, `App\Http\Controllers\Api\Document\DocumentTransactions@__construct`, `App\Http\Controllers\Api\Settings\Settings@__construct`, `App\Jobs\Auth\NotifyUser@handle`, `App\Jobs\Common\CreateMediableForExport@handle`, `App\Listeners\Update\V30\Version300@deleteOldFiles`, `App\Listeners\Update\V30\Version300@updateCompanies`, `App\Listeners\Update\V30\Version300@updatePermissions`, `App\Listeners\Update\V30\Version300@updateTransactions`, `App\Listeners\Update\V30\Version303@updateCompanies`, `App\Listeners\Update\V30\Version304@deleteOldFiles`, `App\Listeners\Update\V30\Version304@updateCompanies`, `App\Listeners\Update\V30\Version304@updateDatabase`, `App\Listeners\Update\V31\Version3119@updatePermissions`, `App\Listeners\Update\V31\Version318@updatePermissions`, `App\Utilities\Installer@finalTouches`, `route::routes/common.php::post::/livewire/update::closure` |
| cluster_002 | `App\Abstracts\Http\ApiController@accepted` | `App\Abstracts\Http\ApiController@created` |
| cluster_003 | `App\Abstracts\Http\Controller@setActiveTabForCategories` | `App\Abstracts\Http\Controller@setActiveTabForDocuments`, `App\Abstracts\Http\Controller@setActiveTabForTransactions`, `App\Abstracts\Job@setOwner`, `App\Abstracts\Job@setSource` |
| cluster_004 | `App\Abstracts\Http\PaymentController@__construct` | `App\Console\Commands\UpdateAll@handle`, `App\Http\Controllers\Settings\Invoice@edit`, `App\Http\Controllers\Settings\Localisation@edit`, `App\Jobs\Common\CreateMediableForDownload@getQueuedMedia`, `App\Jobs\Common\CreateMediableForExport@getQueuedMedia` |
| cluster_005 | `App\Abstracts\Http\PaymentController@getCancelUrl` | `App\Abstracts\Http\PaymentController@getConfirmUrl`, `App\Abstracts\Http\PaymentController@getReturnUrl` |
| cluster_006 | `App\Abstracts\Http\PaymentController@getFinishUrl` | `App\Abstracts\Http\PaymentController@getInvoiceUrl` |
| cluster_007 | `App\Abstracts\Http\PaymentController@getLogger` | `App\Abstracts\Listeners\Report@getDateRange`, `App\Console\Commands\Update@getNewVersion`, `App\Exceptions\Http\Resource@getErrors`, `App\Exceptions\Http\Resource@hasErrors`, `App\Http\Controllers\Settings\Company@edit`, `App\Http\Controllers\Settings\Email@edit`, `App\Http\Controllers\Settings\Schedule@edit`, `App\Http\Requests\Document\Document@messages`, `App\Http\Requests\Setting\Module@rules`, `App\Http\Requests\Setting\Setting@rules`, `App\Http\Requests\Wizard\Company@rules`, `App\Jobs\Common\CreateMediableForDownload@getLocalQueuedMedia`, `App\Jobs\Common\CreateMediableForExport@getLocalQueuedMedia`, `App\Listeners\Update\SendNotificationOnFailure@routeNotificationForMail`, `App\Listeners\Update\SendNotificationOnFailure@routeNotificationForSlack` |
| cluster_008 | `App\Abstracts\Http\PaymentController@getNotifyUrl` | `App\Abstracts\Http\PaymentController@getReference` |
| cluster_009 | `App\Abstracts\Job@bootCreate` | `App\Abstracts\Job@bootDelete`, `App\Abstracts\Job@bootUpdate` |
| cluster_010 | `App\Abstracts\Job@booted` | `App\Abstracts\Job@booting`, `App\Jobs\Common\DeleteCompany@booted`, `App\Jobs\Common\UpdateCompany@booted` |
| cluster_011 | `App\Abstracts\Listeners\Report@applyCustomerGroup` | `App\Abstracts\Listeners\Report@applyDateFilter`, `App\Abstracts\Listeners\Report@applyDiscountFilter`, `App\Abstracts\Listeners\Report@applySearchStringFilter`, `App\Abstracts\Listeners\Report@applyVendorGroup`, `App\Abstracts\Listeners\Report@setDateFilter` |
| cluster_012 | `App\Abstracts\Listeners\Report@getBasis` | `App\Abstracts\Listeners\Report@getDiscount`, `App\Abstracts\Listeners\Report@getPeriod`, `App\Http\Requests\Auth\Forgot@rules`, `App\Http\Requests\Auth\Login@rules`, `App\Http\Requests\Auth\Register@rules`, `App\Http\Requests\Auth\Reset@rules`, `App\Http\Requests\Auth\User@messages`, `App\Http\Requests\Banking\Account@rules`, `App\Http\Requests\Banking\Reconciliation@rules`, `App\Http\Requests\Banking\ReconciliationCalculate@rules`, `App\Http\Requests\Banking\TransactionConnect@rules`, `App\Http\Requests\Banking\Transfer@rules`, `App\Http\Requests\Common\BulkAction@rules`, `App\Http\Requests\Common\Company@messages`, `App\Http\Requests\Common\Company@rules`, `App\Http\Requests\Common\Contact@messages`, `App\Http\Requests\Common\ContactPerson@rules`, `App\Http\Requests\Common\CustomMail@rules`, `App\Http\Requests\Common\Dashboard@rules`, `App\Http\Requests\Common\Import@rules`, `App\Http\Requests\Common\Item@messages`, `App\Http\Requests\Common\Item@rules`, `App\Http\Requests\Common\ItemTax@rules`, `App\Http\Requests\Common\Notification@rules`, `App\Http\Requests\Common\Report@rules`, `App\Http\Requests\Common\ReportShow@rules`, `App\Http\Requests\Common\TotalItem@messages`, `App\Http\Requests\Common\TotalItem@rules`, `App\Http\Requests\Common\Widget@messages`, `App\Http\Requests\Common\Widget@rules`, `App\Http\Requests\Document\DocumentAddItem@rules`, `App\Http\Requests\Document\DocumentHistory@rules`, `App\Http\Requests\Document\DocumentItem@messages`, `App\Http\Requests\Document\DocumentItem@rules`, `App\Http\Requests\Document\DocumentItemTax@rules`, `App\Http\Requests\Document\DocumentTotal@rules`, `App\Http\Requests\Install\Database@rules`, `App\Http\Requests\Install\Setting@rules`, `App\Http\Requests\Module\Install@rules`, `App\Http\Requests\Module\Module@rules`, `App\Http\Requests\Portal\InvoiceConfirm@rules`, `App\Http\Requests\Portal\InvoicePayment@rules`, `App\Http\Requests\Portal\InvoiceShow@rules`, `App\Http\Requests\Portal\PaymentShow@rules`, `App\Http\Requests\Portal\Profile@messages`, `App\Http\Requests\Setting\Category@rules`, `App\Http\Requests\Setting\EmailTemplate@rules`, `App\Http\Requests\Setting\Setting@messages`, `App\Http\Requests\Wizard\Company@messages` |
| cluster_013 | `App\Abstracts\Listeners\Report@getCustomers` | `App\Abstracts\Listeners\Report@getExpenseCategories`, `App\Abstracts\Listeners\Report@getIncomeCategories`, `App\Abstracts\Listeners\Report@getItemCategories`, `App\Abstracts\Listeners\Report@getVendors` |
| cluster_014 | `App\Abstracts\Listeners\Report@skipThisClass` | `App\Abstracts\Listeners\Update@check` |
| cluster_015 | `App\Console\Commands\DownloadModule@copyFiles` | `App\Console\Commands\Update@copyFiles`, `App\Console\Commands\Update@unzip` |
| cluster_016 | `App\Http\Controllers\Api\Auth\Users@disable` | `App\Http\Controllers\Api\Auth\Users@enable` |
| cluster_017 | `App\Http\Controllers\Api\Auth\Users@index` | `App\Http\Controllers\Api\Banking\Accounts@index`, `App\Http\Controllers\Api\Banking\Reconciliations@index`, `App\Http\Controllers\Api\Banking\Transactions@index`, `App\Http\Controllers\Api\Common\Companies@index`, `App\Http\Controllers\Api\Common\Contacts@index`, `App\Http\Controllers\Api\Common\Dashboards@index`, `App\Http\Controllers\Api\Common\Items@index`, `App\Http\Controllers\Api\Common\Reports@index`, `App\Http\Controllers\Api\Document\Documents@index`, `App\Http\Controllers\Api\Settings\Categories@index`, `App\Http\Controllers\Api\Settings\Currencies@index`, `App\Http\Controllers\Api\Settings\Taxes@index` |
| cluster_018 | `App\Http\Controllers\Api\Auth\Users@show` | `App\Http\Controllers\Api\Banking\Accounts@show`, `App\Http\Controllers\Api\Common\Contacts@show`, `App\Http\Controllers\Api\Common\Items@show`, `App\Http\Controllers\Api\Document\Documents@show`, `App\Http\Controllers\Api\Settings\Currencies@show`, `App\Http\Controllers\Api\Settings\Settings@show` |
| cluster_019 | `App\Http\Controllers\Api\Auth\Users@store` | `App\Http\Controllers\Api\Banking\Accounts@store`, `App\Http\Controllers\Api\Banking\Reconciliations@store`, `App\Http\Controllers\Api\Banking\Transactions@store`, `App\Http\Controllers\Api\Banking\Transfers@store`, `App\Http\Controllers\Api\Common\Companies@store`, `App\Http\Controllers\Api\Common\Contacts@store`, `App\Http\Controllers\Api\Common\Dashboards@store`, `App\Http\Controllers\Api\Common\Items@store`, `App\Http\Controllers\Api\Common\Reports@store`, `App\Http\Controllers\Api\Document\Documents@store`, `App\Http\Controllers\Api\Settings\Categories@store`, `App\Http\Controllers\Api\Settings\Currencies@store`, `App\Http\Controllers\Api\Settings\Taxes@store` |
| cluster_020 | `App\Http\Controllers\Api\Common\Companies@canAccess` | `App\Http\Controllers\Api\Common\Companies@show` |

*+ 107 more clusters in the JSON report.*

## Coverage Gaps
Coverage rate: 2.7%; total gaps: 834

Top gaps by domain (full list in JSON):

| Domain | Gaps | Example surface |
|---|---:|---|
| Controllers | 501 | `App\Abstracts\Http\ApiController@accepted` |
| Jobs | 122 | `App\Abstracts\Job@bootCreate` |
| Other | 111 | `App\Exceptions\Http\Resource@getErrors` |
| Listeners | 83 | `App\Abstracts\Listeners\Update@check` |
| Console Commands | 16 | `App\Console\Commands\DownloadModule@copyFiles` |
| Models | 1 | `App\Models\Common\Company@forgetCurrent` |

## Candidates (require human decision)
| Source | Count |
|---|---:|
| Dead-code | 898 |
| Duplication | 594 |
| Coverage gaps | 834 |
| **Total** | **2326** |

Submit decisions via:
```bash
node public/sate.js review-queue --run-id <runId> --candidates <candidates.json>
node public/sate.js submit-decision --run-id <runId> --candidate-id <id> --decision APPROVE|REJECT|DEFER
```

## Governance History
> Decisions are append-only per CORE_FREEZE. No retroactive mutation.

Total decisions on record: 0
| Decision | Count |
|---|---:|
| APPROVE | 0 |
| REJECT | 0 |
| DEFER | 0 |
| OTHER | 0 |

## Version Pins
Every KB-derived fact carries: `learned_under: { safety_gate: v1, quality_gate: v1, manual_contract: v1 }`

## Runner Allowlist Reference
- Path: `.ai/manual/approved-tests.json`
- Approved test files: 1
- RUNNER enforcement is file-level. Per MANUAL_STAGE_CONTRACT v1, requested PHPUnit tests must be in this allowlist or RUNNER aborts.
