feat(issue): Issue-tracker fundament — schema, statusmappers en Forgejo-mirror-render #51

Merged
janpeter merged 3 commits from feat/issue-tracker into main 2026-08-17 07:19:55 +02:00
Owner

Fase 1 van het issue-tracker-plan (docs/superpowers/plans/2026-08-16-issue-tracker.md, dubbel-GO na 9 reviewrondes; spec docs/superpowers/specs/2026-08-16-issue-tracker-design.md rev 9, dubbel-GO na 7 rondes — beide in de Scrum4Me-repo).

De canonieke basis voor een issue-tracker per product of systeem: registratie, onderzoek en oplossing per issue, met een one-way spiegel naar Forgejo zodat de problemen op max2 en scrum4me-server ook daar zichtbaar zijn.

Wat hier landt

prisma/schema.prismaIssue + IssueLog, vijf enums, en Product.kind/Product.issue_code_counter. kind onderscheidt APP van SYSTEM; systemen krijgen dezelfde tracker als apps, wat het titelformaat en het host-label in de mirror bepaalt. Issue draagt naast de inhoud de mirror-boekhouding (forgejo_repo/number/synced_at/attempted_at/dirty/sync_seq/error) zodat de spiegel self-healing kan zijn, plus fingerprint + occurrence_count voor dedup en regressie-heropening. linked_pbi/linked_idea zijn SetNull met expliciete relatienamen: een verwijderde PBI mag de issue niet meeslepen.

lib/issue-status.ts — één bron voor UPPER_SNAKE ↔ lowercase, patroon van lib/task-status.ts. canTransitionIssue codificeert de lifecycle: binnen de open statussen vrij bewegen, sluiten kan altijd, en vanuit CLOSED alleen terug naar INVESTIGATING — heropenen is een nieuw onderzoek, geen sprong terug naar de inbox.

lib/issue-forgejo-mirror.ts — de pure render (titel/body/labels/state). Bewust zonder I/O: beide consumers doen hun eigen HTTP, maar het driftgevoelige deel heeft één bron. De body draagt een marker per issue-id zodat een bestaande spiegel herkenbaar blijft; bij overschrijding van de 60k-cap wordt afgekapt mét behoud van die marker, want zonder marker vindt de volgende sync het issue niet terug.

Consumers

Additief — geen bestaand gedrag verandert. Scrum4Me en scrum4me-mcp bumpen de submodule pas in fase 2 respectievelijk 6 van het plan; die lockstep is onderdeel van het plan.

Verificatie

npm run verify groen: 24 testbestanden, 257 tests, waarvan 15 nieuw (5 status-mappers, 10 mirror-render). Het canonieke schema valideert via een gegenereerd consumer-schema (scripts/gen-consumer-schema.sh); de canonieke vorm zelf heeft per constructie geen datasource.


🤖 Generated with Claude Code

