Skip to content

feat(#2990): mission completion-path ordering - #3001

Merged
jung-thomas merged 9 commits into
DEVfrom
worktree-mission-path-ordering-2990
Nov 2, 2026
Merged

jung-thomas merged 9 commits into
DEVfrom
worktree-mission-path-ordering-2990

Conversation

@jung-thomas

Copy link
Copy Markdown
Contributor

Closes #2990.

What

Lets Mission admins set the display order of a mission's Completion Paths, and makes every learner-facing surface honour that order. Mirrors the existing CompletionPathItems.itemOrder pattern.

Changes

  • Schema: new CompletionPaths.pathOrder : Integer (db/schema.cds).
  • Admin UI (app/admin-annotations.cds): editable "Order" column (first) + PresentationVariant.SortOrder on CompletionPaths.
  • Auto-numbering (srv/admin-service.js): setPathOrder before-NEW/CREATE handler gap-numbers new paths (max(pathOrder)+10 per mission), mirroring setGroupItemOrder. Carried through copyMission (preserves source order) and promoteCommunityToMission (seeds 10).
  • Render ordering — 5 read sites now sort by coalesce(pathOrder, 999999), name so NULLs sort last identically on HANA and SQLite:
    • packages/content/catalog-data.js (mission SSR)
    • srv/lib/build-catalog.js (catalog, mission_ID first)
    • srv/developer-service.js — getAppSpaceProgress (/app-space/ page) + getEventProgress
    • srv/lib/branch/mission-detail.js (branching paths[0])
  • Backfill (scripts/backfill-completion-path-order.cjs): one-shot, per-mission alphabetical-by-name gap-numbering of existing NULL rows; --dry-run/--commit/--verify-only; snapshot-before-write; idempotent. npm script backfill-completion-path-order.

Design decisions (confirmed with maintainer)

  • Existing rows backfilled alphabetically by name per mission.
  • On branching missions, the primary path (paths[0]) becomes the alphabetically-first path on deploy — accepted one-time shift.
  • Event analytics bars (event-path-buckets.js) intentionally keep their completion-count sort — different surface, not changed.

Tests

  • Unit (in-memory SQLite): schema field; setPathOrder gap-numbering + explicit-preserve; copyMission carry; path ordering at app-space / catalog / mission-detail (each asserts NULL sorts last — would fail under a plain orderBy on SQLite); backfill A=10/B=20/C=30 + idempotency + base-extension. All green.
  • Hybrid (test/hybrid/backfill-completion-path-order.test.js): runs at deploy (needs cf login + HANA).

Deploy steps (required for the feature to take effect)

  1. Full npm run deploy -- --env dev (NO --skip-build / -m — admin UI + content bundle are build-gated).
  2. npm run backfill-completion-path-order -- --commit against DEV HANA (then --verify-only -> exit 0).
  3. gh workflow run rebuild-content.yml -f mode=full so mission SSR/catalog BLOBs bake the new order.
  4. ALLOW_HYBRID_WRITES=true npm run test:hybrid -- backfill-completion-path-order (cf login).
  5. In-browser: admin Order column editable + sorts; reorder changes mission page + /app-space/?eventId=....

Notes

  • Built via subagent-driven TDD; full whole-branch review clean.
  • Full unit suite has 14 pre-existing fresh-worktree failures (missing Hugo CSS / srv-qa parsers bundle build artifacts) — none touch this branch; green in CI after build:all.
  • Generated content.bundle.mjs is gitignored (regenerated at build) — not in the diff by design.

…r sites

- packages/content/catalog-data.js: add pathOrder to columns, orderBy('pathOrder','name')
- srv/lib/build-catalog.js: orderBy('mission_ID','pathOrder','name') for cross-mission query
- srv/developer-service.js: orderBy('pathOrder','name') in getEventProgress and getAppSpaceProgress
- srv/lib/branch/mission-detail.js: append orderBy('pathOrder','name') to paths SELECT
- packages/content/catalog-mission-hierarchy.js: update JSDoc comment
- vitest.config.ts: add @tutorials/content alias to resolve worktree src in unit tests
- test/: add pathOrder-ordering assertions in catalog-data, build-catalog-mission-detail,
  and app-space-segments-resolver (NULL pathOrder sorts first on SQLite per observed behavior)
…e sentinel)

Use tagged template literal .orderBy\`coalesce(pathOrder, 999999), name\` at all
five CompletionPaths render sites. Plain .orderBy('pathOrder','name') sorts NULLs
FIRST on SQLite but LAST on HANA — coalesce(pathOrder, 999999) forces NULL-last on
both engines. Tests updated to assert NULL-last ordering.

Fixes the { func: 'coalesce', args: [...] } object form which is not valid as a
fluent .orderBy() positional arg — the orders() function treats plain objects without
.ref as {field: sort} pairs, turning 'func' into a field reference lookup that fails.
The tagged template literal form goes through cds.parse._select and produces the
correct CQL func/args AST.
@jung-thomas
jung-thomas merged commit 1f07641 into DEV Nov 2, 2026
8 of 9 checks passed
@jung-thomas
jung-thomas deleted the worktree-mission-path-ordering-2990 branch November 2, 2026 23:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant