Removes the duplicated join predicate of sync reservations. Three repositories (membership access, instrument metadata, catalog discovery) repeated the same rule: join the same process's running attempt only while it is still active in memory. It now lives in one function. Behaviour is unchanged.
The Issue proposed a shared reserve skeleton. Only the join predicate is shared: the reserve blocks differ in scope key, intent, priority, pre-checks (membership refresh TTL, findUniqueOrThrow), and the active structure (Set vs Map). Passing all of that through one generic runner would be more code than the three duplicates. Candle join (raises priority, rewrites intent) and screen join (checks progress.dispatched and children) follow different rules and are intentionally not changed.
Changes
tbank-access.service.ts: new isJoinableRunningAttempt type guard, next to TbankSyncAttempt. Works with Set and Map.
tbank.api.ts: re-export of the predicate (existing public surface, no new surface).
instrument-metadata.repository.ts, catalog-discovery.repository.ts: use the predicate.
test/tbank-membership-reservation.e2e-spec.ts (new): characterization on SQLite. Same-process join returns the same id and attempt; after the attempt leaves the active set a relaunch creates a new attempt; a foreign process never joins. A mutation that ignores the active set makes the test fail (checked locally).
Verification
Baseline on develop before changes: unit 310/310, e2e 322/322, openapi:check OK.
After changes: tsc app and test configs: 0 errors; npm test 310/310; npm run test:e2e 324/324 (322 + 2 new); npm run openapi:check OK; npm run lint OK.
Mutation check on the membership join: the new test fails when the active-set check is removed.
Not verified
Screen (tbank-sync.repository) and candle reserve are untouched and not refactored.
No production runtime check (only the SQLite test harness).
npm run verify as one run was not executed in full; its components were run separately.
Breaking Changes
None. No API or OpenAPI contract change.
## Summary
Removes the duplicated join predicate of sync reservations. Three repositories (membership access, instrument metadata, catalog discovery) repeated the same rule: join the same process's running attempt only while it is still active in memory. It now lives in one function. Behaviour is unchanged.
## Linked Issue
Closes WealthLedger/backend#119
## Scope decision
The Issue proposed a shared reserve skeleton. Only the join predicate is shared: the reserve blocks differ in scope key, intent, priority, pre-checks (membership refresh TTL, `findUniqueOrThrow`), and the `active` structure (Set vs Map). Passing all of that through one generic runner would be more code than the three duplicates. Candle join (raises priority, rewrites intent) and screen join (checks `progress.dispatched` and children) follow different rules and are intentionally not changed.
## Changes
- `tbank-access.service.ts`: new `isJoinableRunningAttempt` type guard, next to `TbankSyncAttempt`. Works with `Set` and `Map`.
- `tbank.api.ts`: re-export of the predicate (existing public surface, no new surface).
- `instrument-metadata.repository.ts`, `catalog-discovery.repository.ts`: use the predicate.
- `test/tbank-membership-reservation.e2e-spec.ts` (new): characterization on SQLite. Same-process join returns the same `id` and attempt; after the attempt leaves the active set a relaunch creates a new attempt; a foreign process never joins. A mutation that ignores the active set makes the test fail (checked locally).
## Verification
- Baseline on develop before changes: unit 310/310, e2e 322/322, `openapi:check` OK.
- After changes: `tsc` app and test configs: 0 errors; `npm test` 310/310; `npm run test:e2e` 324/324 (322 + 2 new); `npm run openapi:check` OK; `npm run lint` OK.
- Mutation check on the membership join: the new test fails when the active-set check is removed.
## Not verified
- Screen (`tbank-sync.repository`) and candle reserve are untouched and not refactored.
- No production runtime check (only the SQLite test harness).
- `npm run verify` as one run was not executed in full; its components were run separately.
## Breaking Changes
None. No API or OpenAPI contract change.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
Removes the duplicated join predicate of sync reservations. Three repositories (membership access, instrument metadata, catalog discovery) repeated the same rule: join the same process's running attempt only while it is still active in memory. It now lives in one function. Behaviour is unchanged.
Linked Issue
Closes WealthLedger/backend#119
Scope decision
The Issue proposed a shared reserve skeleton. Only the join predicate is shared: the reserve blocks differ in scope key, intent, priority, pre-checks (membership refresh TTL,
findUniqueOrThrow), and theactivestructure (Set vs Map). Passing all of that through one generic runner would be more code than the three duplicates. Candle join (raises priority, rewrites intent) and screen join (checksprogress.dispatchedand children) follow different rules and are intentionally not changed.Changes
tbank-access.service.ts: newisJoinableRunningAttempttype guard, next toTbankSyncAttempt. Works withSetandMap.tbank.api.ts: re-export of the predicate (existing public surface, no new surface).instrument-metadata.repository.ts,catalog-discovery.repository.ts: use the predicate.test/tbank-membership-reservation.e2e-spec.ts(new): characterization on SQLite. Same-process join returns the sameidand attempt; after the attempt leaves the active set a relaunch creates a new attempt; a foreign process never joins. A mutation that ignores the active set makes the test fail (checked locally).Verification
openapi:checkOK.tscapp and test configs: 0 errors;npm test310/310;npm run test:e2e324/324 (322 + 2 new);npm run openapi:checkOK;npm run lintOK.Not verified
tbank-sync.repository) and candle reserve are untouched and not refactored.npm run verifyas one run was not executed in full; its components were run separately.Breaking Changes
None. No API or OpenAPI contract change.