docs(10-02): complete Ausschreibungs-Radar marketplace registration plan
This commit is contained in:
@@ -51,7 +51,7 @@
|
||||
|
||||
### CONFIG — Modul & Verwaltung
|
||||
|
||||
- [ ] **CONFIG-01**: Das Modul ist im Marketplace registriert und pro Mandant aktivierbar (wie DKV-Fleet- und Cert-Manager-Modul).
|
||||
- [x] **CONFIG-01**: Das Modul ist im Marketplace registriert und pro Mandant aktivierbar (wie DKV-Fleet- und Cert-Manager-Modul).
|
||||
- [ ] **CONFIG-02**: Admin verwaltet Quellen-Poll-Konfiguration sowie das E-Mail-Ingestion-Postfach pro Mandant (Zugangsdaten verschlüsselt gespeichert).
|
||||
- [ ] **CONFIG-03**: Die gesamte Modul-UI ist mehrsprachig (Deutsch + Englisch, i18n).
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
|
||||
| Requirement | Phase | Status |
|
||||
|-------------|-------|--------|
|
||||
| CONFIG-01 | Phase 10 | Pending |
|
||||
| CONFIG-01 | Phase 10 | Complete |
|
||||
| INGEST-01 | Phase 10 | Pending |
|
||||
| INGEST-06 | Phase 10 | Pending |
|
||||
| SCHEMA-01 | Phase 10 | Complete |
|
||||
|
||||
@@ -344,7 +344,7 @@ Plans:
|
||||
4. DÖE is polled once on a shared, admin-configurable interval regardless of how many tenants have the module active -- never once per tenant (poll-once-fan-out-many, not the DKV single-tenant `findFirst()` pattern)
|
||||
5. Activating the module for a second tenant does not duplicate ingestion, re-trigger a redundant DÖE poll, or interfere with the first tenant's data
|
||||
|
||||
**Plans**: 1/6 plans executed
|
||||
**Plans**: 2/6 plans executed
|
||||
|
||||
**Wave 1**
|
||||
|
||||
@@ -352,7 +352,7 @@ Plans:
|
||||
|
||||
**Wave 2** *(blocked on Wave 1 completion)*
|
||||
|
||||
- [ ] 10-02-PLAN.md — Marketplace registration slice: self-seed tender-radar module, module-loader whitelist entry, placeholder page, singleton doe-opendata poll config (CONFIG-01)
|
||||
- [x] 10-02-PLAN.md — Marketplace registration slice: self-seed tender-radar module, module-loader whitelist entry, placeholder page, singleton doe-opendata poll config (CONFIG-01)
|
||||
|
||||
**Wave 3** *(blocked on Wave 2 completion)*
|
||||
|
||||
@@ -453,7 +453,7 @@ Phases execute in numeric order: 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> 10
|
||||
| 7. DKV Fleet Module | 6/6 | Complete | 2026-06-27 |
|
||||
| 8. Dashboard Widgets Vollimplementierung | 4/4 | Complete | 2026-07-01 |
|
||||
| 9. Cert Manager Module | 6/6 | Complete | 2026-07-02 |
|
||||
| 10. Ausschreibungs-Radar Foundation & DÖE Ingestion | 1/6 | In Progress| |
|
||||
| 10. Ausschreibungs-Radar Foundation & DÖE Ingestion | 2/6 | In Progress| |
|
||||
| 11. Filter Engine, Results UI & Saved Searches | 0/TBD | Not started | - |
|
||||
| 12. Tender Notifications | 0/TBD | Not started | - |
|
||||
| 13. Scraping Adapters & Cross-Source Deduplication | 0/TBD | Not started | - |
|
||||
|
||||
+11
-7
@@ -5,15 +5,15 @@ milestone_name: Ausschreibungs-Radar
|
||||
current_phase: 10
|
||||
current_phase_name: ausschreibungs-radar-foundation-d-e-ingestion
|
||||
status: executing
|
||||
stopped_at: Completed 10-01-PLAN.md
|
||||
last_updated: "2026-07-21T08:39:50.924Z"
|
||||
stopped_at: Completed 10-02-PLAN.md
|
||||
last_updated: "2026-07-21T08:46:07.001Z"
|
||||
last_activity: 2026-07-21
|
||||
last_activity_desc: Phase 10 execution started
|
||||
progress:
|
||||
total_phases: 14
|
||||
completed_phases: 8
|
||||
total_plans: 46
|
||||
completed_plans: 40
|
||||
completed_plans: 41
|
||||
percent: 57
|
||||
---
|
||||
|
||||
@@ -29,7 +29,7 @@ See: .planning/PROJECT.md (updated 2026-07-17)
|
||||
## Current Position
|
||||
|
||||
Phase: 10 (ausschreibungs-radar-foundation-d-e-ingestion) — EXECUTING
|
||||
Plan: 2 of 6
|
||||
Plan: 3 of 6
|
||||
Status: Ready to execute
|
||||
Last activity: 2026-07-21 — Phase 10 execution started
|
||||
|
||||
@@ -72,6 +72,7 @@ Progress: [░░░░░░░░░░] 0%
|
||||
| Phase 09-cert-manager-module P05 | 8 | 3 tasks | 6 files |
|
||||
| Phase 09 P06 | 7 | 3 tasks | 7 files |
|
||||
| Phase 10 P01 | 15min | 3 tasks | 4 files |
|
||||
| Phase 10 P02 | 20min | 3 tasks | 6 files |
|
||||
|
||||
## Accumulated Context
|
||||
|
||||
@@ -140,6 +141,9 @@ Recent decisions affecting current work:
|
||||
- [Phase ?]: splitCerts PEM path reuses parsePemChain; P7B sniffs first bytes
|
||||
- [Phase ?]: splitCerts format crt comparison removed — detectFormat returns pem|der|pfx|p7b only
|
||||
- [Phase 10]: Tender ist plattform-global (D-03): kein tenantId, keine RLS/forTenant() — Verhindert versehentliches Ausblenden globaler Daten fuer einen zweiten Mandanten
|
||||
- [Phase 10-02]: category: 'procurement' fuer Ausschreibungs-Radar im Marketplace gewaehlt (freies kebab-case, keine Enum-Beschraenkung)
|
||||
- [Phase 10-02]: Singleton doe-opendata Poll-Config wird direkt in TendersModule.onModuleInit() upserted, isActive:true per Default (D-04)
|
||||
- [Phase 10-02]: Platzhalter-Seite tender-radar/page.tsx nutzt hartkodierten deutschen Text statt next-intl (volle i18n ist CONFIG-03, Phase 14)
|
||||
|
||||
### Pending Todos
|
||||
|
||||
@@ -177,7 +181,7 @@ Items acknowledged and carried forward from previous milestone close:
|
||||
|
||||
## Session Continuity
|
||||
|
||||
Last session: 2026-07-21T08:39:50.913Z
|
||||
Stopped at: Completed 10-01-PLAN.md
|
||||
Resume file: .planning/phases/10-ausschreibungs-radar-foundation-d-e-ingestion/10-CONTEXT.md
|
||||
Last session: 2026-07-21T08:46:06.990Z
|
||||
Stopped at: Completed 10-02-PLAN.md
|
||||
Resume file: None
|
||||
Last activity: 2026-07-14 - Built LDAP per-user exclude/denylist filter (9d1323f), migration applied on live DB, verified via Playwright: sync deactivated 4 excluded service accounts (administrator/krbtgt/guest/dns-ldap), 2 real LDAP users stay active, 0 wrongly created
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
---
|
||||
phase: 10-ausschreibungs-radar-foundation-d-e-ingestion
|
||||
plan: 02
|
||||
subsystem: api
|
||||
tags: [nestjs, module-registry, prisma, next.js, module-loader, tender-radar, marketplace]
|
||||
|
||||
# Dependency graph
|
||||
requires:
|
||||
- phase: 10-01 (foundation & dependencies)
|
||||
provides: Tender / TenderSourcePollConfig Prisma models, applied migration
|
||||
provides:
|
||||
- Self-seeding TendersModule registered in app.module.ts imports (marketplace visibility, CONFIG-01)
|
||||
- Singleton doe-opendata TenderSourcePollConfig row upserted idempotently on boot
|
||||
- Web MODULE_REGISTRY['tender-radar'] whitelist entry + minimal placeholder page
|
||||
- Unit-test coverage proving the registry seed call shape
|
||||
affects: [10-03 adapter/normalizer, 10-04 ingestion/scheduler, 10-05 controller/DTOs, phase 11 saved searches UI]
|
||||
|
||||
# Tech tracking
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns:
|
||||
- "Self-seeding NestJS module via OnModuleInit (mirrors DkvModule/CertManagerModule)"
|
||||
- "Singleton platform-global config upsert (sourceType @unique) inside module bootstrap, no forTenant()"
|
||||
- "Frontend module-loader security whitelist entry required alongside DB seed for a module's page to resolve"
|
||||
|
||||
key-files:
|
||||
created:
|
||||
- apps/api/src/tenders/tenders.module.ts
|
||||
- apps/api/src/tenders/tenders.seed.ts
|
||||
- apps/api/src/tenders/tenders.seed.spec.ts
|
||||
- apps/web/src/app/(portal)/modules/tender-radar/page.tsx
|
||||
modified:
|
||||
- apps/api/src/app.module.ts
|
||||
- apps/web/src/lib/module-loader.ts
|
||||
|
||||
key-decisions:
|
||||
- "category: 'procurement' chosen for the marketplace card — free-form kebab-case per existing seed convention (dkv='fleet', cert-manager='security-tools'), no enum constraint in seedModule or the marketplace UI"
|
||||
- "Singleton doe-opendata poll config upserted directly inside TendersModule.onModuleInit() via injected PrismaService, not via a separate ingestion service (Plans 03-04 add ingestion machinery, not this plan)"
|
||||
- "isActive: true by default on the singleton so the platform-global DÖE poll runs regardless of any tenant's per-tenant activation state (D-04); day-cursor gating (Plan 04) makes ticks idempotent before that logic exists"
|
||||
- "Placeholder page.tsx uses a hardcoded German string, not next-intl — full i18n is CONFIG-03 (Phase 14); documented inline as an intentional MVP stub to be replaced in Phase 11"
|
||||
|
||||
requirements-completed: [CONFIG-01]
|
||||
|
||||
coverage:
|
||||
- id: D1
|
||||
description: "TendersModule self-seeds 'tender-radar' into the module registry on boot (isSystem: true, bilingual description) so an admin can find it in the marketplace, same as DKV/Cert-Manager"
|
||||
requirement: "CONFIG-01"
|
||||
verification:
|
||||
- kind: unit
|
||||
ref: "apps/api/src/tenders/tenders.seed.spec.ts#calls moduleRegistryService.seedModule once with tender-radar slug"
|
||||
status: pass
|
||||
- kind: integration
|
||||
ref: "cd apps/api && pnpm exec tsc --noEmit -p tsconfig.json; grep -c TendersModule src/app.module.ts == 2"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: D2
|
||||
description: "Activating the module opens /modules/tender-radar instead of 404, because the module-loader whitelist entry exists"
|
||||
requirement: "CONFIG-01"
|
||||
verification:
|
||||
- kind: integration
|
||||
ref: "cd apps/web && grep -c tender-radar src/lib/module-loader.ts >=1; test -f 'src/app/(portal)/modules/tender-radar/page.tsx'; pnpm exec tsc --noEmit"
|
||||
status: pass
|
||||
human_judgment: true
|
||||
rationale: "Whitelist entry and file existence are statically verified, but actually clicking Activate in the marketplace and confirming the page renders (rather than 404s) in a live browser session was not exercised in this run — recommend a quick UAT pass alongside Plan 03-05 verification."
|
||||
- id: D3
|
||||
description: "Singleton doe-opendata TenderSourcePollConfig row is seeded exactly once per boot (idempotent upsert), no per-tenant duplication possible (sourceType @unique)"
|
||||
requirement: "CONFIG-01"
|
||||
verification:
|
||||
- kind: unit
|
||||
ref: "schema.prisma sourceType @unique constraint (10-01) + upsert-by-sourceType in tenders.module.ts onModuleInit"
|
||||
status: pass
|
||||
human_judgment: true
|
||||
rationale: "DB-level uniqueness and upsert code are statically verifiable; actually restarting the live API container twice and confirming the row count stays at 1 was not exercised in this run (local Docker deploy/restart is left to the user per project convention)."
|
||||
|
||||
# Metrics
|
||||
duration: ~20min
|
||||
completed: 2026-07-21
|
||||
status: complete
|
||||
---
|
||||
|
||||
# Phase 10 Plan 02: Ausschreibungs-Radar Marketplace Registration Summary
|
||||
|
||||
**Self-seeding `TendersModule` (slug `tender-radar`, `isSystem: true`) registered in `app.module.ts`, singleton `doe-opendata` poll config upserted on boot, and a whitelisted `/modules/tender-radar` placeholder page — the first user-facing vertical slice of CONFIG-01.**
|
||||
|
||||
## Performance
|
||||
|
||||
- **Duration:** ~20 min
|
||||
- **Started:** 2026-07-21
|
||||
- **Completed:** 2026-07-21
|
||||
- **Tasks:** 3 (all auto, no checkpoints)
|
||||
- **Files modified:** 6 (3 created API, 1 created web, 2 modified)
|
||||
|
||||
## Accomplishments
|
||||
- `TendersModule` self-seeds the marketplace registry via `seedTendersModule()`, mirroring the exact `DkvModule`/`CertManagerModule` `OnModuleInit` pattern (self-seed in try/catch with Logger).
|
||||
- Singleton `doe-opendata` `TenderSourcePollConfig` row is upserted directly in `TendersModule.onModuleInit()` using the injected (non-tenant-extended) `PrismaService` — no `forTenant()`, matching the D-03 global-data invariant established in Plan 01.
|
||||
- `TendersModule` added to `app.module.ts` imports, alongside `DkvModule`/`CertManagerModule`/`FavoritesModule`.
|
||||
- Web `MODULE_REGISTRY['tender-radar']` whitelist entry added — mandatory per the module-loader's own security-whitelist comment (arbitrary slugs 404 without it, even when activated in the DB).
|
||||
- Minimal `tender-radar/page.tsx` client component created — proves the activation → lazy-load → page-render path end-to-end; real results UI is deferred to Phase 11.
|
||||
- Unit test (`tenders.seed.spec.ts`) asserts the registry seed call shape (slug, `isSystem: true`, bilingual `de`/`en` description) — Wave 0 automated proof for CONFIG-01.
|
||||
|
||||
## Task Commits
|
||||
|
||||
Each task was committed atomically:
|
||||
|
||||
1. **Task 1: TendersModule skeleton + registry self-seed + singleton poll config** - `e7b4094` (feat)
|
||||
2. **Task 2: Web module-loader whitelist entry + minimal portal page** - `3292afb` (feat)
|
||||
3. **Task 3: Seed registration test (Wave 0 coverage for CONFIG-01)** - `9a7ed71` (test)
|
||||
|
||||
**Plan metadata:** see final `docs(10-02)` commit.
|
||||
|
||||
## Files Created/Modified
|
||||
- `apps/api/src/tenders/tenders.seed.ts` - exports `seedTendersModule()`, upserts `tender-radar` into the registry (`isSystem: true`, category `procurement`, bilingual description)
|
||||
- `apps/api/src/tenders/tenders.module.ts` - `TendersModule` with `OnModuleInit`; calls `seedTendersModule()` and upserts the singleton `doe-opendata` poll config (both wrapped in independent try/catch + Logger)
|
||||
- `apps/api/src/tenders/tenders.seed.spec.ts` - Vitest coverage for the seed call shape
|
||||
- `apps/api/src/app.module.ts` - `TendersModule` added to imports
|
||||
- `apps/web/src/lib/module-loader.ts` - `'tender-radar'` entry added to `MODULE_REGISTRY`
|
||||
- `apps/web/src/app/(portal)/modules/tender-radar/page.tsx` - minimal placeholder page (hardcoded German string, `'use client'`, default export)
|
||||
|
||||
## Decisions Made
|
||||
- **`category: 'procurement'`** — confirmed free-form (no enum constraint in `seedModule()` or the marketplace filter UI, which derives categories via `[...new Set(modules.map(m => m.category))]`); follows existing kebab-case-ish convention (`fleet`, `security-tools`, `domain-tools`).
|
||||
- **Poll config upsert lives in the module, not a service** — Plans 03-04 haven't added `TenderIngestionService` yet; the plan explicitly scoped the singleton seed to `TendersModule.onModuleInit()` via injected `PrismaService` for this wave.
|
||||
- **`isActive: true` by default** — per plan instruction (D-04): the platform-global poll should run regardless of tenant activation state; idempotency is guaranteed by `sourceType @unique` plus the (future) day-cursor gate.
|
||||
- **Hardcoded German placeholder string** — explicit MVP stub per plan instruction; not wired to next-intl since full i18n is CONFIG-03 (Phase 14). Documented inline in the page component's doc comment so Phase 11 authors know to replace the whole component rather than just the string.
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
None - plan executed exactly as written.
|
||||
|
||||
## Issues Encountered
|
||||
|
||||
None.
|
||||
|
||||
## Known Stubs
|
||||
|
||||
- `apps/web/src/app/(portal)/modules/tender-radar/page.tsx` - Hardcoded German placeholder text ("Ausschreibungen werden erfasst.") with no real data source wired. This is an intentional, explicitly-planned MVP stub (plan Task 2 action text) proving the activation → page-load path only. Resolved by Phase 11 (results/search UI) and Phase 14 (full i18n via next-intl message keys).
|
||||
|
||||
## User Setup Required
|
||||
|
||||
None - no external service configuration required. Local Docker stack (`tessera-ctl-api-1`) was left running unmodified; restarting/rebuilding it to pick up these code changes is left to the user per project convention.
|
||||
|
||||
## Next Phase Readiness
|
||||
- CONFIG-01 vertical slice complete: module seeds into the registry, is admin-activatable via the existing Marketplace mechanism (`ModuleRegistryService`/`TenantModuleActivation`, unchanged), and its page resolves once the whitelist entry is registered.
|
||||
- `TendersModule` is a clean extension point: `controllers: []` and `providers: []` are placeholders ready for Plan 05 (controller/DTOs) and Plans 03-04 (adapter/normalizer/scheduler/ingestion services) to populate.
|
||||
- No blockers for Plan 10-03.
|
||||
|
||||
## Self-Check: PASSED
|
||||
|
||||
- FOUND: apps/api/src/tenders/tenders.module.ts
|
||||
- FOUND: apps/api/src/tenders/tenders.seed.ts
|
||||
- FOUND: apps/api/src/tenders/tenders.seed.spec.ts
|
||||
- FOUND: apps/web/src/lib/module-loader.ts
|
||||
- FOUND: apps/web/src/app/(portal)/modules/tender-radar/page.tsx
|
||||
- FOUND: apps/api/src/app.module.ts
|
||||
- FOUND commit: e7b4094
|
||||
- FOUND commit: 3292afb
|
||||
- FOUND commit: 9a7ed71
|
||||
|
||||
---
|
||||
*Phase: 10-ausschreibungs-radar-foundation-d-e-ingestion*
|
||||
*Completed: 2026-07-21*
|
||||
Reference in New Issue
Block a user