You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: CLAUDE.md
+1Lines changed: 1 addition & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -5,6 +5,7 @@
5
5
- Never add `Co-Authored-By` trailers to commit messages.
6
6
-**Goal is full http4s migration** — eliminate Lift Web and all deprecated libraries entirely. Treat Lift code as temporary scaffolding to be removed, not maintained. When fixing bugs or adding features, always prefer the http4s path.
7
7
-**Versioning is tech-agnostic** — API version numbers reflect API signature changes (new/changed fields, new behaviour), never the underlying framework. A framework migration (Lift → http4s) happens in-place at the existing version; it does not justify a version bump.
8
+
- **`APIMethodsXYZ.scala` (Lift) files are the source of truth for migration.** The commented-out Lift ResourceDocs and endpoints inside each `APIMethodsXYZ.scala` are the canonical reference for what the http4s version should match: URL templates, verb casing, summaries, descriptions, example bodies, error lists, tags. **Do NOT edit these files to make the parity audit pass.** The audit compares http4s against the Lift source-of-truth — when it flags a diff, the fix is to either (a) update http4s to match Lift, or (b) document the difference at the http4s site as a known intentional drift (e.g. a placeholder rename for `ResourceDocMatcher` middleware, or an upstream-driven case-class shape change). Rewriting the Lift comments to match http4s runs the comparison backwards and destroys the historical record. See `scripts/check_lift_http4s_resource_doc_parity.py` for the audit, and `scripts/rehydrate_resource_docs.py` / `scripts/restore_resource_doc_bodies.py` for the canonical Lift → http4s restoration tools.
Copy file name to clipboardExpand all lines: LIFT_HTTP4S_MIGRATION.md
+131Lines changed: 131 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -164,6 +164,137 @@ Currently served via a raw Lift `serve { case Req(..., "openapi.yaml", ...) }` b
164
164
165
165
---
166
166
167
+
## ResourceDoc parity (per-version drift from Lift)
168
+
169
+
Separate from the resource-docs **serving** workstream above, there is a parity workstream covering the **content** of each migrated ResourceDoc declaration. The goal is for every http4s `ResourceDoc(...)` to render identically to its Lift original, so the public API docs aren't silently degraded by migration.
170
+
171
+
### Principle
172
+
173
+
**`APIMethodsXYZ.scala` (Lift) is the source of truth for migration.** The commented-out Lift ResourceDocs and endpoints inside each `APIMethodsXYZ.scala` are the canonical reference for what the http4s version should render: URL templates, verb casing, summaries, descriptions, example bodies, error lists, tags. **Do NOT edit these files to make the audit pass** — the audit compares http4s against the Lift source-of-truth. When the audit flags a diff, the resolution is either (a) update http4s to match Lift, or (b) document the difference at the http4s site as a known intentional drift (placeholder rename for middleware, upstream-driven case-class shift, etc.). Rewriting the Lift comments runs the comparison backwards and erases the historical record. (Mistakes in commits `d95c1df01` and `6154bf2cc` did this; reverted in `27f48af72`.)
174
+
175
+
**Stub fidelity verified.** Commits `810589330` (v6) and `88f46f854` (v5.1) replaced the live Lift code with commented-out stubs. Comparing each stub's uncommented ResourceDoc bodies against the pre-stub live versions: **0 field diffs across 243/243 v6 docs and 111/111 v5.1 docs**. The non-ResourceDoc deltas (imports, etc., ~16KB v6 / ~5KB v5.1) are immaterial. The stubs are an exact preservation of the original Lift ResourceDocs.
176
+
177
+
### Tooling (`scripts/`)
178
+
179
+
| Script | Role |
180
+
|---|---|
181
+
|`check_lift_http4s_resource_doc_parity.py`| Read-only audit. Parses both files, matches by `nameOf(...)` (with `.replace("a","b")` evaluation for derived names), reports per-field diffs. `--field=X` to focus, `--list-only` for endpoint-presence summary. |
182
+
|`rehydrate_resource_docs.py`| Upstream (simonredfern, `67593ea28`). Lifts positional args 7/8/9 (description, exampleRequestBody, successResponseBody) from commented Lift blocks into http4s. Has a `split-init` subcommand for JVM 64KB method-size workaround. |
183
+
|`restore_resource_doc_bodies.py`| Companion to the above. Restores any subset of (summary, description, exampleRequestBody, successResponseBody, errorResponseBodies, tags) from Lift into http4s. Surgical per-field replacement preserves layout. `--fields=X,Y` to scope, `--only=ep` to target one endpoint. |
184
+
185
+
### Current drift (audit re-run 2026-05-21 evening)
186
+
187
+
| Version | shared | mismatch | only-lift | only-http4s | Status |
### v6.0.0 — 12 specific drifts (each is a fix candidate)
204
+
205
+
These are the cases where http4s deviates from Lift. Under the source-of-truth rule, the default is to fix http4s; deliberate exceptions need to be documented at the http4s site.
206
+
207
+
| Endpoint | Field | Lift | http4s | Resolution |
208
+
|---|---|---|---|---|
209
+
|`createCounterpartyAttribute`| requestUrl |`…/counterparties/COUNTERPARTY_ID/attributes`|`…/COUNTERPARTY_ID_PARAM/…`| TBD — verify `ResourceDocMatcher` correctly handles `COUNTERPARTY_ID` as a wildcard (the literal set contains `COUNTERPARTY`, but `COUNTERPARTY_ID` is whole-segment-different). If safe, revert to Lift's name. |
210
+
|`deleteCounterpartyAttribute`| requestUrl | same | same | same as above |
211
+
|`getAllCounterpartyAttributes`| requestUrl | same | same | same as above |
212
+
|`getCounterpartyAttributeById`| requestUrl | same | same | same as above |
213
+
|`updateCounterpartyAttribute`| requestUrl | same | same | same as above |
214
+
|`createTransactionRequestCardano`| requestUrl |`…/ACCOUNT_ID/owner/transaction-request-types/CARDANO/…`|`…/ACCOUNT_ID/VIEW_ID/…/CARDANO/…`|**Functional broadening** — http4s lets any view, Lift hardcoded `owner`. Keep http4s; document at the http4s ResourceDoc site. |
215
+
|`createTransactionRequestHold`| requestUrl |`…/owner/…HOLD/…`|`…/VIEW_ID/…HOLD/…`| same as above |
216
+
|`getSystemViewById`| requestUrl |`/management/system-views/VIEW_ID`|`/management/system-views/SYS_VIEW_ID`| TBD — disambiguation rename. If `ResourceDocMatcher` handles both fine, revert. |
217
+
|`updateSystemView`| requestUrl |`/system-views/VIEW_ID`|`/system-views/UPD_VIEW_ID`| same as above |
218
+
|`removeBankReaction`| requestUrl |`…/reactions/EMOJI`|`…/reactions/EMOJI_REACTION`|`EMOJI` is NOT in `literalAllCapsSegments` (only `EMAIL`/`SMS`/`IMPLICIT` of the SCA cluster are). Rename may have been defensive; safe to revert. |
219
+
|`removeSystemReaction`| requestUrl | same | same | same as above |
220
+
|`getAccountDirectory`| successResponseBody |`FastFirehoseRoutings(bank_id, account_id)`|`AccountRoutingJsonV121(scheme, address)`|**Upstream functional change** (`9e151c524` / `9dc4c4c46` migrated the case class). Cannot revert; document. Also note: the same change broke `mvn test` (pre-existing upstream compile error in `JSONFactory6.0.0.scala:2934`). |
221
+
222
+
Also: 1 only-http4s (`createWebUiProps`) — genuinely http4s-only with no Lift counterpart. Document.
223
+
224
+
### v5.1.0 — 1 specific drift
225
+
226
+
| Endpoint | Field | Lift | http4s | Resolution |
227
+
|---|---|---|---|---|
228
+
|`revokeMyConsent`| requestVerb |`"Delete"`|`"DELETE"`| Trivial casing fix on the http4s side. |
229
+
230
+
Also:
231
+
- 1 only-lift (`createConsentImplicit`) + 1 only-http4s (`createConsent`) — Lift had `lazy val createConsentImplicit = createConsent` aliasing and registered the doc under the alias; http4s registers under the canonical name. Fix: in http4s, either rename the partial function to `createConsentImplicit` to match Lift, or register a second `nameOf(createConsentImplicit)` doc for the same handler.
232
+
- 1 only-http4s (`getBanks`) — kept in the v5.1.0 layer for metrics attribution (intentional addition; see comment at `Http4s510.scala:288`). Document.
233
+
234
+
### v3.0.0 — 4 specific drifts
235
+
236
+
After semantic-field restoration, only middleware-driven URL renames remain.
|`getFirehoseTransactionsForBankAccount`| requestUrl |`/banks/BANK_ID/firehose/accounts/ACCOUNT_ID/views/VIEW_ID/transactions`|`/banks/FIREHOSE_BANK_ID/firehose/accounts/FIREHOSE_ACCOUNT_ID/views/FIREHOSE_VIEW_ID/transactions`| Same firehose pattern. **Document**. |
244
+
245
+
No only-lift or only-http4s entries for v3.0.0.
246
+
247
+
### v3.1.0 — 5 specific drifts
248
+
249
+
After semantic-field restoration (commit `f4b9bd183`), only middleware-driven placeholder renames remain.
250
+
251
+
| Endpoint | Field | Lift | http4s | Resolution |
252
+
|---|---|---|---|---|
253
+
|`createAccount`| requestUrl |`/banks/BANK_ID/accounts/ACCOUNT_ID`|`…/NEW_ACCOUNT_ID`| PUT-creates-account pattern. Middleware would 404 on `ACCOUNT_ID` lookup before the handler. **Document** — required. |
254
+
|`deleteSystemView`| requestUrl |`/system-views/VIEW_ID`|`/SYS_VIEW_ID`| Disambiguation from other VIEW_ID usages. **Document**. |
255
+
|`getSystemView`| requestUrl | same | same | same |
256
+
|`updateSystemView`| requestUrl | same | same | same |
257
+
|`getFirehoseCustomers`| requestUrl |`/banks/BANK_ID/firehose/customers`|`…/FIREHOSE_BANK_ID/…`| Firehose middleware bypass — prop check must run before bank lookup (see CLAUDE.md). **Document** — required. |
| requestUrl — firehose pattern | 1 |`getFirehoseAccountsAtOneBank` (Lift `BANK_ID/.../VIEW_ID` → http4s `FIREHOSE_BANK_ID/.../FIREHOSE_VIEW_ID`) | Middleware bypass for the prop-check-before-bank-lookup pattern (see CLAUDE.md "Prop check before role check" gotcha). **Document** — required for correctness. |
273
+
| requestUrl — Lift URL malformed | 1 |`deleteCustomerAttribute` (Lift `/banks/BANK_ID/CUSTOMER_ID/attributes/.../...` is missing `/customers/`; http4s uses `/banks/BANK_ID/customers/attributes/...`) | Lift URL was buggy. http4s fixed it. **Document** as deliberate URL fix; flag that the Lift comment preserves the original bug as historical record. |
274
+
275
+
Also: 2 only-lift (`getAllAuthenticationTypeValidationsPublic`, `getAllJsonSchemaValidationsPublic`) — these endpoints exist in Lift v4 but were not migrated to `Http4s400`. **Migration gap** — port them. 5 only-http4s (`createBankLevelDynamicEntity`, `createSystemDynamicEntity`, `updateBankLevelDynamicEntity`, `updateMyDynamicEntity`, `updateSystemDynamicEntity`) — dynamic-entity overrides added in http4s with no Lift equivalent. Document if intentional, or audit whether they should have Lift counterparts.
276
+
277
+
### v5.0.0 — 8 specific drifts + 3 only-http4s
278
+
279
+
| Category | Count | Endpoints | Resolution |
280
+
|---|---|---|---|
281
+
| requestUrl placeholder rename | 1 |`createAccount` (Lift `ACCOUNT_ID` → http4s `NEW_ACCOUNT_ID` for the PUT-creates pattern) | Verify matcher behavior; may be required for `ACCOUNT_ID` literal handling. |
282
+
| errorResponseBodies — SCA val-vs-inline | 3 |`createConsentByConsentRequestIdEmail` / `Sms` / `Implicit`| http4s uses `private val createConsentByConsentRequestIdCommonErrors = List(...)` for DRY; Lift inlined the list. Either inline the val in the 3 doc registrations to match Lift verbatim, or extend the audit script to expand simple `val X = List(...)` references. |
283
+
| errorResponseBodies — system-view accuracy | 4 |`createSystemView`, `deleteSystemView`, `getSystemView`, `updateSystemView`| http4s has more accurate errors (`SystemViewNotFound`, `SystemViewCannotBePublicError`, `InvalidSystemViewFormat`). Lift had wrong/legacy errors (`BankAccountNotFound`, `$BankNotFound`, `"user does not have owner access"`). **Genuine improvement** — document at http4s site. |
284
+
285
+
Also: 3 only-http4s (`getBanks`, `getProduct`, `getProducts`) — kept in this layer for metrics attribution. Document.
286
+
287
+
### Strategy summary
288
+
289
+
For each remaining drift on a migrated version:
290
+
1.**Default**: fix http4s to match Lift verbatim. Use `restore_resource_doc_bodies.py` for field-level restoration.
291
+
2.**Documented exceptions**: where the drift is a deliberate http4s improvement or required by middleware semantics, leave the drift and add a `// Lift had X; we use Y because Z` comment at the http4s ResourceDoc site.
292
+
3.**Never**: edit `APIMethodsXYZ.scala` to make the audit pass. The Lift comments are the canonical record.
293
+
294
+
Untouched versions (v1_2_1 through v4_0_0, plus v2_1_0) need the same treatment: run `rehydrate_resource_docs.py` then `restore_resource_doc_bodies.py`, then audit and address any residual drifts at the http4s site.
295
+
296
+
---
297
+
167
298
## Auth Stack (separate workstream)
168
299
169
300
Token-generation paths — not version-file endpoints. Each `extends RestHelper` and needs to become an http4s route or middleware independently. Can run in parallel with the APIMethods migration.
0 commit comments