Repository navigation
fix(#2732): group/mission completion cascade + self-heal - #2744
Merged
Merged
Conversation
…indUnhealthyEntities)
…n, flagged on empty slots
…ath item
Wires after(['CREATE','UPDATE','DELETE'], 'CompletionPathItems') and
after(['CREATE','UPDATE','DELETE'], 'GroupPathItems') hooks in AdminService.
Each hook resolves the parent MISSION/GROUP legacyId and fire-and-forgets
reconcileEntityForUsers via setImmediate + its own cds.tx so a reconcile
fault never fails the author's SAVE.
DELETE parent resolution: a matching before('DELETE') hook captures path_ID
(or group_ID) from req.query.DELETE.where[2].val before the row is removed;
the after hook reads it from req._cpiPathId / req._gpiGroupId.
Tests: two integration tests verify the DELETE path via AdminService.tx()
with mocked Admin user (pattern from test/pat-admin-grant.test.js) — one
asserts a partial-completion mission heals to COMPLETED after the blocking
item is removed; one asserts the fire-and-forget never throws.
…tions
CAP fires SAVE on the root active entity (Missions/Groups) on draftActivate,
not on composition children -- so the child CompletionPathItems/GroupPathItems
after(CREATE/UPDATE) hooks never fire for normal Fiori edit-and-save. Fix:
add after('SAVE','Missions') and after('SAVE','Groups') root hooks that read
legacyId from req.data, fire-and-forget reconcileEntityForUsers. On draft-
enabled entities SAVE fires specifically on draftActivate; on non-draft active
entities it is a shorthand for CREATE+UPDATE -- both production paths covered.
Also:
- Replace 150ms setTimeout settle with vi.waitFor() polling in tests
- Add draftActivate end-to-end test (full HTTP flow) for the SAVE path
- Tighten fail-open test assertion to expect status === 'COMPLETED' (no change)
rather than accepting either COMPLETED or IN_PROGRESS
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #2732 (root cause of #2718 / #2719 / #2722).
Group/Mission completion was a stored, event-triggered
TaskRecordthat was never recomputed on read and did not self-heal when completion paths changed. This makes it self-healing.What changed
upsertRollupRecordgainsallowDowngrade, default false; opt-intrueonly on the explicit tutorial reset/supersede path). Matches the legacy IMS short-circuit.loadGroupSlots/loadMissionSlots) so a retired tutorial no longer counts as an unsatisfiable required slot — thecp-aibus-dox-booster-appclass of breakage.rollup.empty_slots.srv/lib/completion-reconcile.js:reconcileEntityForUsersrecomputes a mission/group's GROUP/MISSION records for affected users (candidate set = existing parent-record holders ∪ users who already satisfy the slots), so a repaired mapping newly grants completion and a stale record is corrected — without demoting historical completers.after('SAVE', 'Missions'/'Groups')root hooks (the Fiori draft-activate path) + childDELETEhooks fire a fire-and-forget reconcile. Never fails the author's SAVE.reconcile-completionsjob (reconcileAll) sweeps all published missions/groups — the backstop that repairs the ~50 pre-existing dead-slot entities and keeps them healed.AdminService.completionHealth()— on-demand admin function listing missions/groups whose paths contain an INACTIVE/unresolvable tutorial, so authors can find and fix broken mappings.Stored GROUP/MISSION
TaskRecordsremain the source of truth (analytics +/build/my-progressdepend on them); this repairs drift rather than deriving on read. No schema change. All new behavior is fail-open.Testing
npx cds compile srv --to sqlexits 0.completion-reconcile.jsis not acontent-store.jsdependency — no.deploy/mta.yamlchange.Post-deploy (per spec Rollout — "test the actual thing")
After DEV deploy: run
reconcile-completionsvia adminrunJob, spot-check missions 12479 / 12052 / 16326 complete for users who earned them, and opencompletionHealthto confirm it lists the INACTIVE-slot missions.Design:
docs/superpowers/specs/2026-10-13-group-mission-cascade-selfheal-design.mdPlan:
docs/superpowers/plans/2026-10-13-group-mission-cascade-selfheal.md