Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
f8c241d
chore(webapp): replace Biome's linter with oxlint
FelixTJDietrich Aug 21, 2026
85b7568
ci: bound the webapp unit-test leg with a timeout
FelixTJDietrich Aug 21, 2026
89656a7
refactor(webapp): fix the lint findings behind the suppressions
FelixTJDietrich Aug 21, 2026
83323a4
feat(webapp): enforce 168 more lint rules oxlint had switched off by …
FelixTJDietrich Aug 22, 2026
279ca51
fix(webapp): drop a lint rule that silently checks nothing
FelixTJDietrich Aug 22, 2026
d2cdecf
feat(webapp): enable type-aware linting and handle every floating pro…
FelixTJDietrich Aug 22, 2026
b2bf61b
fix(webapp): fix the correctness bugs type-aware linting surfaced
FelixTJDietrich Aug 22, 2026
284bb42
refactor(webapp): drop the type assertions the checker does not need
FelixTJDietrich Aug 22, 2026
d67eb0d
refactor(webapp): validate at runtime where the code was asserting
FelixTJDietrich Aug 22, 2026
d0c2372
refactor(webapp): fix every suppressed lint finding at its source
FelixTJDietrich Aug 22, 2026
9ab24d3
feat(webapp): close the gaps the type-aware pass left open
FelixTJDietrich Aug 22, 2026
140d3c5
feat(webapp): enforce the any boundary, deprecations and layering
FelixTJDietrich Aug 22, 2026
e78c8ba
feat(webapp): index safety, dead conditions and named option lists
FelixTJDietrich Aug 22, 2026
39c53f0
Merge remote-tracking branch 'origin/main' into issue-1463-replace-bi…
FelixTJDietrich Aug 22, 2026
baa2684
refactor(webapp): replace what the framework already provides, and te…
FelixTJDietrich Aug 22, 2026
00fc238
docs(webapp): cut the commentary back to what a reader cannot infer
FelixTJDietrich Aug 22, 2026
279ab28
Merge remote-tracking branch 'origin/main' into issue-1463-replace-bi…
FelixTJDietrich Aug 22, 2026
a896604
fix(server): report an unearned achievement as having no unlock time
FelixTJDietrich Aug 22, 2026
9f9bb9e
Merge remote-tracking branch 'origin/main' into issue-1463-replace-bi…
FelixTJDietrich Aug 22, 2026
4e255af
feat(webapp): enforce the story and query-key conventions in the linter
FelixTJDietrich Aug 22, 2026
d3c0d4b
refactor(webapp): close the gaps an adversarial audit found in the li…
FelixTJDietrich Aug 23, 2026
5ce2304
refactor(config): require the path alias instead of parent-relative i…
FelixTJDietrich Aug 23, 2026
3eae248
feat(webapp): enforce the conventions that were only ever review notes
FelixTJDietrich Aug 23, 2026
16ecaac
Merge remote-tracking branch 'origin/main' into issue-1463-replace-bi…
FelixTJDietrich Aug 23, 2026
9b4f3e7
Merge remote-tracking branch 'origin/main' into issue-1463-replace-bi…
FelixTJDietrich Aug 23, 2026
63efa4c
docs: cut the comments that restate what the linter already says
FelixTJDietrich Aug 23, 2026
25f8062
fix(webapp): type-check the whole .storybook directory, not just preview
FelixTJDietrich Aug 23, 2026
4c63aef
fix: arm the three gates that reported success without checking anything
FelixTJDietrich Aug 23, 2026
b00c877
fix(server): give PMD the classpath it needs to resolve types
FelixTJDietrich Aug 23, 2026
ba8414d
Merge remote-tracking branch 'origin/main' into issue-1463-replace-bi…
FelixTJDietrich Aug 23, 2026
feb9ade
docs(release): say the fixes came from the whole codebase, not the we…
FelixTJDietrich Aug 23, 2026
4f82368
docs: cut the comments that restate the code, and correct the ones th…
FelixTJDietrich Aug 24, 2026
67a3740
feat(webapp): keep Storybook titles derived from the file tree
FelixTJDietrich Aug 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .agents/workflows/format-achievements.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Preferred Order:
// turbo
1. Run the achievement formatter script:
```bash
node scripts/format-achievements.mjs
node scripts/format-achievements.ts
```

2. Review the changes in `achievements.yml`.
5 changes: 3 additions & 2 deletions .changeset/changelog.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// adds PR links + "Thanks @user!"). Operators read this file; git metadata
// lives in git.
module.exports = {
getReleaseLine: async (changeset) => `- ${changeset.summary.trim().split("\n").join("\n ")}`,
getDependencyReleaseLine: async () => "",
/** @param {{ summary: string }} changeset */
getReleaseLine: (changeset) => `- ${changeset.summary.trim().split("\n").join("\n ")}`,
getDependencyReleaseLine: () => "",
};
41 changes: 41 additions & 0 deletions .changeset/oxlint-replaces-biomes-linter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
"hephaestus": patch
---

Screen readers now announce what every dropdown list is for. The status, timeframe, work-type,
rows-per-page and model pickers each opened a list of options with no name attached, so the list
itself was announced as unlabelled.

Also fixed, all surfaced by a stricter type and lint gate across the whole codebase:

- A review schedule saved with a time that had no minutes (`9` rather than `09:00`) stored no minute
at all instead of falling back to the hour's start.
- Audit-log entries and the curated-catalogue version panel printed `[object Object]` for any field
whose value was not plain text.
- A cookie-consent choice was read back from browser storage without checking it, so a corrupted
entry could be treated as a decision.
- A theme, a workspace role or a feature flag that the browser or server reported as something this
build does not recognise is now ignored rather than trusted: the theme falls back to the default,
the role is refused, and the flag reads as off.
- GitLab sub-issue sync could delete parent links it had never looked at. When a page walk stopped
early — an error, or a repository past the pagination ceiling — the cleanup step still ran against
the partial result and cleared the parent of every issue whose link lived on a page it never
fetched, then reported the sync as completed.
- Server errors now keep their original stack trace. Fifty-two places caught an exception and threw a
new one without attaching the cause, so the log recorded where the failure was reported rather than
where it happened. Sign-in, token validation and Slack preference failures were all affected.
- Scrollbars inside scrollable panels rendered 2px wide with no border instead of the intended 10px.
- A checkbox or radio that is switched off now looks switched off: its label kept full contrast, and
the control itself showed neither the dimming nor the blocked cursor.
- The primary button gave no hover feedback. The style was written so that it only applied when the
button was rendered as a link, so the most-used button in the app looked inert under the cursor.
- A mentor attachment that failed to upload disappeared with no message, leaving the sender believing
it was attached. The failure is now reported.
- A disabled accordion section still opened when clicked.
- Copying a mentor reply to the clipboard failed silently when the browser refused.
- Countdowns now advance while the page is open: an Outline token's expiry, and the wait shown while a
sync is rate-limited, previously only moved when something else on the page happened to redraw.
- Screen readers no longer hear an orientation announced on grouped toggle buttons, which is not
something a group can meaningfully have.
- The achievements API described an unlock time as always present, even for an achievement nobody
has earned. It is now reported as absent, which is what the server was already sending.
17 changes: 17 additions & 0 deletions .changeset/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// changesets loads its changelog plugin by `require`, so that one file stays CommonJS JavaScript.
// oxlint's type-aware rules resolve a file's project by looking for the nearest `tsconfig.json`, and
// without one that opts JavaScript in, the plugin's JSDoc types are ignored and its single argument
// reads as `any`. Nothing compiles against this; it exists so the plugin is checked at all.
{
"compilerOptions": {
"target": "ES2023",
"module": "CommonJS",
"lib": ["ES2023"],
"allowJs": true,
"checkJs": true,
"strict": true,
"noEmit": true,
"types": ["node"]
},
"include": ["./*.cjs"]
}
67 changes: 10 additions & 57 deletions .claude/skills/composition-patterns/README.md
Original file line number Diff line number Diff line change
@@ -1,60 +1,13 @@
# React Composition Patterns
# composition-patterns

A structured repository for React composition patterns that scale. These
patterns help avoid boolean prop proliferation by using compound components,
lifting state, and composing internals.
A vendored snapshot of Vercel's React composition-patterns pack (MIT, see `SKILL.md` frontmatter).
There is no build step here: `AGENTS.md` is the compiled form of `rules/*.md` as shipped upstream, and
editing either in place makes the next re-vendor a conflict rather than an overwrite.

## Structure
- `SKILL.md` — what the loader reads, and the only file this repo maintains.
- `rules/*.md` — one rule each; `_sections.md` carries the section order and impact levels, and is the
authority when a summary disagrees with it.
- `AGENTS.md` — every rule expanded into one document.

- `rules/` - Individual rule files (one per rule)
- `_sections.md` - Section metadata (titles, impacts, descriptions)
- `_template.md` - Template for creating new rules
- `area-description.md` - Individual rule files
- `metadata.json` - Document metadata (version, organization, abstract)
- **`AGENTS.md`** - Compiled output (generated)

## Rules

### Component Architecture (CRITICAL)

- `architecture-avoid-boolean-props.md` - Don't add boolean props to customize
behavior
- `architecture-compound-components.md` - Structure as compound components with
shared context

### State Management (HIGH)