Fase 1 van het issue-tracker-plan (`docs/superpowers/plans/2026-08-16-issue-tracker.md`, dubbel-GO na 9 reviewrondes; spec `docs/superpowers/specs/2026-08-16-issue-tracker-design.md` rev 9, dubbel-GO na 7 rondes — beide in de Scrum4Me-repo). De canonieke basis voor een issue-tracker per product of systeem: registratie, onderzoek en oplossing per issue, met een one-way spiegel naar Forgejo zodat de problemen op max2 en scrum4me-server ook daar zichtbaar zijn. ## Wat hier landt **`prisma/schema.prisma`** — `Issue` + `IssueLog`, vijf enums, en `Product.kind`/`Product.issue_code_counter`. `kind` onderscheidt APP van SYSTEM; systemen krijgen dezelfde tracker als apps, wat het titelformaat en het host-label in de mirror bepaalt. `Issue` draagt naast de inhoud de mirror-boekhouding (`forgejo_repo/number/synced_at/attempted_at/dirty/sync_seq/error`) zodat de spiegel self-healing kan zijn, plus `fingerprint` + `occurrence_count` voor dedup en regressie-heropening. `linked_pbi`/`linked_idea` zijn SetNull met expliciete relatienamen: een verwijderde PBI mag de issue niet meeslepen. **`lib/issue-status.ts`** — één bron voor UPPER_SNAKE ↔ lowercase, patroon van `lib/task-status.ts`. `canTransitionIssue` codificeert de lifecycle: binnen de open statussen vrij bewegen, sluiten kan altijd, en vanuit CLOSED alleen terug naar INVESTIGATING — heropenen is een nieuw onderzoek, geen sprong terug naar de inbox. **`lib/issue-forgejo-mirror.ts`** — de pure render (titel/body/labels/state). Bewust zonder I/O: beide consumers doen hun eigen HTTP, maar het driftgevoelige deel heeft één bron. De body draagt een marker per issue-id zodat een bestaande spiegel herkenbaar blijft; bij overschrijding van de 60k-cap wordt afgekapt mét behoud van die marker, want zonder marker vindt de volgende sync het issue niet terug. ## Consumers Additief — geen bestaand gedrag verandert. Scrum4Me en scrum4me-mcp bumpen de submodule pas in fase 2 respectievelijk 6 van het plan; die lockstep is onderdeel van het plan. ## Verificatie `npm run verify` groen: 24 testbestanden, 257 tests, waarvan 15 nieuw (5 status-mappers, 10 mirror-render). Het canonieke schema valideert via een gegenereerd consumer-schema (`scripts/gen-consumer-schema.sh`); de canonieke vorm zelf heeft per constructie geen datasource. --- 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Canonieke basis voor de issue-tracker per product of systeem: elk product
krijgt een eigen genummerde reeks issues (ISS-n) met onderzoek, oplossing en
een one-way spiegel naar Forgejo.

- vijf enums: ProductKind, IssueStatus, IssueResolution, IssueSeverity,
  IssueLogType;
- Product.kind onderscheidt APP van SYSTEM (max2, scrum4me-server), wat het
  titelformaat en het host-label in de mirror bepaalt; issue_code_counter
  draagt de per-product nummering;
- Issue draagt naast de inhoud de mirror-boekhouding (repo/number/synced_at/
  attempted_at/dirty/sync_seq/error) zodat de spiegel self-healing kan zijn,
  plus fingerprint + occurrence_count voor dedup en regressie-heropening;
- linked_pbi/linked_idea zijn SetNull met expliciete relatienamen, omdat een
  verwijderde PBI de issue niet mag meeslepen.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Eén bron voor de UPPER_SNAKE ↔ lowercase-vertaling van issues, zodat web, MCP
en de Forgejo-mirror niet elk hun eigen stringvergelijking krijgen — dezelfde
huisconventie als lib/task-status.ts.

canTransitionIssue codificeert de lifecycle uit spec §3: binnen de open
statussen mag je vrij bewegen, sluiten kan altijd, maar een gesloten issue
kan alleen terug naar INVESTIGATING. Heropenen is per definitie een nieuw
onderzoek, geen sprong terug naar de inbox.

REOPENABLE_RESOLUTIONS beperkt automatische regressie-heropening tot de
resoluties die een terugkerend symptoom écht tegenspreken; WONT_FIX,
DUPLICATE en INVALID tellen alleen op.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
feat(issue): pure Forgejo-mirror-render + boekhoudingsset
All checks were successful
CI / Verify (pull_request) Successful in 19s
fb76c9a2a4
De spiegel naar Forgejo is full-state: elke sync schrijft de complete
gerenderde vorm terug. Titel, body, labels en state zijn daarmee het
driftgevoelige deel, en dat hoort op één plek te staan — beide consumers
(Scrum4Me-web en scrum4me-mcp) doen hun eigen HTTP, maar renderen hetzelfde.
Vandaar een module zonder I/O.

De body draagt een marker per issue-id zodat een bestaande spiegel
herkenbaar blijft, ook nadat iemand in Forgejo heeft zitten typen. Bij
overschrijding van de 60k-cap wordt de inhoud afgekapt met een notitie, maar
de marker blijft staan: zonder marker zou de volgende sync het issue niet
meer terugvinden.

ISSUE_BOOKKEEPING_FIELDS staat bewust zónder forgejo_sync_seq. Dat veld
beweegt alleen in mutatietransacties, dus zijn aanwezigheid in changed_fields
is precies het signaal dat de UI méér moet doen dan een badge verversen.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
janpeter/scrum4me-shared!51
No description provided.