Release 1.22.0 — What's New refresh + version bump - #2176
Merged
Conversation
/sitemap.xml is served from the HANA page-sitemap.xml blob and republished by publish-content.ts on every non-slug rebuild (discoverPageFiles, gated `if (!opts.slug)`). catalog-only skips "Fetch tutorials", so Hugo bakes a sitemap with NO /tutorials/ URLs and republishing it WIPES the live sitemap down to ~180 links (reported 2026-09-03 by the Intelligent Search Data Crawling team: crawled docs ~1700 -> ~1500). Same wipe class as the /browse/ and /authors/ catalog-only wipes. - scripts/seed-sitemap-from-deployed.ts: catalog-only preserve step. Runs AFTER the Hugo build (the sitemap is a build output, not an input) and overwrites the tutorial-less sitemap with the one the approuter is currently serving. Fails the run if the deployed sitemap is unreadable or names zero tutorials (ALLOW_EMPTY_SITEMAP=1 escape hatch for seed envs). - scripts/check-sitemap-tutorials.cjs: fail-closed guard. Runs in full + catalog-only before publish; fails if the baked sitemap has zero /tutorials/ <loc> (the exact wipe signature). - rebuild-content.yml: wire both steps in after "Build Hugo site". - seo-files.test.js: smoke assertion that /sitemap.xml contains /tutorials/. - unit tests cover both tutorial-<loc> counters (shared logic, run through both impls so they can't diverge).
fix(sitemap): guard catalog-only rebuilds from wiping /tutorials/ URLs
…ials (#2127) Devtoberfest "validation" tutorials are published with zero real steps on purpose: the page must be live (visible in the mission) but NOT completable — no Done button, no completion, no Devtoberfest points — until real questions are added. Two defects blocked this. 1. Commented-out steps broke rendering. parseV2Steps was only fence-aware, so a `### Question 1` wrapped in a multi-line `<!-- ... -->` comment was lifted as a phantom step and its `<!--`/`-->` markers stranded across the split, breaking Hugo rendering of the whole page. parseV2Steps now consults commentLineFlags() (already used by frontmatter/intro extraction): H3s inside a spanning comment no longer delimit a step, and commented lines are dropped from step content. 2. No-step tutorials were quarantined. validate-tutorials.ts required stepCount > 0 for every tutorial. A new repo-scoped exception (stepCountReason + NO_STEP_ALLOWED_REPOS) permits exactly 0 steps ONLY for the developer-advocates repo family (looked up via the fetch discovery cache); every other repo still requires >=1 step. Missing/corrupt discovery cache degrades safely (still requires a step). The Hugo layout already omits Done buttons and completion when stepCount == 0 (step shortcodes gone, `total > 0` guards short-circuit). Additionally suppress the now-meaningless progress ring, progress-bar island, PiP launcher, and expand/collapse-all controls for step-less tutorials so the page renders cleanly. Verified: parser + validate unit suites green (adds v2-commented-h3.test.ts and validate-tutorials-stepcount.test.ts); local Hugo build of a 0-step tutorial renders the intro with no Done button and no progress affordances.
…ep-commented fix(tutorials): support no-step + commented-out-step validation tutorials (#2127)
The npm i -g sap-devs instruction 404s — the CLI is no longer published to npm. Replace with the Homebrew/Scoop/GitHub Releases binary install methods and fix the project link to the sap-devs-cli repo. Closes #2123
…s-install fix(api-docs): sap-devs CLI install no longer on npm (#2123)
…nger-hunt Update Devtoberfest scavenger hunt clue captions (#2130)
Add a prominent top-of-homepage banner linking to /devtoberfest/. Before the event it shows a live countdown to the start; during the event it shows a live-now message with the contest date range. Hidden off-season, after the event ends, or on error -- no layout shift. The event window is read at runtime from the existing anonymous GET /api/devtoberfest/status (admin-managed via /admin-ui/#/devtoberfest), so there are no hardcoded dates. Reuses the tested countdown.ts phase/label logic; new phase->view logic in view.ts is unit-tested (9 cases).
…nner feat(homepage): Devtoberfest banner with live countdown (#2131)
…pp-space (#2133) Stop hardcoding "TechEd" and pull real event identity from the related event. Schema: - Events gains description (app-space hero), hasLogo, logoUpdatedAt. - New EventLogo entity: 1:1 composition child holding a WebP BLOB (mirrors DevtoberfestBanner — plain hdbtable, no journal annotation). - Events hdbmigrationtable regenerated (version=4). Backend: - srv/lib/event-logo-store.js — sharp -> WebP q82 pipeline, sha256, upsert + hasLogo flip, HANA-raw / SQLite-CDS-QL fetch (LOB locator rule). - AdminService bound actions uploadEventLogo / clearEventLogo + handlers. - Anonymous GET /api/event-logo?eventLegacyId=N (ETag + 1d cache), with matching approuter authenticationType:none route. - getEventBuckets now returns { eventName, eventType, hasLogo, buckets }; getAppSpaceProgress adds eventDescription + hasLogo. Frontend: - event-display: real event name in the hero label + logo lockup. - app-space: event name, description, and logo from the related event. - Admin Events object page: Upload/Clear Logo header actions. Tests: test/unit/events/event-logo-store.test.js (pipeline + upsert + fetch + clear). Migration verified idempotent via canonical cds build.
…omization feat(events): per-event name, description & logo on event-display + app-space (#2133)
The mcp-remote --static-oauth-client-info blocks hardcoded the dev client sb-tutorials!t676072. Prod runs a distinct xsappname (tutorials-prod) in the same XSUAA tenant, so its OAuth client is sb-tutorials-prod!t676072. Against developers.sap.com the dev client_id + prod-owned Tutorial.MCP scope (emitted by the .well-known discovery) is rejected at /oauth/authorize as an invalid authorization request. Switch the example blocks to the prod client_id and add a dev/prod mapping table plus a cf env lookup so users pick the id matching their <base>.
…-uri whitelist Some mcp-remote builds bind the callback to the loopback IP 127.0.0.1 (RFC 8252's preferred default), producing http://127.0.0.1:<port>/oauth/callback. XSUAA matches redirect_uri literally and the client whitelists only http://localhost:*/oauth/callback, so the IP form fails at /oauth/authorize with 'redirect_uri does not match the configuration'. Pin --host localhost in both example blocks and add a troubleshooting note.
…uer) MCP clients (mcp-remote / MCP SDK) read authorization_servers[0] from the protected-resource metadata and run RFC 8414 discovery against it. Option A pointed that at the raw XSUAA URL, but XSUAA does not implement RFC 8414: GET <xsuaa>/.well-known/oauth-authorization-server 302-redirects to /login, which returns 200 (HTML). The SDK follows the redirect, parses HTML as JSON, and every required field is undefined -> ZodError. Because the response is 200 (not 404) the SDK never falls back to XSUAA's working openid-configuration, so the token exchange dies after a successful authorize. Fix: advertise the approuter itself as the authorization server. protected- resource authorization_servers and the AS metadata issuer now use the request- derived self base URL; authorize/token endpoints still point at XSUAA. Clients discover our valid 200 RFC 8414 doc and never touch XSUAA's broken well-known. Token validation on the resource side is unchanged (XSUAA binding). Reverses the authorization_servers/issuer=XSUAA half of the frozen Option A (2026-08-28 spec); the rejected 'Option B' (relocating discovery to the CAP origin) remains out of scope. Updates unit + hybrid tests, architecture doc, and adds a superseding note to the design spec.
fix(mcp-oauth): advertise approuter as authorization server (self-issuer)
…rod-clientid docs(mcp): fix OAuth quickstart to use prod client_id
… loadLiveTags; generic media-diet error messages
…crosswalk feat(channels-hub): Phase 2 — crosswalk Direction 2, media-diet signed-in + export
… wiring - srv/server.js: add GET /build/channel-atlas handler (after /build/channel-collections); selects published Channels + separate REVIEWED ChannelTopicMap SELECT; groups topicTags by channel_ID; lazy import of buildAtlasChannels inside handler (srv-qa safe) - srv/lib/build-channel-atlas.js: convert module.exports → ESM export (project type=module) - approuter/xs-app.json: add /channel-atlas-ui/ SPA route (after /explore-ui/); add /channels/atlas/ static route (before /browse/ routes); /build/ allowlist already contained channel-atlas (confirmed present, no change needed) - .deploy/mta.yaml: add ../../srv/lib/build-channel-atlas.js to srv-qa cp list - test/unit/channels-atlas-endpoint.test.js: 4/4 passing unit tests
…es, styles) Adds all base files for the channel-atlas Vue 3 + Vite + TS SPA: - package.json with pinned deps (sigma 3.0.3, graphology 0.26.0, vue 3.5.34) - vite.config.ts with /channel-atlas-ui/ base, gzip budget plugin (150KB), dev proxy - index.html, tsconfig.json, src/main.ts, src/styles.css, src/App.vue (minimal stub) - Replaces types.ts stub with full domain types: OwnerType, AtlasChannelDTO, AtlasNode, AtlasEdge (shape preserved), AtlasPayload Verified: 20/20 graph.test.ts pass; vue-tsc --noEmit 0 errors.
…tlas2) - Add AtlasGraph.vue: Sigma 3 renderer with graphology MultiDirectedGraph, forceAtlas2 layout, node/edge styling, nodeClick emit - Add AtlasGraph.test.ts: 5 unit tests (mount, nodes, edge dedup, nodeClick emit, prop-change rebuild) - Fix tsconfig.json: add "types": ["vite/client"] so import './styles.css' resolves (TS2882)
…l, useOwnerTypeFilter
…ate, doc star exclusion, ref-based click-outside
The vitest `unit` project (run from repo root in CI) includes app/channel-atlas/src/**/__tests__, and App.test.ts + AtlasGraph.test.ts transitively import AtlasGraph.vue → graphology/sigma. Those deps live only in app/channel-atlas/package.json, not root, so unit-tests.yml's `npm run setup` must install that workspace the same way it already installs app/explore. Without it CI fails: 'Failed to resolve import graphology from AtlasGraph.vue'. Passed locally only because the SPA node_modules was already present.
…atlas feat(channels): Phase 3 — Channel Atlas SPA (/channels/atlas/)
#2169 added the channel-detail serve route to srv/server.js but not srv-qa/server.js, tripping check-srv-qa-route-drift. The channel-detail page is a public prod content surface (like /content/topics/:slug), not tutorial-draft author preview, and its publish sibling POST /content/publish/render-channels is already allowlisted. Add the serve counterpart to ALLOWLIST_ONLY_ON_SRV.
…il-route-drift fix(ci): allowlist GET /content/channel-detail/:slug (route-drift guard)
…result The seed sent all ~236 published channels plus a 236-entry sourceId enum and 160-entry topicTag enum in one chatCompletion capped at max_tokens:4000. The response truncated to invalid/empty tool args, so the whole seed silently produced 0 drafts (exit 0 masked it). Batch channels 25 at a time; one failing batch is logged and skipped rather than aborting the run. Full DEV run now seeds 602 drafts across all 236 channels.
…ed-chunking fix(channels): chunk ChannelTopicMap AI seed to avoid token-cap zero-result
check-slug-lookups flagged srv/lib/build-channel-detail.js:35 as an unmarked direct slug lookup, failing the 'unit tests' CI job (introduced via #2171). The slug is already lowercased into canonSlug at the top of buildChannelDetailPayload, so add the // slug-canonical: pre-canonicalized marker the guard requires.
…enrich health - ChannelsDirectory: remove Learn↔Follow hub card linking to /channels/crosswalk/ (page was never built; xs-app.json reserves but does not serve it → 404). Crosswalk data already surfaces on topic pages + channel-detail pages. - MediaDiet: two-column layout (sticky picker rail + results), live match count so results are never invisible below the fold, raise focus-area cap 3→6. - ChannelsHealth: add narrative summary, active-rate % headline (color-coded good/warn/poor), proportion bars per panel, and 'thin' coverage flags — replaces the bare number table. No new data fields required. Tests updated: hub band 4→3 cards + no-crosswalk assertion; media-diet cap→6 + live-count; health narrative + active-rate. 32/32 island tests pass.
…tail fix(ci): mark pre-canonicalized slug lookup in build-channel-detail
fix(channels): hub UX — crosswalk 404, media-diet layout, health enrichment
Hugo's {{ . | jsonify }} emits the atlas-payload inline JSON double-encoded
(a JSON string whose contents are themselves JSON), so a single JSON.parse
in useAtlasData yielded a string, not an AtlasPayload. App.vue then crashed
on payload.value.channels.map(...) — blank Atlas.
useAtlasData now re-parses when the first parse returns a string and
validates that the result is an object with a channels array before
assigning; otherwise it fails open with an error state. Also repairs
already-served pages (the fix ships in the SPA bundle).
Tests: added double-encoded + missing-channels cases (34/34 pass).
The atlas SPA never consumed the site's --sap* theme variables (flipped by the html.dark class), so in dark mode: owner-type filter gave no visible selected/deselected indication, detail-panel text was black-on-black, and Sigma graph labels rendered black on the dark canvas. - styles.css: body/loading/error/side-border now use --sap* vars w/ light fallbacks - OwnerTypeFilter.vue: add <style> with clear selected (highlight + checkmark) vs deselected (dimmed + faded swatch) states; add role/aria-selected - ChannelDetailPanel.vue: add <style> using theme vars for text/labels/link/borders - AtlasGraph.vue: Sigma labelColor from computed --sapTextColor; MutationObserver live-updates label color on theme toggle (setting refresh, no re-layout)
fix(channel-atlas): dark-mode theming — legible text + visible filter selection
- Add 45 What's New entries (Channels hub Phases 0-3, Channel Atlas, community submissions, nav improvements, admin FE fixes, and more) - Bump .deploy/mta.yaml version 1.21.1 -> 1.22.0 for the release deploy
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.
Release 1.22.0 (DEV → Main)
Promotes the current DEV state to
mainfor the production deploy.Release housekeeping in this PR
1.21.1 → 1.22.0(minor — feature release).Deploy note
.deploy/mta.yamlversion drives the mtar filename (tutorials-ims_1.22.0.mtar) andcf mtas. Deploy is manual/blue-green after merge.