- `state-lift-state.md` - Lift state into provider components
- `state-context-interface.md` - Define clear context interfaces
(state/actions/meta)
- `state-decouple-implementation.md` - Decouple state management from UI

### Implementation Patterns (MEDIUM)

- `patterns-children-over-render-props.md` - Prefer children over renderX props
- `patterns-explicit-variants.md` - Create explicit component variants

## Core Principles

1. **Composition over configuration** — Instead of adding props, let consumers
compose
2. **Lift your state** — State in providers, not trapped in components
3. **Compose your internals** — Subcomponents access context, not props
4. **Explicit variants** — Create ThreadComposer, EditComposer, not Composer
with isThread

## Creating a New Rule

1. Copy `rules/_template.md` to `rules/area-description.md`
2. Choose the appropriate area prefix:
- `architecture-` for Component Architecture
- `state-` for State Management
- `patterns-` for Implementation Patterns
3. Fill in the frontmatter and content
4. Ensure you have clear examples with explanations

## Impact Levels

- `CRITICAL` - Foundational patterns, prevents unmaintainable code
- `HIGH` - Significant maintainability improvements
- `MEDIUM` - Good practices for cleaner code
`/storybook-components` states what these patterns cost in this repo — Base UI `render=` slots,
Storybook Controls, the two-call-site test. Read it alongside, not instead.
2 changes: 1 addition & 1 deletion .claude/skills/composition-patterns/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
name: vercel-composition-patterns
name: composition-patterns
description:
React composition patterns that scale. Use when refactoring components with
boolean prop proliferation, building flexible component libraries, or
Expand Down
123 changes: 40 additions & 83 deletions .claude/skills/fix-ci/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ disable-model-invocation: true
allowed-tools:
- Bash(gh *)
- Bash(git *)
- Bash(npm *)
- Bash(mvn *)
- Bash(pnpm *)
- Bash(./mvnw *)
- Read
- Grep
- Glob
Expand All @@ -20,10 +20,10 @@ metadata:

# Fix CI

Diagnose and fix ALL failing CI checks in ONE pass. Never push until all
known issues are resolved.
Diagnose every failing check in one pass, then push once. Fixing 2 of 5 failures burns a push cycle
and a full CI run.

## 1. Ensure All Checks Are Complete
## 1. Wait for the run to finish

```bash
PAGER=cat gh pr view --json number,statusCheckRollup --jq '{
Expand All @@ -33,114 +33,71 @@ PAGER=cat gh pr view --json number,statusCheckRollup --jq '{
}'
```

If there are pending checks, wait for all checks to complete before diagnosing.
Fixing 2 of 5 failures wastes a push cycle.
Anything pending means you do not yet know the failure set.

## 2. Get ALL Failed Job IDs At Once
## 2. Read the job summaries before the logs

```bash
RUN_ID=$(PAGER=cat gh run list --branch $(git branch --show-current) --limit 1 --json databaseId,conclusion --jq '[.[] | select(.conclusion == "failure")][0].databaseId')
echo "Run ID: $RUN_ID"
```

Then get all failed jobs:
The quality and test workflows write a markdown table to `$GITHUB_STEP_SUMMARY` naming, per failed
check, the exact command that fixes it, and emit the same text as `::error::` annotations. That table
is the prescription. Logs are only for what it cannot express — a test assertion, a type error.

```bash
PAGER=cat gh api repos/{owner}/{repo}/actions/runs/$RUN_ID/jobs --jq '[.jobs[] | select(.conclusion == "failure" and (.name | test("CI Status|all-ci") | not)) | {id, name}]'
RUN_ID=$(PAGER=cat gh run list --branch "$(git branch --show-current)" --limit 1 \
--json databaseId,conclusion --jq '[.[] | select(.conclusion == "failure")][0].databaseId')
PAGER=cat gh api "repos/{owner}/{repo}/actions/runs/$RUN_ID/jobs" \
--jq '[.jobs[] | select(.conclusion == "failure" and (.name | test("CI Status|all-ci") | not)) | {id, name}]'
```

## 3. Get ALL Job Logs At Once

For EACH failed job ID from step 2, get logs in a single loop:
Then per failed job id:

```bash
for JOB_ID in <SPACE_SEPARATED_JOB_IDS>; do
echo "=== JOB $JOB_ID ==="
PAGER=cat gh api repos/{owner}/{repo}/actions/jobs/$JOB_ID/logs 2>&1 | tail -80
echo ""
done
PAGER=cat gh api "repos/{owner}/{repo}/actions/jobs/$JOB_ID/logs" 2>&1 | tail -80
```

Read ALL output before making any fixes.

## 4. Classify ALL Failures

Before fixing anything, categorize every failure into this table.
**Fix in this order** (earlier fixes often resolve later issues):
Read every failure before changing anything.

