diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fa48408..3e7dc30 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,8 +16,10 @@ name: CI on: push: branches: [master, main] + # Every pull request, whatever it targets. A base-branch filter here would + # skip each layer of a stacked PR except the bottom one, which is exactly the + # work that most needs checking before it reaches master. pull_request: - branches: [master, main] # Least privilege: jobs only read the repo. Override per-job if more is needed. permissions: diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index 7009ed2..faa5c70 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -43,7 +43,7 @@ jobs: run: npm run build # Static Pages has no gateway/nginx to rewrite the fragment asset prefix, - # and /__wf/* is outside the project-site mount. Point the marketplace + # and /__wf/* is outside the project-site mount. Point the knowledge base # stylesheet at its real served path (/knowledge-base/style.css → dist/style.css). - name: Rewrite fragment asset paths for static hosting run: find dist -name '*.html' -exec sed -i 's#/__wf/knowledge-base/#/knowledge-base/#g' {} + diff --git a/.github/workflows/pr-requirements.yml b/.github/workflows/pr-requirements.yml index 1dec647..4767bef 100644 --- a/.github/workflows/pr-requirements.yml +++ b/.github/workflows/pr-requirements.yml @@ -5,9 +5,10 @@ name: PR Requirements on: + # No base-branch filter: a stacked PR targets the layer below it, and each + # layer needs the same hygiene checks as the one that reaches master. pull_request: types: [opened, synchronize, reopened, edited, labeled, unlabeled] - branches: [master] # Least privilege: read PR metadata only. permissions: @@ -19,6 +20,25 @@ jobs: name: PR Requirements runs-on: ubuntu-latest steps: + # A stacked PR targets the layer below it rather than master, so a fixed + # allow-list of `master` fails every layer but the bottom one. Allow the + # base branch as well when it is itself a convention-named branch — that + # still rejects a PR aimed at some unrelated branch, which is what the + # check is for, while letting a stack be reviewed layer by layer. + - name: Resolve allowed target branches + id: targets + env: + KB_BASE_REF: ${{ github.event.pull_request.base.ref }} + KB_BRANCH_PATTERN: '^(feat|feature|fix|bugfix|hotfix|release|support|chore|docs|ci|test)/[a-zA-Z0-9._/-]+$' + run: | + set -euo pipefail + if printf '%s' "$KB_BASE_REF" | grep -Eq "$KB_BRANCH_PATTERN"; then + echo "Stacked PR: also allowing the layer below ($KB_BASE_REF)." + echo "list=master,$KB_BASE_REF" >> "$GITHUB_OUTPUT" + else + echo "list=master" >> "$GITHUB_OUTPUT" + fi + - name: Check PR requirements uses: AbsaOSS/check-pr-requirements@adb22f6d88c93fd801e787a63d948d6fbbdd4af3 # v0.2.0 with: @@ -48,4 +68,4 @@ jobs: # segments in dependabot branches (e.g. dependabot/npm_and_yarn/…). branch-pattern: "^(feat|feature|fix|bugfix|hotfix|release|support|chore|docs|ci|test|dependabot)/[a-zA-Z0-9._/-]+$" max-files-changed: "50" - allowed-target-branches: "master" + allowed-target-branches: ${{ steps.targets.outputs.list }} diff --git a/.github/workflows/validate-doc-app.yml b/.github/workflows/validate-doc-app.yml index 4c424ef..ecca0a1 100644 --- a/.github/workflows/validate-doc-app.yml +++ b/.github/workflows/validate-doc-app.yml @@ -1,6 +1,6 @@ # Reusable workflow — validate a doc app against the knowledge-base contract. # -# Called by doc-app repos in their own CI to verify the app meets the marketplace +# Called by doc-app repos in their own CI to verify the app meets the knowledge base # contract before raising a PR or publishing a release. # # Usage in a doc repo (.github/workflows/validate.yml): @@ -28,7 +28,7 @@ permissions: jobs: validate: - name: Validate marketplace contract + name: Validate knowledge base contract runs-on: ubuntu-latest steps: - name: Checkout @@ -106,15 +106,15 @@ jobs: echo "✓ dist/${ENTRY} found" # ── Validate headless HTML structure ─────────────────────────────────── - - name: Check data-mp-headless attribute + - name: Check data-kb-headless attribute run: | ENTRY=$(node -e "const m=require('./marketplace.json'); console.log(m.entryPoint || 'index.html')") - if ! grep -q 'data-mp-headless="true"' "dist/${ENTRY}"; then - echo "::error file=dist/${ENTRY}::Missing data-mp-headless=\"true\" on element." - echo "Add data-mp-headless=\"true\" to the tag when building with --headless." + if ! grep -q 'data-kb-headless="true"' "dist/${ENTRY}"; then + echo "::error file=dist/${ENTRY}::Missing data-kb-headless=\"true\" on element." + echo "Add data-kb-headless=\"true\" to the tag when building with --headless." exit 1 fi - echo "✓ data-mp-headless=\"true\" found" + echo "✓ data-kb-headless=\"true\" found" - name: Check no fixed site header in headless output run: | diff --git a/AGENTS.md b/AGENTS.md index 110739b..6c5f3d8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -121,13 +121,13 @@ add a registry entry that requires network or a sibling checkout without it. ### Two build modes, one document Every page renders through `src/layouts/Base.astro`. Headless (web-fragment) and -standalone differ only by `data-mp-headless` and the shadow-DOM compat styles — +standalone differ only by `data-kb-headless` and the shadow-DOM compat styles — not by a different layout. Changes that add a mode-specific code path need a strong reason. ### Light only -The marketplace has no dark mode: no theme toggle, no persisted theme, no `dark` +The knowledge base has no dark mode: no theme toggle, no persisted theme, no `dark` class, no dark palette. A sub-app's own theme bootstrap is removed twice: `scripts/hoist-inline-scripts.js` deletes it while it is still inline, and `src/utils/transform.js` strips any that survives, along with a `dark` body diff --git a/CLAUDE.md b/CLAUDE.md index fc8b5de..5bdcc77 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -72,7 +72,7 @@ Orchestrator: `scripts/build-vite.js`. Flags: `--local`, `--headless`. - `src/pages/index.astro` — Landing catalog page - `src/utils/apps.js` — `getAppPages()` enumerates sub-app HTML (manifest-driven or filesystem crawl) - `src/utils/transform.js` — `transformSubAppHtml()`: URL rewriting, document splitting (head/body/title/body-class), headless transforms -- `src/layouts/Base.astro` — The one document shell: head, marketplace CSS (which carries the self-hosted Inter faces), ``, shadow-DOM compat styles +- `src/layouts/Base.astro` — The one document shell: head, knowledge base CSS (which carries the self-hosted Inter faces), ``, shadow-DOM compat styles - `src/components/Masthead.astro` — Persistent Knowledge base header + Library/current-app sub-nav (all pages, both modes) - `src/components/AppCard.astro`, `src/components/AppIcon.astro` — Catalog card and its icon - `src/templates/shadow-compat.js` — Shadow-DOM design-token styles, injected into the body by the layout @@ -90,21 +90,21 @@ An `apps.json` entry is one of: - **default (packaged)** — a repo publishes a headless static site as `dist.tar.gz` plus `marketplace.json`. Every HTML file becomes a route. - **`type: "iframe"`** — no artifact; a single route renders a full-viewport `