|
| 1 | +# Phase 4b.2 audit — Orphan capability classification |
| 2 | + |
| 3 | +**Date:** 2026-04-21 |
| 4 | +**HEAD:** `6f79b1e` (`main`) — Phase 4b.1 shipped; this audit is for the follow-on step |
| 5 | +**Scope:** read-only audit of the 32 DB rows that lack a YAML source file. No DB writes, no YAML generation, no row deletion. Output is the narrative below plus the decision CSV at `audit-reports/2026-04-21-phase-4b2-orphan-decisions.csv`. |
| 6 | + |
| 7 | +--- |
| 8 | + |
| 9 | +## 1. Executive summary |
| 10 | + |
| 11 | +Thirty-two capability rows exist in the DB without a matching YAML file in `manifests/` — the set difference after Phase 4b.1's 275-slug backfill. The cohort breakdown from audit §4.13 matches current prod reality exactly: 17 active web3 / 11 suspended UK-property / 4 deactivated. Evidence strongly favours a three-way split for the vast majority: **17 `yaml-generate`** (all active web3, all with code + recent traffic + solution refs), **9 `suspend-add-yaml`** (UK-property caps still tied to a suspended `uk-property-check` solution), **3 `delete`** (deactivated, no solution refs, no recent traffic). Three slugs land in `needs-human-input`: `singapore-company-data` (deactivated but referenced by three **active** solutions — broken prod state, can't auto-classify), and `email-pattern-discover` + `officer-search` (mis-cohorted by the original audit as UK-property — they actually belong to `company-intelligence-sdr`, a currently-active SDR solution, yet themselves are suspended — lifecycle inconsistency). |
| 12 | + |
| 13 | +Two structural issues surface that the implementation prompt must handle before YAML generation can succeed for the 17 web3 slugs: |
| 14 | + |
| 15 | +1. **`web3` category not in VALID_CATEGORIES** ([apps/api/src/lib/onboarding-gates.ts:203](../apps/api/src/lib/onboarding-gates.ts#L203)). All 17 web3 orphans have `category: web3` in the DB. The manifest-completeness CI gate (Phase 4b.1) would reject generated YAML. The implementation prompt must add `web3` to the enum. |
| 16 | +2. **`uk-property-check` solution is `is_active=false`** while the 9 capabilities it depends on are `lifecycle_state=suspended` with `is_active=true` in the capabilities table. Consistent at the aggregate level but worth confirming whether the solution's suspension actually blocks calls or whether the suspended capabilities could still be invoked independently. |
| 17 | + |
| 18 | +--- |
| 19 | + |
| 20 | +## 2. Methodology |
| 21 | + |
| 22 | +### 2.1 Evidence axes |
| 23 | + |
| 24 | +Five axes of evidence per slug, all queried live against prod: |
| 25 | + |
| 26 | +| Axis | Query source | What it tells us | |
| 27 | +|---|---|---| |
| 28 | +| Lifecycle state | `capabilities.lifecycle_state`, `is_active`, `visible` | Is operator intent active/paused/dead | |
| 29 | +| Code presence | `apps/api/src/capabilities/<slug>.ts` filesystem check | Is there a working implementation | |
| 30 | +| Test activity | `test_results` (`capability_slug`, `executed_at`) | Is the capability being tested (= maintained) | |
| 31 | +| Transaction activity | `transactions` joined via `capability_id` | Have agents/users actually called this | |
| 32 | +| Solution composition | `solution_steps.capability_slug` → `solutions.slug`, `solutions.is_active` | Does composition depend on this | |
| 33 | + |
| 34 | +### 2.2 Confidence rules applied |
| 35 | + |
| 36 | +- **high**: all five axes align (e.g. active lifecycle + code + recent tests + recent tx + active solution → `yaml-generate`; deactivated + no solution + old/no tx → `delete`). |
| 37 | +- **medium**: one axis ambiguous but the others outweigh (e.g. UK-property: suspended, no recent tests — but clear intent from the `uk-property-check` solution being in the catalogue as a suspended package, code present, recent capability-row touches → `suspend-add-yaml`). |
| 38 | +- **low / needs-human-input**: two or more axes contradict, or a structural issue blocks the natural classification (singapore-company-data: deactivated capability depended on by active solutions; email-pattern-discover: mis-cohort'd in original audit and inconsistent lifecycle across solution and capability). |
| 39 | + |
| 40 | +### 2.3 What was NOT queried |
| 41 | + |
| 42 | +- User-level activity (which specific API key or agent invoked a capability): not needed for classification, would be needed for deprecation communications. |
| 43 | +- x402 exposure (`x402_enabled`): not queried because none of the orphans are reasonably candidates for payment-gateway exposure right now — 4b.2 is upstream of that concern. |
| 44 | +- Free-tier membership: all 32 orphans have `is_free_tier = false` per the evidence pass, confirmed during Step 2. |
| 45 | + |
| 46 | +--- |
| 47 | + |
| 48 | +## 3. Cohort analysis |
| 49 | + |
| 50 | +### 3.1 Active web3 — 17 slugs |
| 51 | + |
| 52 | +All 17 capabilities share a stable pattern: |
| 53 | + |
| 54 | +- `lifecycle_state = active`, `visible = true`, `is_active = true` |
| 55 | +- `category = web3` (enum gap; see §1 item 1 and §6 item 1) |
| 56 | +- `maintenance_class = free-stable-api` (12) or `pure-computation` (1 — `contract-verify-check`) |
| 57 | +- Code present (`apps/api/src/capabilities/<slug>.ts` exists for all 17) |
| 58 | +- Last test run within the last 6 days (typical: 2026-04-15 / 16, 100–280 total runs) |
| 59 | +- 28–66 transactions ever, 1–6 in the last 7 days |
| 60 | +- 15 of 17 referenced by at least one active web3 solution (`web3-*`) |
| 61 | +- 2 of 17 — `ens-resolve` and `fear-greed-index` — stand without a current solution dependency, but are actively tested and seeing traffic (37 tx and 31 tx respectively). Both are self-contained primitives (ENS lookup, sentiment snapshot) that don't naturally compose into multi-step solutions. |
| 62 | + |
| 63 | +**CC recommendation: all 17 → `yaml-generate` (high confidence)**. |
| 64 | + |
| 65 | +All 17 look like straightforward Phase 4b.1-style backfill targets that didn't get a YAML simply because they were onboarded via a different path in late March 2026 (earliest `created_at: 2026-03-29`). Implementation must add `web3` to `VALID_CATEGORIES` first or every generated YAML fails the CI gate. |
| 66 | + |
| 67 | +Notable slugs: |
| 68 | +- `wallet-risk-score` — most-composed (5 solution refs). Highest downstream blast radius. |
| 69 | +- `contract-verify-check` — only `pure-computation` orphan in the cohort; the rest are `free-stable-api` (Etherscan / DefiLlama / GoPlus Labs / ENS RPC). |
| 70 | +- `ens-resolve` / `ens-reverse-lookup` — 2 slugs, 1 is heavily composed (3 solution refs), the other is not. Generate YAML for both symmetrically. |
| 71 | + |
| 72 | +### 3.2 Suspended UK-property — 11 slugs (recategorised to 9 + 2) |
| 73 | + |
| 74 | +Original audit cohort'd these as 11 UK-property caps, but the evidence shows that's slightly miscounted: |
| 75 | + |
| 76 | +**9 true UK-property caps** (all reference `uk-property-check` solution): |
| 77 | +`council-tax-lookup`, `stamp-duty-calculate`, `uk-crime-stats`, `uk-deprivation-index`, `uk-epc-rating`, `uk-flood-risk`, `uk-rental-yield`, `uk-sold-prices`, `uk-transport-access`. |
| 78 | + |
| 79 | +All 9: |
| 80 | +- `lifecycle_state = suspended`, but `is_active = true` (lifecycle vs is_active flag mismatch — flag `is_active` seems unused for gate purposes, `lifecycle_state` is authoritative) |
| 81 | +- Code present |
| 82 | +- Zero test runs, zero transactions — suspended before ever running |
| 83 | +- Referenced by `uk-property-check` solution (itself `is_active=false`) |
| 84 | +- Created 2026-04-11 (same day; this was a planned suspend-in-stasis onboarding cohort) |
| 85 | + |
| 86 | +The cohort represents a coherent package waiting on a regulatory/business trigger; Phase 4b.2 should bring them inside the YAML model so if the suspension lifts, they have complete manifests for the pipeline. **All 9 → `suspend-add-yaml` (medium confidence)**. Medium rather than high because an alternative reading is "this is vapourware — delete until there's actual customer interest" — but the solution in the catalogue and the coherent data-source set argue for preservation. |
| 87 | + |
| 88 | +**2 mis-cohort'd SDR caps**: `email-pattern-discover`, `officer-search`. |
| 89 | + |
| 90 | +Both: |
| 91 | +- `lifecycle_state = suspended`, `is_active = true` |
| 92 | +- Code present |
| 93 | +- 1 test run each (created + tested once, then suspended) |
| 94 | +- 1 transaction each (the test) |
| 95 | +- Referenced by `company-intelligence-sdr` — an **active** solution |
| 96 | + |
| 97 | +This is an inconsistency: an active solution contains steps pointing to suspended capabilities. The solution would either fail at execution or silently skip these steps. This is a second prod-state concern (alongside `singapore-company-data`, §3.3). Per prompt stop conditions, any active-solution → suspended-or-deactivated-capability dependency is a surface-not-auto-classify case. **Both → `needs-human-input` (low confidence)**. Petter must decide whether to: |
| 98 | +- Reactivate the two capabilities so `company-intelligence-sdr` works end-to-end → `yaml-generate` |
| 99 | +- Deactivate the two capabilities AND remove them from the solution (or deactivate the solution) → `delete` or `deprecate` |
| 100 | +- Accept the inconsistency (maybe these are optional steps with graceful degradation) and `suspend-add-yaml` |
| 101 | + |
| 102 | +### 3.3 Deactivated — 4 slugs (3 delete + 1 broken) |
| 103 | + |
| 104 | +**3 clean deletes**: `amazon-price`, `hong-kong-company-data`, `indian-company-data`. |
| 105 | + |
| 106 | +All 3: |
| 107 | +- `lifecycle_state = deactivated`, `visible = false`, `is_active = false` |
| 108 | +- Code present (from earlier onboarding) but unused at runtime |
| 109 | +- Last test run / transaction in mid-March 2026 (6+ weeks ago) |
| 110 | +- No solution references |
| 111 | +- No recent activity |
| 112 | + |
| 113 | +**All 3 → `delete` (high confidence)**. Standard cleanup pattern. |
| 114 | + |
| 115 | +**1 broken / prod-state concern**: `singapore-company-data`. |
| 116 | + |
| 117 | +- `lifecycle_state = deactivated`, `visible = false`, `is_active = false` |
| 118 | +- **Referenced by 3 active solutions**: `invoice-verify-sg`, `kyb-complete-sg`, `kyb-essentials-sg` |
| 119 | +- Last activity March 2026 |
| 120 | + |
| 121 | +Deleting `singapore-company-data` would violate the `solution_steps.capability_slug` FK (`onDelete: "restrict"` per [schema.ts:378](../apps/api/src/db/schema.ts#L378)). The FK would abort the DELETE. More importantly, the three solutions would break at runtime — any agent calling `invoice-verify-sg` or `kyb-complete-sg` or `kyb-essentials-sg` today would fail at the Singapore step. |
| 122 | + |
| 123 | +This is an out-of-scope prod issue that 4b.2 can't resolve without Petter deciding: does Strale offer Singapore KYB, or not? If yes, the capability needs to be reactivated (needs a working data source — ACRA seems to have fallen over per the deactivation). If not, the three Singapore solutions need to be either removed from the catalogue or have their Singapore step replaced with a fallback. |
| 124 | + |
| 125 | +**Recommendation: `needs-human-input` (low confidence)**. Deferred to Petter. |
| 126 | + |
| 127 | +--- |
| 128 | + |
| 129 | +## 4. High-confidence recommendations (rubber-stamp batch) |
| 130 | + |
| 131 | +Petter can batch-approve these 20 slugs without per-slug review: |
| 132 | + |
| 133 | +**`yaml-generate` — 17 slugs (all active web3):** |
| 134 | +approval-security-check, contract-verify-check, ens-resolve, ens-reverse-lookup, fear-greed-index, gas-price-check, phishing-site-check, protocol-fees-lookup, protocol-tvl-lookup, stablecoin-flow-check, token-security-check, vasp-non-compliant-check, vasp-verify, wallet-age-check, wallet-balance-lookup, wallet-risk-score, wallet-transactions-lookup. |
| 135 | + |
| 136 | +**`delete` — 3 slugs:** |
| 137 | +amazon-price, hong-kong-company-data, indian-company-data. |
| 138 | + |
| 139 | +## 5. Needs-human-input — 3 slugs |
| 140 | + |
| 141 | +| Slug | Ambiguity | |
| 142 | +|---|---| |
| 143 | +| `singapore-company-data` | Deactivated in `capabilities`; referenced by 3 **active** solutions (`invoice-verify-sg`, `kyb-complete-sg`, `kyb-essentials-sg`). Deletion is FK-blocked. Needs product decision on Singapore KYB offering. | |
| 144 | +| `email-pattern-discover` | Suspended capability, referenced by active `company-intelligence-sdr` solution. Lifecycle inconsistent. Reactivate, remove from solution, or accept as optional step? | |
| 145 | +| `officer-search` | Same inconsistency as email-pattern-discover — suspended capability referenced by active `company-intelligence-sdr` solution. Treat same as above. | |
| 146 | + |
| 147 | +## 6. Medium-confidence recommendations |
| 148 | + |
| 149 | +9 slugs — **`suspend-add-yaml`**: council-tax-lookup, stamp-duty-calculate, uk-crime-stats, uk-deprivation-index, uk-epc-rating, uk-flood-risk, uk-rental-yield, uk-sold-prices, uk-transport-access. |
| 150 | + |
| 151 | +Rationale: UK-property suspended cohort, consistent with the `uk-property-check` (also suspended) solution. All 9 are in stasis waiting for a potential revival. Bringing them inside the YAML model means the suspend→reactivate path works through the normal pipeline. Alternative (`delete` + re-author later) is viable but loses the authored structural work that already exists in the DB row. |
| 152 | + |
| 153 | +--- |
| 154 | + |
| 155 | +## 7. Risks and considerations |
| 156 | + |
| 157 | +### 7.1 FK constraints block delete operations |
| 158 | + |
| 159 | +`solution_steps.capability_slug` has `onDelete: "restrict"` on `capabilities.slug`. Any orphan with solution references **cannot be deleted** without first removing its solution steps. This affects: |
| 160 | + |
| 161 | +- `singapore-company-data` (3 solutions) — delete blocked, deferred to human |
| 162 | +- None of the other deactivated orphans (amazon-price, hong-kong-company-data, indian-company-data) have solution refs, so they can be safely deleted |
| 163 | + |
| 164 | +The implementation prompt must check solution references at delete time and fail loudly if any are found — not silently skip. |
| 165 | + |
| 166 | +### 7.2 `web3` category enum gap |
| 167 | + |
| 168 | +All 17 web3 orphans have `category: web3`, but `VALID_CATEGORIES` in [onboarding-gates.ts:203-210](../apps/api/src/lib/onboarding-gates.ts#L203-L210) does not include `web3`. Generating YAML for these 17 with `category: web3` would fail the Phase 4b.1 manifest-completeness CI gate. |
| 169 | + |
| 170 | +**Options** (implementation prompt decides): |
| 171 | +- Add `web3` to `VALID_CATEGORIES` (1-line change; matches DB reality; low risk) |
| 172 | +- Re-categorise each web3 capability to an existing category (e.g. `finance` or `data-extraction`) — loses the web3 grouping signal |
| 173 | +- Add to VALID_CATEGORIES AND verify no downstream consumers (frontend catalog, search indexing, dashboard) treat the category as a fixed list that excludes `web3` |
| 174 | + |
| 175 | +Recommendation: **add `web3` to the enum**, update `capability-field-authority.test.ts` if it asserts the enum contents, ship alongside the web3 YAML generation in Phase 4b.2 implementation. Blocker for the 17-slug batch. |
| 176 | + |
| 177 | +### 7.3 `uk-property-check` solution is inactive |
| 178 | + |
| 179 | +`uk-property-check` has `is_active=false`, matching its 9 suspended caps. No immediate call risk. But if `suspend-add-yaml` lands for the 9 capabilities without also resolving the solution's fate, the catalog ends up with "suspended solution, YAML-complete suspended caps" — coherent but adds a third state to track. Phase 4b.2 implementation should decide whether to reactivate the solution alongside the YAML-adds, or leave both suspended as a unit. |
| 180 | + |
| 181 | +### 7.4 `company-intelligence-sdr` mixed-state solution |
| 182 | + |
| 183 | +The solution is `is_active=true`, but 2 of its 9 steps (officer-search + email-pattern-discover) are suspended capabilities. Any agent calling the solution today would hit an error at those steps. Either the 2 caps shouldn't be suspended, the steps shouldn't be in the solution, or the solution shouldn't be active. Flagged for human decision above. Resolution should happen before Phase 4b.2 implementation, or in concert with it. |
| 184 | + |
| 185 | +### 7.5 Code retention for deleted rows |
| 186 | + |
| 187 | +Deleting the 3 clean-delete slugs from the DB leaves their TypeScript implementations (`apps/api/src/capabilities/<slug>.ts`) in the repo. Those files also register themselves via `registerCapability()` at module load — if they're not also deleted, the registration calls happen but route to no DB row (matched by slug), which should be a safe no-op but is worth verifying. Implementation prompt should delete the `.ts` files alongside the DB rows. |
| 188 | + |
| 189 | +### 7.6 No snapshot for orphans (expected) |
| 190 | + |
| 191 | +Per Phase 4b.1's Outcome C, the 32 orphans' `onboarding_manifest` column is NULL. Deletion doesn't have to touch that column. For `yaml-generate` and `suspend-add-yaml` targets, the implementation prompt should also run the snapshot step (`phase-4b1-snapshot-onboarding-manifest.ts` or equivalent) after generating YAML so the new rows match the 275-slug baseline. |
| 192 | + |
| 193 | +--- |
| 194 | + |
| 195 | +## 8. Follow-up implementation prompt scope |
| 196 | + |
| 197 | +Phase 4b.2 implementation (after Petter fills the CSV's `decision` column) should: |
| 198 | + |
| 199 | +### 8.1 Pre-conditions |
| 200 | + |
| 201 | +- All 3 `needs-human-input` slugs have a concrete decision filled in the CSV. |
| 202 | +- The `web3` category decision is made (add to enum vs re-categorise) if any `yaml-generate` remains. |
| 203 | + |
| 204 | +### 8.2 Action order |
| 205 | + |
| 206 | +1. **Enum extension first** — add `web3` to `VALID_CATEGORIES` if any web3 `yaml-generate` remains. Ship as a separate small commit or folded into the generation commit. CI gate green after this or the next step fails. |
| 207 | +2. **`yaml-generate` pass** — for each marked slug, run the extended `generate-manifests.ts` generator (scoped to the slug list) or the 4b.1 backfill-style script adapted for orphans. Review each output, commit in one batch. |
| 208 | +3. **Snapshot pass** — run the 4b.1 snapshot script for the newly-YAMLd slugs so their `onboarding_manifest` column aligns with the 275-slug baseline. |
| 209 | +4. **`suspend-add-yaml` pass** — same generator, but the YAMLs go in with `lifecycle_state` unchanged (still suspended). |
| 210 | +5. **`deprecate` pass, if any** — likely won't apply here, but if Petter marks anything `deprecate`, implementation needs to: announce (via changelog or deprecation notice), set a sunset date, then eventually delete. Out of scope for this audit. |
| 211 | +6. **`delete` pass (last)** — for each marked slug: |
| 212 | + - Verify no solution_steps reference (abort if any) |
| 213 | + - `DELETE FROM capabilities WHERE slug = ?` |
| 214 | + - Delete the TypeScript implementation file |
| 215 | + - Grep remaining code for any straggling references (`rg "<slug>"` in src/) |
| 216 | + |
| 217 | +### 8.3 Rollback considerations per action |
| 218 | + |
| 219 | +| Action | Rollback | |
| 220 | +|---|---| |
| 221 | +| `yaml-generate` | `git revert` the commit; deletes the new YAMLs | |
| 222 | +| `suspend-add-yaml` | `git revert` same as above; lifecycle is DB-canonical per 4a so not touched | |
| 223 | +| Enum extension | `git revert` removes `web3` from `VALID_CATEGORIES`; any YAML relying on it then fails the CI gate | |
| 224 | +| `delete` | Capability row is gone from DB; only rollback is re-INSERT with same data. Capture pre-state in the commit body. TypeScript file rollback is `git revert`. | |
| 225 | + |
| 226 | +### 8.4 Not in 4b.2 scope |
| 227 | + |
| 228 | +- The broader solution-vs-capability-lifecycle consistency issue (singapore-company-data / company-intelligence-sdr mixed states) is a prod-correctness issue and deserves its own prompt with product-level input from Petter. |
| 229 | +- Orphan resolution for any DB rows that appear after 4b.2 ships — this audit is a snapshot of 2026-04-21. Future orphan prevention is the 4b.1 CI gate's job. |
| 230 | +- Phase 5 Cluster 2 tasks (F-B-003/F-B-004/F-B-012) remain separate. |
| 231 | + |
| 232 | +--- |
| 233 | + |
| 234 | +## 9. References |
| 235 | + |
| 236 | +- **Phase 4b audit** (`ec2a6aa`): `audit-reports/2026-04-20-phase-4b-audit.md` §4.13 (orphan identification) |
| 237 | +- **Phase 4b.1 commit** (`6f79b1e`): ships the 275-slug backfill; orphans excluded |
| 238 | +- **Schema**: [apps/api/src/db/schema.ts](../apps/api/src/db/schema.ts) — capabilities, solution_steps FK, test_results, transactions |
| 239 | +- **CI gate**: [apps/api/src/lib/manifest-completeness.test.ts](../apps/api/src/lib/manifest-completeness.test.ts) (Phase 4b.1) — what a YAML must satisfy |
| 240 | +- **Validators**: [apps/api/src/lib/onboarding-gates.ts](../apps/api/src/lib/onboarding-gates.ts) — `VALID_CATEGORIES` (L203), `validateManifest` (L401) |
| 241 | +- **Evidence table**: [audit-reports/2026-04-21-phase-4b2-orphan-decisions.csv](2026-04-21-phase-4b2-orphan-decisions.csv) — 32 rows, per-slug evidence + CC recommendation, `decision` column for Petter |
0 commit comments