| Priority | Category | Symptoms | Fix Command |
|----------|----------|----------|-------------|
| 1 | Formatting | "Formatting failed", biome/prettier diff | `pnpm run format` |
| 2 | Lint | Biome lint errors | `pnpm run check:fix` |
| 3 | TypeScript | TS2xxx errors, type mismatch | Fix the type error in source |
| 4 | Build failure | Compilation errors, missing exports | Fix imports/exports, verify with `pnpm run build:webapp` |
| 5 | Webapp tests | "FAIL" in webapp test output | Fix test or source, verify with `pnpm run test:webapp` |
| 5 | App server tests | Maven test failures, assertion errors | Fix test or source, verify with `cd server && ./mvnw test -Dsurefire.includedGroups="unit" -Dmaven.test.skip=false -T 2C --batch-mode -q` |
| 6 | OpenAPI sync | "OpenAPI out of sync" | `pnpm run generate:api` |
| 6 | DB schema | "Schema drift detected" | `pnpm run db:draft-changelog` |
| 6 | DB ERD | "ERD outdated" | `pnpm run db:generate-erd-docs` |
## 3. Fix in dependency order

## 5. Fix ALL Issues
Formatting, then lint, then types, then behaviour — an earlier fix routinely erases a later failure.
Each leg's annotation names its own command; this table is only what the annotation cannot tell you.

Work through the entire list. Fix root causes, not symptoms.
| Failure | What it actually means |
|---|---|
| `routeTree.gen.ts is stale` | Only a Vite build writes it. `cd webapp && pnpm run build`, then commit the file. |
| `README images are stale` | The storybook job runs `export:readme-assets` *after* `test:storybook`, so the job goes red having printed a clean pass line. Run `pnpm --filter webapp run export:readme-assets` and commit `docs/images/readme`. |
| Biome version skew | `check:biome-pin` compares `package.json`, `node_modules` and the `$schema` URLs in both `biome.jsonc` files. Fix the pin; do not reformat. |
| Migrations gate | A changelog that reached `main` was edited, renamed or deleted, or a `master.xml` `<include>` was not appended at the end. Fix forward with a new changeset; never edit the released file. |
| `verify-changesets` | The PR touches shipped code with no `.changeset/*.md`. `/land-pr` step 9 has the rules. |
| App Server leg red on a docs-only PR | Expected, not a misconfiguration: `docs/**` is inside the `application-server` paths filter, because `docs:lint` and `check:diagrams` run on that leg. |

IMPORTANT: Do NOT push after fixing only one failure if multiple exist.
Fix everything first.

## 6. Validate Locally Before Pushing

After ALL fixes are applied, run local validation:
## 4. Reproduce locally before pushing

```bash
pnpm run format
pnpm run check
```

Then run tests for ALL components that had failures:

```bash
# If webapp tests failed:
pnpm run test:webapp

# If app-server tests failed:
cd server && ./mvnw test -Dsurefire.includedGroups="unit" -Dmaven.test.skip=false -T 2C --batch-mode -q && cd ../..
```

ALL must pass locally before pushing.

## 7. Regenerate If Needed
`check` runs every leg CI runs except those needing Docker or a live credential — `docs:lint`
included. If `check` is green and CI is not, the difference is one of those.

If any OpenAPI or DB validation failed:
Server tests need `-P'!quick'`. The `quick` profile is activated by the presence of generated GraphQL
sources and sets `maven.test.skip=true`, so a plain `./mvnw test` prints BUILD SUCCESS having run
nothing:

```bash
pnpm run generate:api
pnpm run db:generate-erd-docs
cd server && ./mvnw test -P'!quick' -Dsurefire.includedGroups=unit -T 2C --batch-mode -q
```

Run format + check again after regeneration:

```bash
pnpm run format
pnpm run check
```
`-Dgroups` is ignored: the POM binds `${surefire.includedGroups}`, and a POM element beats the
`-Dgroups` user property. `server/AGENTS.md` § Build traps has the other three tiers.

## 8. Commit and Push (ONCE)
## 5. Commit and push once

```bash
git add -A
git commit -m "fix(<scope>): resolve ci failures"
git push
PAGER=cat gh pr checks --watch
```

## 9. Monitor

```bash
PAGER=cat gh pr checks $(PAGER=cat gh pr view --json number -q .number) --watch
```

## Rules

1. NEVER push after fixing only one failure if multiple failures exist
2. ALWAYS run local validation (format + check + affected tests) before pushing
3. If step 1 shows pending checks, wait for all checks to complete first
4. If the same CI check fails twice in a row, investigate deeper - do not retry the same approach
If the same check fails twice with the same diagnosis, re-read the log rather than retrying the fix.
Loading
Loading