feat(issues): issue-tracker per product of systeem met Forgejo-mirror #174
No reviewers
Labels
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
janpeter/Scrum4Me!174
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "claude/issue-tracker-per-product-4720ca"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Implementatie van de issue-tracker per product of systeem. Spec
docs/superpowers/specs/2026-08-16-issue-tracker-design.md(dubbel-GO, 7 reviewrondes) en plandocs/superpowers/plans/2026-08-16-issue-tracker.md(dubbel-GO, 9 rondes), sprintS-2026-08-17-1/ PBI-143, 16 taken over 7 fasen — allemaal afgerond.Waarom
De problemen die op max2 en scrum4me-server optreden hadden nergens een plek. Ze worden nu Product-rijen met
kind = SYSTEMen krijgen dezelfde tracker als een app: registratie, onderzoek, oplossing — en een one-way spiegel naar Forgejo zodat ze daar zichtbaar zijn naast het gewone werk.Wat er landt
Data.
Issue+IssueLog(shared-first, submodule opc63f0bd) met migratie20260817090000_issue_tracker. Twee invarianten staan bewust in de database:closed_ataan elkaar — een gesloten issue zonder afhandeling is geen geldige toestand voor welk schrijfpad dan ook;issues_link_loss_dirtyvangt wat de applicatie per definitie niet ziet: een PBI- of Idea-delete zetlinked_*_idviaON DELETE SET NULLop NULL buiten alle app-code om. Zonder die trigger blijft de spiegel schoon-maar-verouderd achter, en dus ook buiten bereik van de sweep.De partial unique index op open fingerprints maakt dedup race-bestendig.
Mirror. Full-state en self-healing. Drie dingen dragen de correctheid: een session-scoped advisory lock op een pooler-vrije verbinding (hij moet de HTTP-calls overspannen, dus Prisma's transactiedeadline is te kort), alle render-data uit de read ná die lock, en een CAS op
forgejo_sync_seqdie alleen opschoont als de rij nog dezelfde versie draagt. Bij een botsing blijft dirty staan en blijftattempted_atonaangeroerd, zodat de sweep het issue meteen weer kiest. De boekhouding gaat via een pg-client en niet via Prisma —@updatedAtzou dechanged_fields-diff vervuilen waar de badge-only-tak op stuurt.Invarianten rond de mirror. De spiegel rendert meer dan het issue zelf: productnaam, soort en PBI-code. Een wijziging daar verandert dus de gespiegelde staat van issues die niemand heeft aangeraakt.
updateProductWithMirrorInvariantis nu het enige schrijfpad voor product-updates (alle vier de actions, bewaakt door een wiring-test) enupdatePbiActiondoet hetzelfde voorPbi.code. Beide vergelijken tegen de verse in-transactie-rij onder Serializable: onder READ COMMITTED zou een stale formulierwaarde "geen wijziging" kunnen concluderen terwijl er wél iets veranderde.Cron-gating.
enqueue-docs-auditendispatch-reviewsfilteren opkind: 'APP'. Een SYSTEM-product draagt de gedeelde infra-repo; daar hoort geen docs-audit of PR-review op los te gaan.UI. Globale lijst op
/issuesmet filters en realtime, plus een detailpagina waar de drie secties inline bewerken. Sluiten heeft een eigen dialoog omdat er een verplichte keuze bij hoort./issuesstaat inprotectedRoutes— zonder die regel was de pagina publiek.Realtime. Eigen SSE-route (geen
product_id-parameter; server-side gefilterd op de accessible-set), solo weert issue-events expliciet, en de hook coalesceert her-fetches binnen 250 ms zodat een sweep over N issues één fetch oplevert.Bijbehorende PR's
scrum4me-shared#51 + #52 — gemerged, submodule staat opc63f0bd.scrum4me-mcp#117 —create_issue/update_issue/list_issues/get_issue+ inline-sync. Nog open; de merge en de mcp-stable-release staan in het rollout-runbook.Verificatie
npm run verifygroen: 264 testbestanden, 2108 tests, waarvan 58 nieuw.npm run docsgroen (218 docs, 177 bestanden linkcheck).npm run buildis in een worktree bekend onhaalbaar (prisma-CLI in prebuild) en is niet gedraaid.Nog te doen — niet in deze PR
docs/runbooks/issue-tracker-rollout.md. De agent-users als ProductMember zetten is daar een preconditie, geen detail — zonder dat weigertuserCanAccessProductélkecreate_issue.migrate deploy).🤖 Generated with Claude Code
Verdict: REQUEST_CHANGES
geen gekoppeld plan gevonden — beoordeeld op codekwaliteit + product-standaarden.
Findings
prisma/schema.prisma:1537— De PR voegtHubPermissionRuleScope,HubPermissionRuleList,HubPermissionRuleOrigin,HubPermissionRuleStatusenmodel HubPermissionRuletoe aan het Prisma schema, maar de nieuwe migratieprisma/migrations/20260817090000_issue_tracker/migration.sqlmaakt alleen de issue-tracker types/tabellen aan. Daardoor is het Prisma schema niet meer conform de migratiegeschiedenis: een verse database die deze PR migreert mist dehub_permission_rules-tabel en bijbehorende enum-types, terwijl Prisma Client ze wel verwacht. Verplaats deze unrelated schema-wijziging naar de migratie die hem introduceert of voeg de ontbrekende migratie expliciet toe in deze PR.Verdict-log
REQUEST_CHANGES vanwege schema/migratie-drift. De issue-tracker bevat tests en documentatie, maar deze drift is blokkerend voor betrouwbare deploys en lokale setup.
Finding verwerkt in
9bf4316— de review had gelijk.Geverifieerd voordat ik iets aanpaste:
git show main:prisma/schema.prisma | grep -c HubPermissionRulegeeft 0, de branch geeft 10, en er bestond geen migratie methub_permission_rules. De drift is dus echt en komt van deze PR.Oorzaak: het consumer-schema is gegenereerd uit
vendor/scrum4me-shareden daarmee alles-of-niets. De submodule-bump die de issue-tracker-modellen ophaalt (54277fe→c63f0bd) nam ookHubPermissionRulemee, dat in scrum4me-shared #50 landde vóór de issue-PR. Scrum4Me had die consumer-migratie nooit gekregen.Toegevoegd:
prisma/migrations/20260815090000_hub_permission_rules/— die naam en dat tijdstempel staan zo in het shared-model-commentaar genoemd. De migratie draagt de twee invarianten die Prisma's schema-DSL niet kan uitdrukken en die het commentaar expliciet aan de consumer opdraagt:(scope, rule, list) WHERE repo_key IS NULL— de composite@@uniquededupt alleen PRODUCT-rijen, want Postgres beschouwt NULLs als onderling verschillend;((scope = 'GLOBAL') = (repo_key IS NULL)), die scope aan repo_key bindt.De tabel blijft leeg: de approval-rules-sync-feature zelf is nog niet gebouwd. Ordening klopt —
20260815…komt vóór20260817_issue_tracker, en geen van beide is op de deploys toegepast, dusmigrate deployblijft chronologisch.Vormtest
__tests__/db/hub-permission-rules-migration.test.tslegt beide invarianten vast.In dezelfde commit zit ook een bevinding uit de aparte security-review van spec §10: de ideeën-select op de issue-detailpagina filterde niet op
user_id. Issues zijn bewust team-zichtbaar, ideeën bewust niet — élke andere idea-query in de app scopet op gebruiker. Zonder die filter zag ieder productlid codes en titels van andermans ideeën. Ook die krijgt een regressietest.npm run verifygroen: 266 testbestanden, 2114 tests.Verdict: APPROVED
Geen gekoppeld plan gevonden — beoordeeld op codekwaliteit + product-standaarden.
Findings
Review-notities
productAccessFilter, transactionele mutaties enrevalidatePath.CRON_SECRETvoor cron. Dat wijkt niet problematisch af van de gelezen route-handlerdoc omdat dit geen publieke REST-consumer endpoint is.forgejo_sync_seqCAS, failure-registratie en tests voor stale/failure/lock-paden.