Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,3 +159,4 @@ The load-bearing few. **Full detail for every relocated item → [tutorials-ims-
- **External channels subsystem** — `Channels` entity (`db/channels.cds`) is the source of truth; re-ingest via `npm run seed-channels`; directory at `/channels` (baked by `fetch-channels` in `build:all`); verb-lane fill via `npm run promote-channels`; community items never land in `START_HERE`. → [channels.md](docs/developers/reference/channels.md).
- **TechEd sessions subsystem (#2312)** — new `TechEd*` entities (`db/external/teched.cds`, NOT reused `CommunityEvents`), loaded via a **`using` side-effect** in `db/external-content.cds` (don't delete the import); RainFocus ingest (`POST https://events.rainfocus.com/api/sessions`, headers `rfapiprofileid`/`rfwidgetid` per venue from `RAINFOCUS_{TE26,TEV26}_PROFILE`/`_WIDGET` env — live-confirmed defaults baked in, IDs may rotate; speakers embedded in `participants[]`, tracks in `attributevalues[attribute=="Track"]`, offset-paginated); idempotent seed via `scripts/seed-teched.cjs`; public `/build/teched` feed; corrected fetcher + delta cron jobs shipped in #2332; `/teched/` page + island, KG projection, embeddings, and Devtoberfest cross-linking are later units. → [teched.md](docs/developers/reference/teched.md).
- **Signed provenance envelope (#2245)** — `PROVENANCE_ENVELOPE_ENABLED` (DB config, default OFF, DEV-first); two anonymous Express endpoints (`/content/tutorials/:slug/provenance` and `/.well-known/tutorial-provenance/jwks.json`); signing key = `PROVENANCE_SIGNING_KEY` credstore secret (Ed25519 PKCS8 PEM); rotate via `/admin-ui/#secrets`. Fail-open. → gotchas.md "Signed provenance envelope".
- **Optimized draft deletion `draft_admin_integrity` ON (#2909, CAP 10.2)** — `cds.fiori.draft_admin_integrity: true` in package.json; draft discard cascade-deletes draft `CompletionPathItems`/`GroupPathItems` at the DB level, so rollup reconcile now fires via `after('DISCARD','Missions.drafts'/'Groups.drafts')` (the `.drafts` target is required), NOT the per-row child `after('DELETE')` hooks (those stay for direct programmatic deletes). Deleting an active entity right after `draftActivate` can 403 ("a draft exists") until the residual draft is discarded. Constraint migration deploys to HANA (`.hdbconstraint`, `NOT VALIDATED`) BEFORE the flag serves. → gotchas.md "Optimized draft deletion".
14 changes: 14 additions & 0 deletions docs/developers/reference/tutorials-ims-gotchas.md
Original file line number Diff line number Diff line change
Expand Up @@ -203,3 +203,17 @@ All default OFF and DEV-only unless noted. Toggles fail-open on every fault path
- **`sourceCommit` plumbing** — `ContentCurrent.sourceCommit` (String(64)) is populated by the publish pipeline via the `source_commit` field in the publish payload; `scripts/fetch-tutorials.ts` threads the HEAD commit SHA through to the publish client. On older published content the column is NULL; the provenance JWS payload will carry `sourceCommit: null` in that case, which is valid.

- **Quarantine + publisher are status-aware for DELETED/INACTIVE (#2585 follow-up)** — a tutorial with `Tutorials.status IN ('DELETED','INACTIVE')` is excluded from the whole publish/quarantine path, so a retired tutorial whose source `.md` still exists upstream no longer gets re-discovered, re-quarantined, or shown in the Admin active-quarantine facet. Mechanism: `GET /content/excluded-slugs` (public-read on prod srv, `hashesAuth` on srv-qa) returns those lowercased slugs; `scripts/fetch-tutorials.ts` (`applyExclusion` after discovery, both main and `--regenerate` paths) and `scripts/validate-tutorials.ts` (skip at the top of the file loop) fetch it and skip those slugs — **fail-open**, an unreachable endpoint means no filtering; and `quarantineIngestHandler` (`packages/content/content-store.js`) drops events for those slugs before persisting, so the slug-only Admin facet join never surfaces them. One-off cleanup of a live snapshot without a full rebuild: `scripts/clear-deleted-from-quarantine.cjs` (`cds bind --exec … -- node scripts/clear-deleted-from-quarantine.cjs --commit`; dry-run by default). Note: the pre-existing `/content/source-hashes` filter excludes only `INACTIVE` — the new `/content/excluded-slugs` query covers **both** statuses.

## Optimized draft deletion — `draft_admin_integrity` (CAP 10.2, #2909)

- **`cds.fiori.draft_admin_integrity: true` is ON in `package.json`** — CAP 10.2 optimized draft deletion. `cds build` generates `ON DELETE CASCADE` constraints on each draft table's `DraftAdministrativeData_DraftUUID` FK; draft activation and root cancellation then delete only the `DraftAdministrativeData` record and let the DB cascade the child draft rows, instead of the runtime issuing a per-table DELETE for each. All 19 draft-enabled entities live in `AdminService`.

- **Draft DISCARD now reconciles rollups via root handlers, NOT child DELETE events** — because the cascade removes draft `CompletionPathItems`/`GroupPathItems` at the DB level, the per-row `before/after('DELETE')` hooks (`srv/admin-service.js:1197-1242`) do **not** fire on a draft discard. `srv/admin-service.js` registers `this.after('DISCARD', 'Missions.drafts', …)` and `…('Groups.drafts', …)` (the `.drafts` target is required — DISCARD on the active entity does not fire) which re-fire `reconcileEntityForUsers` for the active entity's `legacyId`. The existing child DELETE handlers stay — they still fire for **direct programmatic deletes** (e.g. `service.tx()` row deletes), which the cascade does not touch. Fail-open like the SAVE reconcile hooks.

- **Delete-active-immediately-after-activate now 403s until the draft is cleared** — under the flag, `DELETE …(IsActiveEntity=true)` right after `draftActivate` can return `403 — Entity cannot be deleted because a draft exists` (observed on SQLite in tests). Any code or test that deletes an active entity shortly after activation must first discard the residual draft (`DELETE …(IsActiveEntity=false)`), then delete the active. This is the documented 10.2 behavior change, not a bug.

- **Constraint-deploy migration ordering (HANA)** — constraints generate at `cds build` once the flag is on and deploy to HANA as `.hdbconstraint` with `NOT VALIDATED` (so existing orphaned draft rows don't block the deploy). Deploy the constraints to a target env and let them land **before** the flag serves there — once the runtime serves with the flag on, it stops issuing per-table draft deletes and relies on the cascade. Order DEV → test → PROD per the no-main-hotfix model; confirm scope with the maintainer before any PROD step.

- **Optional orphan purge** — set `cds.fiori.draft_orphan_cleanup: true` to purge pre-existing orphaned draft rows as background cleanup during draft create/edit, then turn it back off once orphans are cleared. Independent of `draft_admin_integrity`; default OFF. Not enabled in `package.json`.

- **Rollback** — the constraints are additive (`ON DELETE CASCADE` on draft tables only). To revert: flip `draft_admin_integrity` back off (the runtime resumes per-table draft deletes, and the DISCARD reconcile handlers become harmless no-ops relative to the restored child-DELETE path) and optionally drop the generated constraints on the next deploy.
Loading
Loading