14 KiB
phase, plan, subsystem, tags, requires, provides, affects, actuals, tech-stack, key-files, key-decisions, patterns-established, requirements-completed, coverage, duration, completed, status
| phase | plan | subsystem | tags | requires | provides | affects | actuals | tech-stack | key-files | key-decisions | patterns-established | requirements-completed | coverage | duration | completed | status | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| quick-260909-laa | 01 | database |
|
|
|
|
|
|
|
|
|
|
|
45min | 2026-09-09 | complete |
Quick Task 260909-laa: Etappe 2 Bereich tenders Summary
Five tender user-CRUD services and the per-hit halves of two background notification services bound to forTenant(), with the platform-wide catalog, two fan-out adapters, and three WINDOWS-#19-affected RSS paths deliberately left unbound and documented as such.
Performance
- Duration: ~45 min
- Started: 2026-09-09T13:20:00Z (approx.)
- Completed: 2026-09-09T14:03:00Z
- Tasks: 3
- Files modified: 20 (13 source/spec files + 2 docs, across three commits)
Accomplishments
- Measured the three special cases of this area against the delivered
_rls_remaining_tenant_tablesmigration (32/32 scratch checks): the five policies have no user dimension, a platform-wide RSS row is invisible under every tenant context, and a boundupsertonto an invisible cross-tenant row fails on the unique constraint, not the policy. - Bound the five per-user CRUD services (
tender-saved-search,tender-triage,tender-notification-pref,tender-email-config,tender-rss-feed) toforTenant();tender-rss-feed.service.tsbinds onlycreateForUserand leaveslistForUser/createPlatform/removedeliberately unbound with a WINDOWS #19 code comment, because they touch the nullable-tenant platform-wide row. - Bound the per-hit halves of both background notification services (
tender-digest.scheduler.ts,tender-matching.service.ts) to the tenant of the candidate/profile row currently being processed; their cross-tenant candidate/profile queries stay unbound and are commented as a Stage 3 handoff. - Wrote a
## Bereich tenderssection into the critique document with the observed scratch-tool output, a per-path signal table, and a dedicated subsection for this area's new failure form: two notification paths that, on too little read, send nothing — silently. - Kept
docs/mandantentrennung-zugriffsklassifikation.mdin sync withrls-access-inventory.spec.tsfor all 23 tenders pairs, including reclassifyingtenderRssFeedSourcefrommuss-mandantengebundentobeides.
Task Commits
Each task was committed atomically:
- Task 1: Measure the three special cases and write the tenders failure-direction section -
3498147(feat) - Task 2: Bind the five user-CRUD services and thread tenantId through the controller -
3336a6e(feat) - Task 3: Bind the per-hit halves of both background services and close both documents -
df5c5b7(feat)
Plan metadata: committed separately by the orchestrator after this summary.
Note: all three tasks were TDD-flavored (test proof before/alongside the binding change), single commit per task since the two-client proof and the binding change belong to the same logical unit.
Files Created/Modified
apps/api/scripts/rls-scratch-check.mjs— newrunTendersAreaCheckssection (9 checks: no user dimension, WINDOWS #19 invisibility + rejected insert, P2002 on invisible-row upsert), wired intomain()afterrunGroupsAreaChecksdocs/mandantentrennung-etappe2-fehlerrichtung.md— new## Bereich tenderssection (t1–t5), plus a Task 3 nachtrag closing the per-hit halvesdocs/mandantentrennung-zugriffsklassifikation.md— Stand column for 5 pairs updated in Task 2, 5 more in Task 3; area overview table, class distribution, and "Der Hintergrunddienst als Falle" section all re-measured and updatedapps/api/src/tenders/tender-saved-search.service.ts—list/update/removebind viaforTenant(), gainedtenantIdparameterapps/api/src/tenders/tender-triage.service.ts—listForUser/favoriteIdsbind viaforTenant(), gainedtenantIdparameterapps/api/src/tenders/tender-notification-pref.service.ts—getForUserbinds,setForUsertranslates P2002 into a GermanConflictExceptionapps/api/src/tenders/tender-email-config.service.ts—getConfigForApi/testConnectionbind and gainedtenantId;saveConfig's internal read now also binds; P2002 translatedapps/api/src/tenders/tender-rss-feed.service.ts—createForUserbinds;listForUser/createPlatform/removestay unbound with WINDOWS #19 commentsapps/api/src/tenders/tenders.controller.ts— eight call sites threadtenantIdfromextractTriageContextinto the newly-parameterized service methodsapps/api/src/tenders/tender-digest.scheduler.ts— candidate query selects denormalizedtenantId; per-candidate loop binds pref/match/user/stampapps/api/src/tenders/tender-matching.service.ts— per-profile loop binds match upsert and the instant-dispatch fresh/user/stamp accesses- All corresponding
.spec.tsfiles —__makeBoundClient()two-client proof, per-method binding tests, gegentest for the three intentionally-unbound RSS paths, silent-failure tests for both background services
Decisions Made
tender-rss-feed.service.ts/tenderRssFeedSourcereclassified frommuss-mandantengebundentobeidesin the classification doc — mirrors theldapConfigprecedent from 260909-ipc, no behavior change, just a more accurate class.- No
withTenantTransaction()introduced anywhere in this area: Task 1 measured exactly one$transactioninapps/api/src/tenders(array form, on the platform-globalTendertable intender-fingerprint-backfill.service.ts), outside any tenant binding — the extension header's mandated re-check for a new transactional case is answered with "no new case," not assumed. - The digest scheduler's candidate query now additionally selects the match row's denormalized
tenantIdso the per-row loop can bind at all; the edge case of a user having matches under two different tenants (a stale denormalized value after a tenant switch) is named in code and in both docs, not solved — explicitly Stage 3's problem.
Deviations from Plan
None — plan executed exactly as written. The one place where execution diverged from the plan's literal wording (Befund C's "zehn Aufrufstellen") is a clarification, not a deviation: of the ten controller call sites that today discard tenantId, only eight actually needed the parameter threaded through, because the two RSS call sites (listRssFeeds, removeRssFeed) call service methods (listForUser, remove) that deliberately stay unbound and therefore never gained a tenantId parameter. This is the same "a raw count is a claim, not a finding" lesson the plan itself calls out repeatedly (Befund C is analogous to the 62-vs-61 raw-hit correction) — verified by re-reading each of the ten call sites individually rather than trusting the count.
Issues Encountered
- The
rls-access-inventory.spec.tsdoc-vs-source consistency check failed after Task 2's and Task 3's binding changes, as expected since the check runs on every test invocation — the classification doc's Stand column was updated within the same task (not deferred to a later pass) so every task's own verification stayed self-contained and green. - TypeScript flagged two implicit-
anyparameters intender-matching.service.tsaftertenantPrisma(typedany) replacedthis.prismaas the receiver for two.map()calls — fixed with explicit inline parameter types (match: { tender: unknown },match: { id: string }).
User Setup Required
None — no external service configuration required. DATABASE_URL remains on the tessera role with BYPASSRLS; the switch stays off.
Next Phase Readiness
- The
tendersarea is now at a mixed-but-fully-documented state: 5 pairs fully bound, 1 pair (tenderRssFeedSource) mixed with a code-level WINDOWS #19 boundary, 2 pairs (tenderMatch/userin the two background services split acrossbeides/gemischt) with their per-hit halves closed and cross-tenant halves named as Stage 3 handoffs, and 12 pairs deliberately untouched (D-03 catalog + fan-out adapters). - Stage 3 inherits: the WINDOWS #19 policy fix for
TenderRssFeedSource/SearchProvider, the cross-tenant halves of the two background services (with the documented tenant-switch edge case), and thereq.tenantPrismaarchitecture question (still undecided, as in every prior area). - Stage 4 (cutover) preflight inherits Befund K:
tender-mail.service.tsdepends onSettingsService.getDecryptedSmtpConfig(tenantId), andsettings.service.ts(4 raw hits) is still fully unbound — after cutover this would silently stop all outbound mail. Recorded in the critique doc, not solved here. - The classification doc's area overview now shows
tendersat 36 ungebunden / 26 gebunden (was 62/0); remaining areas at their prior stand:dkv(21),user(17),module-registry(17),dashboard(13),calendar(12),tenant(8),favorites(7),settings(4) — all still fully untouched, the largest remaining pool of work for whichever Stage 2 area comes next.
Phase: quick-260909-laa Completed: 2026-09-09
Self-Check: PASSED
All 12 referenced artifact files found on disk; all 3 task commit hashes (3498147, 3336a6e, df5c5b7) found in git history.