3d1ed15b6d
* feat(registry): move community registry source of truth into the public repo The site's registry page says "Lägg till en egen" and links here, but the MDX entries lived in the private website repo, so an external contributor had no path to open the PR we were inviting (found by the first person who tried). This makes the invitation real: - registry/entries/ + registry/authors/ hold the 20 existing entries and 2 author profiles, migrated verbatim from the website repo, which now syncs FROM this directory instead of owning the content - registry/README.md documents the frontmatter convention and the flow - scripts/validate-registry.ts (npm run validate:registry, wired into core-build) checks structure and rejects JSX/import/export in bodies: the site renders entries through MDX, which would execute those inside the website build - CONTRIBUTING.md points at the registry for listing community work Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com> * fix(registry): close MDX-safety gaps and correct six compliance claims from PR review Review bot findings on #1458, both verified and addressed: - The body safety gate only rejected capitalized JSX tags, but MDX also evaluates lowercase HTML tags (<div>, <img onerror=...>) and bare {...} expressions. The validator now rejects any raw tag and any brace outside fenced code and backtick inline code; literal tags in prose go in backticks. Verified: a crafted entry with all three bypasses fails, all existing content still passes. - Six factual errors in migrated entries, each checked against the skill sources in .claude/skills/ before editing (these were live on the site already): traktamente 2026 is 300 kr not 260; employer contributions for 66+ at year start (67+ from 2026) are 10.21% not "65+: 16.36%", and the under-18 0% claim is replaced with the documented 18-22 youth reduction; electronics reverse-charge threshold is 100 000 kr excl VAT per invoice not 250 000; half prisbasbelopp 2026 is 29 600 not 24 750; kostnadsställe is SIE dimension 1 not 7; SRU period suffixes encode the fiscal-year end range (P1 jan-apr, P2 maj-aug, P4 sep-dec) not fixed months. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com> --------- Signed-off-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com> Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
159 lines
7.0 KiB
YAML
159 lines
7.0 KiB
YAML
name: Core Build (no extensions)
|
|
|
|
# Split out of a single serial job that took 7m43s wall-clock: npm ci 24s, lint
|
|
# ratchet 1m29s, build 2m00s, unit tests 3m31s, ratchets ~10s. None of those
|
|
# stages needed the previous one's output, so they were serial only by
|
|
# accident. Running them as independent jobs (and sharding the 897-file unit
|
|
# suite four ways) puts the critical path on `build` at roughly 2m30s.
|
|
#
|
|
# The prologue each job needs is in .github/actions/setup-core.
|
|
|
|
on: [pull_request]
|
|
|
|
# Six concurrent jobs per push makes stale runs far more expensive than they
|
|
# were when this was one job, and a superseded push has nothing worth finishing.
|
|
concurrency:
|
|
group: core-build-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
jobs:
|
|
checks:
|
|
name: Checks
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
|
with:
|
|
# Nothing here pushes, so leaving the token in .git/config only widens
|
|
# what a compromised dependency in the build could reach.
|
|
persist-credentials: false
|
|
- uses: ./.github/actions/setup-core
|
|
|
|
- name: Verify skill bodies are in sync with the seed migration
|
|
# Fails if a .claude/skills/**/SKILL.md changed without regenerating the
|
|
# seed migration (npm run skills:generate). Keeps prod skill content from
|
|
# silently drifting out of sync. No DB needed: reads files + manifest.
|
|
run: npm run skills:check
|
|
|
|
- name: Verify taxonomy registry is in sync with the element lists
|
|
# Fails if dev_docs/bokslut/taxonomi/** changed without regenerating
|
|
# lib/bokslut/ixbrl/taxonomy/generated/ (npm run taxonomy:generate).
|
|
# The iXBRL generator emits facts strictly from the generated registry,
|
|
# so drift here means filings tagged against a stale concept set.
|
|
run: npm run taxonomy:check
|
|
|
|
- name: Lint ratchet (no new ESLint errors)
|
|
# `npm run lint` was never wired into CI, so ~60 legacy errors
|
|
# accumulated. This ratchet (sibling of check:guards) fails only when
|
|
# a PR ADDS an error beyond scripts/checks/eslint-baseline.json; the
|
|
# baseline ratchets down as legacy errors get fixed.
|
|
run: npm run check:lint
|
|
|
|
- name: Antipattern ratchet (no new MFA-bypassing routes / naive öre-rounding)
|
|
# Fails only if a PR ADDS a route that hand-rolls supabase.auth.getUser()
|
|
# instead of the MFA-enforcing guard, or a new Math.round(x*100)/100.
|
|
# Baseline lives in scripts/checks/antipatterns-baseline.json and ratchets
|
|
# down as the A1 (route auth) and D1 (rounding) migrations land.
|
|
run: npm run check:guards
|
|
|
|
- name: Validate konteringspaket catalogue
|
|
# The system booking templates are data files under packs/ rather than
|
|
# rows frozen inside a migration. This gate is what makes that safe: it
|
|
# checks the schema, that every BAS account actually exists in the 2026
|
|
# chart, and that each pack BALANCES when applied through the real
|
|
# applyTemplate(). PR #1321 shipped seeded reference data that
|
|
# contradicted the engine; that class of bug is structurally valid and
|
|
# only a semantic check catches it.
|
|
run: npm run validate:packs
|
|
|
|
- name: Validate community registry
|
|
# registry/ is the public source of truth for gnubok.se/community/registry
|
|
# and takes entries by PR from outside the team. The gate checks the
|
|
# frontmatter the website build consumes, and rejects JSX/import/export
|
|
# in entry bodies: the site renders bodies through MDX, which would
|
|
# execute those at build time.
|
|
run: npm run validate:registry
|
|
|
|
- name: Check no core imports from extensions
|
|
run: |
|
|
VIOLATIONS=$(grep -r "from '@/extensions/" lib/ app/api/ components/ --include="*.ts" --include="*.tsx" \
|
|
| grep -v "app/api/extensions/" \
|
|
| grep -v "components/extensions/" \
|
|
| grep -v "lib/extensions/_generated/" \
|
|
| grep -v "lib/extensions/loader.ts" || true)
|
|
if [ -n "$VIOLATIONS" ]; then
|
|
echo "ERROR: Core code imports from @/extensions/:"
|
|
echo "$VIOLATIONS"
|
|
exit 1
|
|
fi
|
|
|
|
build:
|
|
name: Build (zero extensions)
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
|
with:
|
|
# Nothing here pushes, so leaving the token in .git/config only widens
|
|
# what a compromised dependency in the build could reach.
|
|
persist-credentials: false
|
|
- uses: ./.github/actions/setup-core
|
|
- run: npm run build
|
|
|
|
test:
|
|
# 897 unit test files (11,341 tests) take 3m31s in one process. Measured
|
|
# locally, four shards split them 225/224/224/224 with the slowest at 64s,
|
|
# which puts this comfortably under the build job so it stops being the
|
|
# critical path. Vitest hard-errors when the shard count exceeds the
|
|
# resolved file count, which is nowhere near a concern at this size.
|
|
name: Unit tests (${{ matrix.shard }}/4)
|
|
runs-on: ubuntu-latest
|
|
strategy:
|
|
# One shard failing should not hide failures in the other three: a red PR
|
|
# is more useful when it lists every broken test, not just the first.
|
|
fail-fast: false
|
|
matrix:
|
|
shard: [1, 2, 3, 4]
|
|
steps:
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
|
with:
|
|
# Nothing here pushes, so leaving the token in .git/config only widens
|
|
# what a compromised dependency in the build could reach.
|
|
persist-credentials: false
|
|
- uses: ./.github/actions/setup-core
|
|
# Via env rather than interpolated straight into the shell body: the value
|
|
# is a static matrix integer and harmless, but keeping every `run:` free of
|
|
# ${{ }} is the rule that makes the template-injection audit meaningful.
|
|
- run: npm test -- --shard="$SHARD/4"
|
|
env:
|
|
SHARD: ${{ matrix.shard }}
|
|
|
|
core-build:
|
|
# One stable check name covering all six jobs above, so the PR checks list
|
|
# (and any future required-status-check rule) has a single thing to read
|
|
# rather than a shard-numbered matrix. `needs` alone would not be enough:
|
|
# a needed job that is skipped or cancelled does not fail its dependents,
|
|
# so the results are asserted explicitly.
|
|
name: Core Build
|
|
if: always()
|
|
needs: [checks, build, test]
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Assert every core job succeeded
|
|
env:
|
|
CHECKS: ${{ needs.checks.result }}
|
|
BUILD: ${{ needs.build.result }}
|
|
TEST: ${{ needs.test.result }}
|
|
run: |
|
|
set -euo pipefail
|
|
echo "checks=$CHECKS build=$BUILD test=$TEST"
|
|
failed=0
|
|
for r in "$CHECKS" "$BUILD" "$TEST"; do
|
|
[ "$r" = "success" ] || failed=1
|
|
done
|
|
if [ "$failed" -ne 0 ]; then
|
|
echo "::error::One or more core-build jobs did not succeed"
|
|
exit 1
|
|
fi
|