Skip to content

feat(workspace): connect any OpenAI-compatible model, and cap what AI spends - #1400

Merged
FelixTJDietrich merged 92 commits into
mainfrom
1368-workspace-production-readiness
Jul 28, 2026
Merged

feat(workspace): connect any OpenAI-compatible model, and cap what AI spends#1400
FelixTJDietrich merged 92 commits into
mainfrom
1368-workspace-production-readiness

Conversation

@FelixTJDietrich

@FelixTJDietrich FelixTJDietrich commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator

Description

Today a Hephaestus instance can only run AI against the providers we hard-coded, and nobody can see what that AI costs. A single busy workspace — or one runaway sync — can quietly spend the whole instance's budget, and the first sign of it is the provider bill.

This PR makes AI spend visible and bounded, and opens the model choice up.

Connect any OpenAI-compatible model. An instance admin registers a provider by base URL and API key, adds the models it serves, and shares them with the workspaces that should have them. A workspace that would rather pay its own provider can connect one itself. Self-hosted gateways and university GPU clusters work the same way as commercial APIs — there is no provider allowlist any more, just a URL that speaks the OpenAI wire format.

See what it costs. Every AI call now lands in one ledger, whether it came from practice detection or from a mentor conversation. Workspace admins get a monthly report broken down by day and by kind of work; instance admins get the same across every workspace. Amounts can be shown in a second currency alongside USD, converted at the European Central Bank's published reference rate.

Cap it. There are two budgets, and they never mix:

Set by Pays for If reached
Shared-model budget Instance admin The models the host provides Only host-funded work pauses
Own-provider budget Workspace admin The workspace's own connected provider Only that workspace's own-provider work pauses

Keeping them apart is the point: a host that has run out of budget must not be able to stop work a workspace is paying for itself, and a workspace must never be able to spend the host's money by raising its own limit.

Spend counts while work is happening, not only when it finishes. That matters more than it sounds — a single agent run or mentor conversation makes many provider calls, so a cap that only sees completed work can be passed by a wide margin before it notices.

Fixes #1368

Reviewing this

It is a large diff. In rough order of how much it matters:

  1. server/…/agent/usage/ — the ledger, the two budgets, and the rule that decides whether a call may proceed. LlmBudgetService states the policy once, in prose, at the top.
  2. server/…/agent/proxy/ — where a call is metered as it is served, and where the cap is re-checked before each forward.
  3. server/…/db/changelog/1785015307013_changelog.xml — one changelog. The interesting part is the migration that carries existing configurations into the new model catalogue before the old table is dropped.
  4. webapp/…/components/admin/usage/ — the reports, the meters and the currency conversion.
  5. Everything else is the surface that follows from those: the admin screens, the API, the docs.

Three decisions are written down rather than left to be inferred from the code:

  • ADR 0026 — per-purpose bindings, the two purses, and exactly how tight the cap is, including what it does not bound.
  • ADR 0027 — where the outcome of a write lands when the dialog that started it is already gone.
  • docs/contributor/llm-cost-vocabulary.md — price, cost, spend, cap and budget are five different numbers, and the UI is strict about which word appears where.

How to test

Automated. CI covers the unit, architecture and integration tiers, plus the webapp and Storybook suites. Locally: 5,683 unit and 223 architecture tests, 682 webapp unit and 1,044 Storybook interaction tests, all passing.

The migration has its own integration test that builds the pre-upgrade schema, seeds representative legacy rows, runs the upgrade, and asserts that every endpoint, model name and encrypted key survived — then re-runs it and rolls it back. The changelog was additionally applied to a clean PostgreSQL 17 instance (861 change sets) to confirm it runs outside the test harness, since the test tiers use ddl-auto: create and never execute Liquibase.

By hand, against any OpenAI-compatible endpoint:

  1. As an instance admin, go to Admin → AI models, add a provider (base URL + key), and use Test & fetch models to confirm it answers. Add one of the models it lists and share it with a workspace.
  2. In that workspace, go to Admin → AI models and bind the model to practice detection and to the mentor.
  3. Trigger a review, or open the mentor and ask it something. Then open Admin → Usage — the run should appear, broken down by day and by kind of work, with its token counts and cost.
  4. Set the budget to a value below what you have already spent. The report should say the budget is reached and name which of the two it was.
  5. Trigger another review: no run is created. Ask the mentor: it replies saying the cap is reached and who can raise it.
  6. Raise the budget again. Work resumes without a restart.

Also worth a look: set HEPHAESTUS_LLM_DISPLAY_CURRENCY=EUR and reload the usage page — every amount gains a second-currency estimate that states the rate and the date it was published. Open a past month and the estimate says it is frozen. Narrow the browser to 320 px and the dialogs still fit.

What was verified live

Both paths were run end to end against a real self-hosted OpenAI-compatible model and a real GitLab instance, not mocks:

  • Practice detection made 12 provider calls on a real merge request. Token counts accumulated on the job row during the run, and the ledger recorded $0.139245 against 136,263 input and 1,491 output tokens — exact to the micro-dollar at the configured rates.
  • The run's sandbox was then killed by the host's memory limit. That is worth stating plainly, because it exercised the path that matters: the crashed run still billed exactly what had been observed, rather than recording the month as unverifiable. (The 4 GB sandbox limit is pre-existing, unchanged here, and configurable via SANDBOX_MEMORY_BYTES.)
  • The mentor streamed a real reply and metered it: 11,443 input and 101 output tokens, $0.011645 — again exact.
  • Both landed in one ledger, one row per source kind, and the workspace report showed them separately by kind of work with the live ECB rate attached.
  • With the budget set below the spend already recorded, a new review created no job and the mentor answered with the budget message. Raising it let work resume.

The GitHub and GitLab comment-lookup documents added here were also executed against both live APIs, walking a real thread backwards and finding a real marker on the first request.

Fixes you may not expect to find in a cost-control PR

Working on the metering path surfaced defects around it. Each one either loses money, hides a change, or was outright broken:

  • A mentor sandbox call made outside a conversation was served and never billed — while two comments in the code asserted the opposite. There is no row to bill it to, so it is now refused.
  • Login provider changes left no audit trail. Creating, editing or deleting a way of signing in to the instance was exempt from auditing, waiting on another ticket. They now land on the authentication trail; an edit records which fields changed, so a rotated client secret is recorded as rotated without the secret being stored.
  • A mentor turn could be reaped while still running, if an admin set a timeout past the reaper's assumed window. That window was a constant guarding a value nothing bounded; the ceiling is now real, the window derives from it, and the form explains the limit instead of posting a value the server rejects.
  • Two closed price windows could overlap, so one call could price two ways. The price history now carries an exclusion constraint.
  • GitLab merge-request approval never worked — it called a mutation GitLab has never defined, and the unit test passed because it mocked the response. Nothing calls it yet, so no user ever saw it. It was found by a new test that validates all 83 GraphQL operation documents against the checked-in schemas, which will catch the next one at build time.
  • AGENT_ENABLED now defaults to false on every runtime role. A worker started with the documented minimum environment used to claim jobs and spend money the operator believed was inert.
  • Every container in both stacks rotates its log, so a retry loop cannot fill an operator's disk.

Known limits

Stated plainly, because a cap described as tighter than it is would be worse than no cap.

The cap is eventually consistent, and the overshoot is bounded but not zero. The issue accepts this ("a small overshoot is acceptable"); ADR 0026 derives the bound and lists what it does not cover:

  • Concurrency. N workspaces crossing the line at once can overshoot by up to N × the per-call headroom.
  • A crashed mentor turn. Its spend is recorded on the conversation but does not reach the ledger until the in-flight reaper runs, so for up to 70 minutes the gate can see headroom that is already spent. Nothing is lost — it is late, not missing.
  • Providers that omit usage. A streamed call whose provider rejects stream_options.include_usage contributes nothing to the in-flight term; it is billed when the call completes.

Other boundaries:

  • EUR is the only display currency today. An unsupported value now fails startup naming what is accepted, rather than booting and silently showing USD only.
  • The GraphQL document validation carries one narrow waiver. GitHub's RequestedReviewer union conflicts with the spec's field-merging rule (User.name: String vs Team.name: String!). GitHub does not enforce it and the documents run; the waiver is scoped to that message on those paths, and any other conflict fails the build.
  • The live SCM checks are not repeatable in CI. No GitHub App credentials are available to the test tier, so the live executions described above were manual. The schema validation that runs on every build is the repeatable part.
  • Image digest pinning still covers agent-pi only — deliberately. It is the one image resolved at runtime rather than at deploy time, its digest is written by the release workflow, and a boot guard rejects anything that is not a digest. Nothing maintains a digest for the other images, and a pin nothing updates silently freezes a service on unpatched bytes. Self-hosters get an exact, CI-enforced release tag instead.
  • Per-user mentor quotas remain out of scope, as the issue specifies. The workspace budget is the backstop.

One change reads like a regression and is not: docs/runbooks/auth-cutover.md now says JWK rotation is not implemented. It never was — the previous text described a rotate() method that does not exist. This replaces a false document with an accurate one.

Upgrade notes

The database migration runs automatically. Existing AI configurations are carried into the new catalogue — endpoints, model names and encrypted keys included — and land disabled, because the endpoint some of them used lived in an environment variable the migration cannot read. Re-enable them once you have checked the endpoint is the one you expect. Any configuration that could not be carried over is named in the deploy log while the old table still exists, so nothing is lost silently.

MIGRATION.md walks through it step by step, and 13 of the 52 changesets carry an explicit Operators: line.

Checklist

  • My changeset summary reads as an operator/user-facing note (it becomes the changelog entry) — see .changeset/README.md
  • If the operator must act on this change (new required env var, manual migration step), the changeset summary says how (**Operators:** …) and MIGRATION.md is updated

Screenshots

Not attached. The admin screens are covered by Storybook — see the Chromatic build linked in the checks for the usage report, the budget meters, the currency disclosure and the mobile layouts.

Give operators and workspace admins visibility into LLM spend, and stop a
single workspace from quietly burning the whole instance budget.

Consolidate every source of LLM cost — practice detection, replay, and
mentor turns (web + Slack) — into one append-only ledger (`llm_usage_event`)
instead of reading cost back out of `chat_message.metadata` JSON. The ledger
is the accounting source of truth; the existing per-job columns and message
metadata stay as diagnostics and the streaming wire contract.

On top of the ledger:

- Monthly budget cap per workspace (`workspace.monthly_llm_budget_usd`,
  instance-admin-set, null = uncapped, 0 = paused). When the current UTC month's
  spend reaches the cap, new detection jobs and mentor turns pause until the
  month rolls over or an admin raises the cap. Enforcement lives at the two
  choke points every path funnels through, so replay, dev/bot triggers and
  Slack are all covered. Checking is eventually consistent — a small overshoot
  is accepted rather than paying for hard reservation on the hot path.
- Spend rollup APIs: a workspace admin sees their own month (by job type and
  day); an instance admin sees a metadata-only rollup across all workspaces and
  sets the cap.
- Admin UI: a per-workspace usage page and an instance-wide table with an
  inline budget editor, plus paused/over-budget and unpriced-model warnings.

Cost is resolved and sanity-checked in one place (the recorder): the runtime's
reported cost wins when plausible, else the model pricing table, else the row
is stored uncosted and surfaced (count + metric + warning) so the cap's blind
spot is visible rather than silent.

Includes a Liquibase migration that creates the ledger, adds the budget column,
and backfills both histories. Two refactors fell out of the module boundaries:
agent-job cancel/retry split into `AgentJobLifecycleService`, and the
cross-tenant admin read split into a `@WorkspaceAgnostic` service.

Part of #1354.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@FelixTJDietrich
FelixTJDietrich requested a review from a team as a code owner July 17, 2026 09:34
@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

Too many files!

This PR contains 811 files, which is 711 over the limit of 100.

To get a review, narrow the scope:
• coderabbit review --committed # exclude uncommitted changes
• coderabbit review --dir # limit to a subdirectory
• coderabbit review --base # compare against a closer base

Upgrade to a paid plan to raise the limit.

Usage-priced reviews support at most 300 files.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9f6ba7b6-2944-44fa-b500-86dfb1cff2fa

📥 Commits

Reviewing files that changed from the base of the PR and between f6cf745 and 1179c3f.

📒 Files selected for processing (877)
  • .changeset/a-paused-job-no-longer-looks-queued.md
  • .changeset/admin-console-naming.md
  • .changeset/agent-binding-redesign.md
  • .changeset/agent-queue-on-postgres.md
  • .changeset/agent-run-timeout-ceiling.md
  • .changeset/ai-console-copy-and-tall-dialogs.md
  • .changeset/audit-login-provider-changes.md
  • .changeset/bill-proxy-recorded-llm-spend.md
  • .changeset/bound-llm-cap-overshoot.md
  • .changeset/cap-remaining-container-logs.md
  • .changeset/clearer-encryption-key-length-error.md
  • .changeset/commit-enrichment-query-under-guard.md
  • .changeset/communication-practice-area-icon.md
  • .changeset/config-audit-log.md
  • .changeset/cost-control-copy-and-a11y.md
  • .changeset/feature-flag-settings-work-outside-compose.md
  • .changeset/fix-ai-console-form-and-row-state.md
  • .changeset/fix-ai-model-save-readback-and-delete-confirm.md
  • .changeset/fix-binding-listing-and-e2e.md
  • .changeset/fix-concurrent-ai-edits-and-past-month-hints.md
  • .changeset/fix-model-form-errors-and-choice-card.md
  • .changeset/fix-poll-fairness-per-purpose.md
  • .changeset/fix-readiness-lazy-load.md
  • .changeset/gitlab-issue-summary-dedup.md
  • .changeset/gitlab-merge-request-approval.md
  • .changeset/gitlab-server-follows-your-gitlab-login.md
  • .changeset/isolate-per-attempt-llm-billing.md
  • .changeset/job-model-and-single-binding-path.md
  • .changeset/jvm-memory-limits.md
  • .changeset/llm-catalog-and-budget.md
  • .changeset/llm-model-duplicate-upstream-id-409.md
  • .changeset/llm-settings-env-vars-and-role-gating.md
  • .changeset/llm-spend-display-currency.md
  • .changeset/meter-mentor-turns-live.md
  • .changeset/money-on-the-wire.md
  • .changeset/no-request-log-from-the-frontend-containers.md
  • .changeset/one-name-per-cost-number.md
  • .changeset/one-vocabulary-for-the-ai-api.md
  • .changeset/per-workspace-llm-budget.md
  • .changeset/proxy-accounting-extraction.md
  • .changeset/remove-legacy-agent-config-ui.md
  • .changeset/responsive-dialogs-and-tables.md
  • .changeset/retire-named-agent-configs.md
  • .changeset/retired-settings-are-actually-reported.md
  • .changeset/timeout-ceiling-in-the-form.md
  • .changeset/trim-ai-cost-copy.md
  • .changeset/usable-table-pagination.md
  • .changeset/webhook-mem-limit.md
  • .changeset/workspace-own-provider-cap.md
  • .gitignore
  • AGENTS.md
  • MIGRATION.md
  • docker/.env.example
  • docker/compose.app.yaml
  • docker/compose.proxy.yaml
  • docker/preview/compose.app.yaml
  • docker/self-host/.env.example
  • docs/README.md
  • docs/admin/ai-providers.mdx
  • docs/admin/compatibility-policy.mdx
  • docs/admin/dsms/dpia-prescreen.md
  • docs/admin/dsms/record-of-processing.md
  • docs/admin/install.mdx
  • docs/admin/production-setup.mdx
  • docs/admin/runtime-roles.mdx
  • docs/contributor/ai-code-review.mdx
  • docs/contributor/e2e-testing.md
  • docs/contributor/erd/schema.mmd
  • docs/contributor/evaluation-provenance.md
  • docs/contributor/instance-admin.md
  • docs/contributor/llm-cost-vocabulary.md
  • docs/contributor/local-development.mdx
  • docs/contributor/overview.mdx
  • docs/contributor/practice-catalogue.md
  • docs/contributor/practice-feedback-schema.md
  • docs/contributor/testing.mdx
  • docs/contributor/unified-pi-runtime.mdx
  • docs/contributor/workspace-context.mdx
  • docs/decisions/0005-two-role-runtime-via-conditional-on-property.md
  • docs/decisions/0006-llm-proxy-on-coordinator-trust-model.md
  • docs/decisions/0009-worker-runtime-substrate-wss-control-channel.md
  • docs/decisions/0021-findings-feedback-synthesis-seam.md
  • docs/decisions/0025-agent-job-queue-on-postgresql.md
  • docs/decisions/0026-per-purpose-agent-bindings-and-llm-governance.md
  • docs/decisions/0027-dialog-lifetime-and-where-a-write-outcome-lands.md
  • docs/decisions/README.md
  • docs/runbooks/README.md
  • docs/runbooks/auth-cutover.md
  • docs/sidebars.admin.ts
  • docs/sidebars.contributor.ts
  • scripts/e2e-setup.sh
  • scripts/jean-public-test.sh
  • server/AGENTS.md
  • server/openapi.yaml
  • server/src/main/java/de/tum/cit/aet/hephaestus/OpenAPIConfiguration.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/AgentControllerAdvice.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/CredentialMode.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/LlmProperties.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/AvailableLlmModelDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/CatalogSlug.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/CreateLlmConnectionRequestDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/CreateLlmModelRequestDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/CreateWorkspaceLlmConnectionRequestDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/CreateWorkspaceLlmModelRequestDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/EgressPolicy.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/InstanceLlmSettings.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/InstanceLlmSettingsController.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/InstanceLlmSettingsDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/InstanceLlmSettingsRepository.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/InstanceLlmSettingsService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/LlmAuthMode.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/LlmConnection.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/LlmConnectionAdminController.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/LlmConnectionDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/LlmConnectionInUseException.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/LlmConnectionProbeService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/LlmConnectionRepository.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/LlmConnectionService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/LlmConnectionSlugConflictException.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/LlmModel.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/LlmModelAdminController.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/LlmModelDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/LlmModelInUseException.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/LlmModelPrice.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/LlmModelPriceDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/LlmModelPriceRepository.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/LlmModelRepository.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/LlmModelResolver.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/LlmModelScope.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/LlmModelService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/LlmModelSlugConflictException.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/LlmModelUpstreamIdConflictException.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/LlmModelWorkspaceGrant.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/LlmModelWorkspaceGrantRepository.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/LlmPriceValidation.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/LlmProbeResultDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/LlmProbeTarget.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/ModelBindingSource.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/ModelVisibility.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/PricingMode.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/ProbeLlmConnectionRequestDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/ResolvedLlmModel.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/UpdateInstanceLlmSettingsRequestDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/UpdateLlmConnectionRequestDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/UpdateLlmModelPriceRequestDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/UpdateLlmModelRequestDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/UpdateLlmModelSharingRequestDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/UpdateWorkspaceLlmConnectionRequestDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/UpdateWorkspaceLlmModelRequestDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/WorkspaceLlmConnection.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/WorkspaceLlmConnectionController.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/WorkspaceLlmConnectionDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/WorkspaceLlmConnectionRepository.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/WorkspaceLlmConnectionService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/WorkspaceLlmConnectionSnapshot.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/WorkspaceLlmModel.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/WorkspaceLlmModelController.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/WorkspaceLlmModelDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/WorkspaceLlmModelRepository.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/WorkspaceLlmModelService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/WorkspaceLlmModelSnapshot.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/WorkspaceLlmProbeResultDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/WorkspaceLlmSettingsController.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/catalog/WorkspaceLlmSettingsDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/config/AgentBindingController.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/config/AgentBindingDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/config/AgentBindingLimits.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/config/AgentBindingRequestDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/config/AgentBindingService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/config/AgentConfig.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/config/AgentConfigBoundException.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/config/AgentConfigCheckerAdapter.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/config/AgentConfigController.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/config/AgentConfigCredentialModeException.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/config/AgentConfigDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/config/AgentConfigHasActiveJobsException.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/config/AgentConfigNameConflictException.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/config/AgentConfigRepository.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/config/AgentConfigService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/config/AgentConfigSnapshot.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/config/AgentPurpose.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/config/ConfigSnapshot.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/config/CreateAgentConfigRequestDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/config/DefaultAgentConfigProperties.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/config/DefaultAgentConfigSeeder.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/config/PracticeDetectionReadinessAdapter.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/config/UpdateAgentConfigRequestDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/config/WorkspaceAgentBinding.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/config/WorkspaceAgentBindingRepository.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/context/providers/GitDiffOperations.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/context/providers/PullRequestContentSource.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/handler/FeedbackDeliveryService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/handler/IssueReviewHandler.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/handler/PullRequestCommentPoster.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/handler/PullRequestReviewHandler.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/handler/conversation/ConversationalFeedbackPreparer.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/handler/spi/ExistingDeliveryLookup.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/handler/spi/JobTypeHandler.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/job/AgentJob.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/job/AgentJobBackoff.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/job/AgentJobCancellationReason.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/job/AgentJobController.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/job/AgentJobCreatedEvent.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/job/AgentJobDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/job/AgentJobEventListener.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/job/AgentJobExecutor.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/job/AgentJobLifecycleService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/job/AgentJobLlmUsage.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/job/AgentJobRepository.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/job/AgentJobRetentionService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/job/AgentJobService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/job/AgentJobSubmitter.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/job/AgentJobZombieSweeper.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/job/AgentNatsConfiguration.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/job/AgentNatsConsumerConfig.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/job/AgentNatsProperties.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/job/AgentProperties.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/job/AgentQueueHealthSampler.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/job/BotCommandProcessor.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/job/DevTriggerController.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/job/IssueAgentJobEventListener.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/job/OrphanedJobRef.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/job/ReviewableArtifactLoader.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/job/TerminalUsage.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/job/WorkerLivenessReporter.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/job/WorkerRegistry.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/job/WorkerRegistryRepository.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/mentor/MentorAgentProperties.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/mentor/MentorLlmConfig.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/mentor/MentorPiAdapter.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/mentor/MentorRunnerProfile.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/mentor/chat/DefaultMentorReadinessQuery.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/mentor/chat/MentorAdmissionMetadata.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/mentor/chat/MentorChatMetrics.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/mentor/chat/MentorChatService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/mentor/chat/MentorInFlightReaper.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/mentor/chat/MentorTurnPersistence.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/mentor/chat/wire/PiEventToUiChunkTranslator.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/mentor/chat/wire/TranslatorState.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/mentor/package-info.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/package-info.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/practice/PracticeAgentRequest.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/practice/PracticePiAdapter.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/practice/PracticeRunnerProfile.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/pricing/ModelPricing.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/pricing/ModelPricingRepository.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/pricing/ModelPricingService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/proxy/JobTokenAuthentication.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/proxy/JobTokenAuthenticationFilter.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/proxy/LlmProxyController.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/proxy/LlmProxyProperties.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/proxy/LlmProxySecurityConfig.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/proxy/LlmProxyWebClientConfig.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/proxy/MentorProxyCredentialRegistry.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/proxy/MentorTurnMeter.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/proxy/MentorTurnUsageAccumulator.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/proxy/ProviderProxyConfig.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/proxy/ProxyAccounting.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/proxy/ProxyBudgetGate.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/proxy/ProxyRouting.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/proxy/ProxyStreamUsageTap.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/proxy/ProxyTokenUsage.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/proxy/ProxyUsageAccumulator.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/runtime/LlmProxyAuthShell.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/runtime/PiPlanSpec.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/runtime/PiResultParser.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/runtime/PiRuntimeFactory.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/runtime/SandboxLayout.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/runtime/worker/WorkerCapacityState.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/runtime/worker/WorkerConfiguration.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/runtime/worker/WorkerDrainCoordinator.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/runtime/worker/WorkerProperties.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/runtime/worker/WorkerPropertiesConfiguration.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/sandbox/docker/DockerClientOperations.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/sandbox/docker/DockerSandboxAdapter.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/sandbox/docker/DockerSandboxConfiguration.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/sandbox/docker/SandboxContainerManager.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/sandbox/docker/SandboxReconciler.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/sandbox/docker/SandboxWorkspaceManager.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/sandbox/docker/interactive/DockerAttachedSandboxAdapter.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/sandbox/docker/interactive/DockerInteractiveSandboxAdapter.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/sandbox/docker/interactive/InteractiveSandboxRuntimeKey.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/sandbox/spi/EvictionReason.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/sandbox/spi/NetworkPolicy.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/sandbox/spi/SandboxInfrastructureException.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/sandbox/spi/SandboxManager.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/settings/AgentBindingSnapshot.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/settings/AiSettingsController.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/settings/AiSettingsService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/settings/AiSettingsViewDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/settings/UpdateAgentBindingRequestDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/AdminLlmUsageReportDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/AdminWorkspaceLlmUsageDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/AdmittedLlmModel.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/FundingSource.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/LlmAdmissionService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/LlmBudgetBlockReason.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/LlmBudgetDecision.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/LlmBudgetExhaustedException.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/LlmBudgetHeadroom.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/LlmBudgetService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/LlmBudgetVerdict.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/LlmPriceSnapshot.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/LlmUnpricedUsageBlockedException.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/LlmUsageAdminController.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/LlmUsageAdminService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/LlmUsageByDayDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/LlmUsageByJobTypeDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/LlmUsageController.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/LlmUsageEvent.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/LlmUsageEventRepository.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/LlmUsageInsert.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/LlmUsageJobType.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/LlmUsageRecorder.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/LlmUsageService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/LlmUsageSourceType.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/PricingState.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/UpdateLlmBudgetRequestDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/WorkspaceLlmBudgetController.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/WorkspaceLlmUsageReportDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/fx/EcbFxRateClient.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/fx/FxRate.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/fx/FxRateFetchScheduler.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/fx/FxRateInfoDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/fx/FxRateLookup.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/fx/FxRateRepository.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/package-info.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/core/AuditLedger.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/core/Audited.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/core/SsrfGuardedResolverGroup.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/core/TransactionCallbacks.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/core/WebClientConnectors.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/core/audit/ConfigAuditDiff.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/core/audit/ConfigAuditEvent.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/core/audit/ConfigAuditRecorder.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/core/audit/spi/ConfigAuditEntityType.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/core/audit/spi/ConfigAuditPort.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/core/audit/spi/package-info.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/core/auth/audit/AuthEvent.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/core/auth/audit/LlmCatalogAuditAdapter.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/core/auth/export/AccountExportService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/core/auth/provider/LoginProviderService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/core/auth/spi/LlmConnectionAudit.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/core/auth/spi/LlmModelAudit.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/core/auth/spi/LlmSettingsAudit.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/core/auth/spi/package-info.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/core/auth/web/AccountAdminController.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/core/auth/web/AuthLifecycleController.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/core/auth/web/LoginProviderAdminController.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/core/proxy/ProxyStreamingUtils.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/core/runtime/DeprecatedEnvVarStartupWarner.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/core/runtime/RuntimeRole.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/core/security/EncryptedStringConverter.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/core/tenancy/WorkspaceScopedTables.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/integration/core/connection/api/ConnectionController.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/integration/core/consumer/NatsConnectionProperties.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/integration/core/graphql/FragmentMergingDocumentSource.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/integration/core/spi/FeedbackChannel.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/integration/core/spi/ScmTokenSource.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/integration/core/webhook/WebhookConfiguration.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/integration/scm/domain/workdir/GitRepositoryManager.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/integration/scm/github.qkg1.topmit/CommitMetadataEnrichmentService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/integration/scm/github/feedback/GithubFeedbackChannel.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/integration/scm/github/graphql/GitHubGraphQlConfig.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/integration/scm/github/graphql/GitHubGraphQlFragments.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/integration/scm/github/workspace/GitHubScmTokenSource.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/integration/scm/github/workspace/GitHubWorkspaceProvisioningAdapter.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/integration/scm/gitlab/common/graphql/GitLabBackwardPageInfo.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/integration/scm/gitlab/feedback/GitlabApprovalChannel.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/integration/scm/gitlab/feedback/GitlabFeedbackChannel.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/integration/scm/gitlab/workspace/GitLabPreflightController.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/integration/scm/gitlab/workspace/GitLabScmTokenSource.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/mentor/ChatMessage.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/mentor/ChatMessageRepository.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/mentor/MentorTurnLlmUsage.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/PracticeCatalogController.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/review/PracticeReviewDetectionGate.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/review/PracticeReviewSettingsController.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/review/PracticeReviewSettingsDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/review/PracticeReviewSettingsService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/review/PracticeReviewSnapshot.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/review/UpdatePracticeReviewSettingsRequestDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/spi/AgentConfigChecker.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/spi/PracticeDetectionReadiness.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/spi/package-info.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/workspace/Workspace.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/workspace/WorkspaceController.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/workspace/WorkspaceMembershipController.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/workspace/WorkspaceRepository.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/workspace/WorkspaceSummaryQueryAdapter.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/workspace/dto/UpdateWorkspaceTokenRequestDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/workspace/spi/WorkspaceSummaryQuery.java
  • server/src/main/resources/agent/pi-mentor-runner.mjs
  • server/src/main/resources/agent/pi-provider.mjs
  • server/src/main/resources/agent/pi-runner.mjs
  • server/src/main/resources/application-cds-training.yml
  • server/src/main/resources/application-e2e.yml
  • server/src/main/resources/application-prod.yml
  • server/src/main/resources/application-specs.yml
  • server/src/main/resources/application-webhook.yml
  • server/src/main/resources/application-worker.yml
  • server/src/main/resources/application.yml
  • server/src/main/resources/db/changelog/1785015307013_changelog.xml
  • server/src/main/resources/db/master.xml
  • server/src/main/resources/graphql/github/fragments/CommitEnrichmentFields.graphql
  • server/src/main/resources/graphql/github/operations/GetCommitMetadata.graphql
  • server/src/main/resources/graphql/github/operations/GetIssueCommentsNewest.graphql
  • server/src/main/resources/graphql/github/operations/GetPullRequestCommentsNewest.graphql
  • server/src/main/resources/graphql/gitlab/operations/ApproveMergeRequest.graphql
  • server/src/main/resources/graphql/gitlab/operations/GetIssueNotesNewest.graphql
  • server/src/main/resources/graphql/gitlab/operations/GetMergeRequestNotesNewest.graphql
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/AgentControllerAdviceTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/AgentsPathDispatchIntegrationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/LlmPropertiesTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/catalog/CatalogSlugTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/catalog/EgressPolicyTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/catalog/InstanceLlmSettingsControllerIntegrationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/catalog/InstanceLlmSettingsServiceTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/catalog/LegacyAgentConfigMigrationIntegrationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/catalog/LlmConnectionAdminControllerIntegrationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/catalog/LlmConnectionProbeServiceTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/catalog/LlmConnectionServiceTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/catalog/LlmModelAdminControllerIntegrationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/catalog/LlmModelResolverTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/catalog/LlmModelServiceTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/catalog/LlmPriceWidthTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/catalog/WorkspaceLlmConnectionControllerIntegrationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/catalog/WorkspaceLlmConnectionServiceTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/catalog/WorkspaceLlmModelControllerIntegrationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/catalog/WorkspaceLlmModelServiceTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/catalog/WorkspaceLlmSettingsControllerIntegrationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/config/AgentBindingControllerIntegrationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/config/AgentBindingServiceTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/config/AgentBindingTimeoutRangeTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/config/AgentConfigControllerIntegrationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/config/AgentConfigServiceTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/config/AgentConfigSnapshotTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/config/ConfigSnapshotTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/config/DefaultAgentConfigSeederTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/config/PracticeDetectionReadinessAdapterTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/context/providers/PullRequestContentSourceTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/context/providers/mentor/MentorContextQueryRepositoryIntegrationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/handler/FeedbackDeliveryServiceTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/handler/PracticeDetectionDeliveryServiceIntegrationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/handler/PracticeDetectionPipelineIntegrationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/handler/PullRequestCommentPosterTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/job/AgentJobBackoffTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/job/AgentJobControllerIntegrationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/job/AgentJobDTOTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/job/AgentJobEventListenerTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/job/AgentJobExecutorTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/job/AgentJobLifecycleServiceTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/job/AgentJobPollFairnessIntegrationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/job/AgentJobRetentionServiceTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/job/AgentJobServiceTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/job/AgentJobStaleReapIntegrationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/job/AgentJobSubmissionIntegrationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/job/AgentJobZombieSweeperTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/job/AgentOrphanRecoveryIntegrationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/job/AgentOrphanRecoveryNatsIntegrationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/job/AgentPropertiesTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/job/AgentQueueHealthSamplerTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/job/AgentRoleGatingIntegrationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/job/BotCommandProcessorTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/job/IssueAgentJobEventListenerTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/job/TerminalUsageTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/job/WorkerRegistryOrphanRecoveryIntegrationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/mentor/MentorPiAdapterTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/mentor/MentorRoleGatingIntegrationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/mentor/chat/DefaultMentorReadinessQueryTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/mentor/chat/MentorAdmissionMetadataTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/mentor/chat/MentorChatControllerTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/mentor/chat/MentorChatServiceTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/mentor/chat/MentorInFlightReaperTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/mentor/chat/MentorTurnCrashBillingTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/mentor/chat/MentorTurnPersistenceCostTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/mentor/chat/MentorTurnPersistenceIntegrationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/mentor/chat/wire/PiEventToUiChunkTranslatorTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/mentor/live/MentorLiveLlmTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/mentor/live/MentorSandboxStressTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/practice/PracticePiAdapterTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/practice/live/PracticeRunnerLiveLlmTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/pricing/ModelPricingServiceTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/proxy/JobTokenAuthenticationFilterTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/proxy/JobTokenAuthenticationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/proxy/LlmProxyControllerTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/proxy/LlmProxyIntegrationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/proxy/LlmProxyWebClientConfigTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/proxy/MentorProxyCredentialRegistryTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/proxy/MentorTurnMeterCommitOrderingTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/proxy/MentorTurnUsageAccumulatorIntegrationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/proxy/ProviderProxyConfigTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/proxy/ProxyAccountingUnparseableUsageTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/proxy/ProxyBudgetGateTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/proxy/ProxyStreamUsageTapTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/proxy/ProxyUsageAccumulatorIntegrationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/runtime/LlmProxyAuthShellTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/runtime/PiPlanSpecValidationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/runtime/PiResultParserTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/runtime/PiRuntimeFactoryTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/runtime/worker/WorkerControlChannelHealthIndicatorTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/runtime/worker/testing/WorkerPropertiesFixtures.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/sandbox/docker/ContainerSecurityPolicyTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/sandbox/docker/DockerSandboxAdapterTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/sandbox/docker/DockerSandboxLiveTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/sandbox/docker/SandboxReconcilerTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/sandbox/docker/interactive/DockerInteractiveSandboxLiveTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/sandbox/spi/InteractiveSandboxSpecTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/sandbox/spi/NetworkPolicyTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/settings/AiSettingsControllerIntegrationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/settings/AiSettingsServiceTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/usage/LlmAdmissionServiceTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/usage/LlmBudgetCapConcurrencyIntegrationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/usage/LlmBudgetCapWriteTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/usage/LlmBudgetServiceTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/usage/LlmPriceSnapshotTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/usage/LlmUsageAdminControllerIntegrationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/usage/LlmUsageControllerIntegrationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/usage/LlmUsageFxDisplayIntegrationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/usage/LlmUsageInsertContractTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/usage/LlmUsageLedgerIntegrationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/usage/LlmUsageRecorderTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/usage/MoneyWirePrecisionTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/usage/fx/EcbFxRateClientTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/usage/fx/FxRateFetchSchedulerTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/usage/fx/FxRateInfoDTOTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/usage/fx/FxRateLookupTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/architecture/ActivityModuleBoundaryTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/architecture/AdvancedArchitectureTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/architecture/AuditByDefaultArchTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/architecture/AuditByDefaultGateDetectionTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/architecture/CodeQualityTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/architecture/ConfigAuditSnapshotArchTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/architecture/ConfigAuditSnapshotSecretDetectionTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/architecture/DataIsolationArchitectureTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/architecture/InstanceAdminGateExemptionTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/architecture/IntegrationConsumerBoundaryTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/architecture/IntegrationTestNamingConventionTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/architecture/LlmBudgetFxIsolationArchTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/architecture/MultiTenancyArchitectureTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/architecture/RuntimeRoleBoundaryTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/architecture/SecurityFilterChainRuntimeIntegrationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/core/SsrfGuardedResolverGroupTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/core/audit/ConfigAuditDiffTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/core/audit/ConfigAuditImmutabilityIntegrationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/core/audit/ConfigAuditIntegrationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/core/auth/export/AccountExportServiceTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/core/auth/provider/LoginProviderServiceTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/core/proxy/ProxyStreamingUtilsTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/core/runtime/DeprecatedEnvVarStartupWarnerTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/core/runtime/NoPerRequestAccessLogTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/core/runtime/WorkerProfileOverlayTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/core/security/EncryptedStringConverterTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/feature/FeatureFlagEnvBindingTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/integration/core/graphql/FragmentMergingDocumentSourceTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/integration/scm/GraphQlOperationDocumentValidationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/integration/scm/GraphQlResponseStubValidator.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/integration/scm/GraphQlResponseStubValidatorTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/integration/scm/domain/workdir/GitRepositoryManagerTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/integration/scm/github.qkg1.topmit/CommitMetadataEnrichmentServiceQueryTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/integration/scm/github.qkg1.topmon/ScopedRateLimitTrackerTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/integration/scm/github/feedback/GithubFeedbackChannelTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/integration/scm/github/feedback/NewestCommentsPageDecodingTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/integration/scm/gitlab/commit/GitLabCommitMergeRequestLinkerTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/integration/scm/gitlab/feedback/GitlabApprovalChannelTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/integration/scm/gitlab/feedback/GitlabFeedbackChannelTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/integration/scm/gitlab/feedback/GitlabInlineFindingChannelTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/integration/scm/gitlab/subissue/GitLabSubIssueSyncServiceTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/integration/scm/gitlab/sync/GitLabDeletionSweepServiceTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/integration/slack/SlackConsentLifecycleE2EIntegrationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/integration/slack/SlackHephaestusUiLinksTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/integration/slack/preferences/SlackUserPreferencesServiceTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/practices/DefaultPracticeCatalogSeederTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/practices/review/PracticeDetectionGateIntegrationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/practices/review/PracticeReviewDetectionGateTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/practices/review/PracticeReviewSettingsControllerIntegrationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/practices/review/PracticeReviewSettingsServiceTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/testconfig/LiveLlmCredentials.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/testconfig/LlmCatalogTestFixtures.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/workspace/WorkspaceScopedControllerComplianceIntegrationTest.java
  • server/src/test/resources/agent/fx/eurofxref-daily.xml
  • webapp/.storybook/preview.ts
  • webapp/AGENTS.md
  • webapp/biome.json
  • webapp/docker/nginx.conf
  • webapp/e2e/practice-detection.spec.ts
  • webapp/e2e/seed.sql
  • webapp/openapi-ts.config.ts
  • webapp/src/api/@tanstack/react-query.gen.ts
  • webapp/src/api/index.ts
  • webapp/src/api/sdk.gen.ts
  • webapp/src/api/transformers.gen.ts
  • webapp/src/api/types.gen.ts
  • webapp/src/components/achievements/AchievementHeader.tsx
  • webapp/src/components/achievements/AchievementNode.stories.tsx
  • webapp/src/components/achievements/AchievementProgressDisplay.stories.tsx
  • webapp/src/components/achievements/AchievementRarities.stories.tsx
  • webapp/src/components/achievements/AchievementSidebar.stories.tsx
  • webapp/src/components/achievements/AchievementTooltip.stories.tsx
  • webapp/src/components/achievements/AchievementsListView.stories.tsx
  • webapp/src/components/achievements/SkillTree.stories.tsx
  • webapp/src/components/achievements/story-mock-data.ts
  • webapp/src/components/admin/AdminAchievementsPage.tsx
  • webapp/src/components/admin/AdminAchievementsTable.stories.tsx
  • webapp/src/components/admin/AdminAchievementsTable.tsx
  • webapp/src/components/admin/AdminMembersPage.tsx
  • webapp/src/components/admin/AdminTeamsTable.tsx
  • webapp/src/components/admin/UsersTable.stories.tsx
  • webapp/src/components/admin/UsersTable.tsx
  • webapp/src/components/admin/ai/AgentActivityPage.stories.tsx
  • webapp/src/components/admin/ai/AgentActivityPage.tsx
  • webapp/src/components/admin/ai/AgentBindingsPage.stories.tsx
  • webapp/src/components/admin/ai/AgentBindingsPage.test.tsx
  • webapp/src/components/admin/ai/AgentBindingsPage.tsx
  • webapp/src/components/admin/ai/AgentConfigCard.stories.tsx
  • webapp/src/components/admin/ai/AgentConfigCard.tsx
  • webapp/src/components/admin/ai/AgentConfigForm.stories.tsx
  • webapp/src/components/admin/ai/AgentConfigForm.tsx
  • webapp/src/components/admin/ai/AgentJobDetailsPanel.stories.tsx
  • webapp/src/components/admin/ai/AgentJobDetailsPanel.tsx
  • webapp/src/components/admin/ai/AgentJobsTable.stories.tsx
  • webapp/src/components/admin/ai/AgentJobsTable.tsx
  • webapp/src/components/admin/ai/AgentRuntimesPage.tsx
  • webapp/src/components/admin/ai/BudgetExhaustedAlert.stories.tsx
  • webapp/src/components/admin/ai/BudgetExhaustedAlert.tsx
  • webapp/src/components/admin/ai/CredentialField.stories.tsx
  • webapp/src/components/admin/ai/CredentialField.tsx
  • webapp/src/components/admin/ai/ModelPicker.stories.tsx
  • webapp/src/components/admin/ai/ModelPicker.test.tsx
  • webapp/src/components/admin/ai/ModelPicker.tsx
  • webapp/src/components/admin/ai/PracticeDetectionPolicyCard.stories.tsx
  • webapp/src/components/admin/ai/PracticeDetectionPolicyCard.test.tsx
  • webapp/src/components/admin/ai/PracticeDetectionPolicyCard.tsx
  • webapp/src/components/admin/ai/WorkspaceLlmConnectionFormDialog.stories.tsx
  • webapp/src/components/admin/ai/WorkspaceLlmConnectionFormDialog.test.tsx
  • webapp/src/components/admin/ai/WorkspaceLlmConnectionFormDialog.tsx
  • webapp/src/components/admin/ai/WorkspaceLlmModelFormDialog.stories.tsx
  • webapp/src/components/admin/ai/WorkspaceLlmModelFormDialog.test.tsx
  • webapp/src/components/admin/ai/WorkspaceLlmModelFormDialog.tsx
  • webapp/src/components/admin/ai/WorkspaceLlmModelsTable.stories.tsx
  • webapp/src/components/admin/ai/WorkspaceLlmModelsTable.tsx
  • webapp/src/components/admin/ai/WorkspaceLlmProviderPanel.test.tsx
  • webapp/src/components/admin/ai/WorkspaceLlmProviderPanel.tsx
  • webapp/src/components/admin/ai/job-utils.test.tsx
  • webapp/src/components/admin/ai/job-utils.tsx
  • webapp/src/components/admin/ai/jobUtils.ts
  • webapp/src/components/admin/ai/story-mock-data.ts
  • webapp/src/components/admin/ai/storyMockData.ts
  • webapp/src/components/admin/ai/utils.test.ts
  • webapp/src/components/admin/ai/utils.ts
  • webapp/src/components/admin/audit-shared/AuditDateFacet.stories.tsx
  • webapp/src/components/admin/audit-shared/AuditDateFacet.tsx
  • webapp/src/components/admin/audit-shared/AuditFacetFilter.tsx
  • webapp/src/components/admin/audit-shared/AuditRefFilterPill.stories.tsx
  • webapp/src/components/admin/audit-shared/AuditRefFilterPill.tsx
  • webapp/src/components/admin/audit-shared/AuditTabs.stories.tsx
  • webapp/src/components/admin/audit-shared/AuditToolbar.stories.tsx
  • webapp/src/components/admin/audit-shared/AuditToolbar.tsx
  • webapp/src/components/admin/audit-shared/DetailRow.tsx
  • webapp/src/components/admin/audit-shared/FilterLink.tsx
  • webapp/src/components/admin/audit-shared/audit-search.test.ts
  • webapp/src/components/admin/audit-shared/audit-search.ts
  • webapp/src/components/admin/audit-shared/dedupe-by-id.test.ts
  • webapp/src/components/admin/audit-shared/dedupe-by-id.ts
  • webapp/src/components/admin/audit-shared/name-for-ref.ts
  • webapp/src/components/admin/audit-shared/pretty-json.ts
  • webapp/src/components/admin/audit-shared/ref-label.ts
  • webapp/src/components/admin/audit-shared/refLabel.ts
  • webapp/src/components/admin/audit-shared/spring-page.ts
  • webapp/src/components/admin/audit-shared/time-format.ts
  • webapp/src/components/admin/audit-shared/timeFormat.ts
  • webapp/src/components/admin/audit/AdminAuditTable.stories.tsx
  • webapp/src/components/admin/audit/AdminAuditTable.tsx
  • webapp/src/components/admin/audit/AuditEventDetailSheet.tsx
  • webapp/src/components/admin/audit/AuthAuditPanel.stories.tsx
  • webapp/src/components/admin/audit/AuthAuditPanel.tsx
  • webapp/src/components/admin/audit/audit-format.ts
  • webapp/src/components/admin/config-audit/ConfigAuditDetailSheet.stories.tsx
  • webapp/src/components/admin/config-audit/ConfigAuditDetailSheet.tsx
  • webapp/src/components/admin/config-audit/ConfigAuditPanel.stories.tsx
  • webapp/src/components/admin/config-audit/ConfigAuditPanel.tsx
  • webapp/src/components/admin/config-audit/ConfigAuditTable.stories.tsx
  • webapp/src/components/admin/config-audit/ConfigAuditTable.tsx
  • webapp/src/components/admin/config-audit/config-audit-format.test.ts
  • webapp/src/components/admin/config-audit/config-audit-format.ts
  • webapp/src/components/admin/integrations/AdminRepositoriesSettings.stories.tsx
  • webapp/src/components/admin/integrations/IntegrationOverviewCard.tsx
  • webapp/src/components/admin/integrations/RelativeTime.tsx
  • webapp/src/components/admin/integrations/SyncJobsTable.stories.tsx
  • webapp/src/components/admin/integrations/SyncJobsTable.tsx
  • webapp/src/components/admin/integrations/SyncResourcesTable.tsx
  • webapp/src/components/admin/integrations/SyncStatusHeader.tsx
  • webapp/src/components/admin/integrations/outline/OutlineConnectCard.tsx
  • webapp/src/components/admin/integrations/slack-channels/ChannelHistorySheet.tsx
  • webapp/src/components/admin/integrations/slack-channels/SlackChannelRow.tsx
  • webapp/src/components/admin/integrations/sync-format.ts
  • webapp/src/components/admin/llm/AdminLlmConnectionFormDialog.stories.tsx
  • webapp/src/components/admin/llm/AdminLlmConnectionFormDialog.test.tsx
  • webapp/src/components/admin/llm/AdminLlmConnectionFormDialog.tsx
  • webapp/src/components/admin/llm/AdminLlmConnectionsTable.stories.tsx
  • webapp/src/components/admin/llm/AdminLlmConnectionsTable.test.tsx
  • webapp/src/components/admin/llm/AdminLlmConnectionsTable.tsx
  • webapp/src/components/admin/llm/AdminLlmModelAccessDialog.stories.tsx
  • webapp/src/components/admin/llm/AdminLlmModelAccessDialog.test.tsx
  • webapp/src/components/admin/llm/AdminLlmModelAccessDialog.tsx
  • webapp/src/components/admin/llm/AdminLlmModelFormDialog.stories.tsx
  • webapp/src/components/admin/llm/AdminLlmModelFormDialog.test.tsx
  • webapp/src/components/admin/llm/AdminLlmModelFormDialog.tsx
  • webapp/src/components/admin/llm/AdminLlmModelsSection.stories.tsx
  • webapp/src/components/admin/llm/AdminLlmModelsSection.test.tsx
  • webapp/src/components/admin/llm/AdminLlmModelsSection.tsx
  • webapp/src/components/admin/llm/InstanceLlmSettingsCard.stories.tsx
  • webapp/src/components/admin/llm/InstanceLlmSettingsCard.test.tsx
  • webapp/src/components/admin/llm/InstanceLlmSettingsCard.tsx
  • webapp/src/components/admin/llm/LlmConnectionFields.tsx
  • webapp/src/components/admin/llm/LlmModelFields.tsx
  • webapp/src/components/admin/llm/ModelAccessScopeChoice.stories.tsx
  • webapp/src/components/admin/llm/ModelAccessScopeChoice.tsx
  • webapp/src/components/admin/llm/PriceModeEditor.stories.tsx
  • webapp/src/components/admin/llm/PriceModeEditor.test.tsx
  • webapp/src/components/admin/llm/PriceModeEditor.tsx
  • webapp/src/components/admin/llm/workspace-options.ts
  • webapp/src/components/admin/login-providers/LoginProvidersTable.stories.tsx
  • webapp/src/components/admin/login-providers/LoginProvidersTable.tsx
  • webapp/src/components/admin/practices/AreaVisualPicker.tsx
  • webapp/src/components/admin/practices/PracticeForm.stories.tsx
  • webapp/src/components/admin/practices/PracticeForm.tsx
  • webapp/src/components/admin/practices/area-visuals.test.ts
  • webapp/src/components/admin/practices/area-visuals.ts
  • webapp/src/components/admin/practices/story-mock-data.ts
  • webapp/src/components/admin/usage/AdminInstanceLlmUsageTable.stories.tsx
  • webapp/src/components/admin/usage/AdminInstanceLlmUsageTable.test.tsx
  • webapp/src/components/admin/usage/AdminInstanceLlmUsageTable.tsx
  • webapp/src/components/admin/usage/AdminLlmUsagePage.stories.tsx
  • webapp/src/components/admin/usage/AdminLlmUsagePage.test.tsx
  • webapp/src/components/admin/usage/AdminLlmUsagePage.tsx
  • webapp/src/components/admin/usage/BudgetAmountDialog.stories.tsx
  • webapp/src/components/admin/usage/BudgetAmountDialog.test.tsx
  • webapp/src/components/admin/usage/BudgetAmountDialog.tsx
  • webapp/src/components/admin/usage/BudgetPaceAlert.stories.tsx
  • webapp/src/components/admin/usage/BudgetPaceAlert.tsx
  • webapp/src/components/admin/usage/CapIsNotMonthScoped.tsx
  • webapp/src/components/admin/usage/CapMeter.stories.tsx
  • webapp/src/components/admin/usage/CapMeter.test.tsx
  • webapp/src/components/admin/usage/CapMeter.tsx
  • webapp/src/components/admin/usage/LlmUsageBreakdownTables.stories.tsx
  • webapp/src/components/admin/usage/LlmUsageBreakdownTables.test.tsx
  • webapp/src/components/admin/usage/LlmUsageBreakdownTables.tsx
  • webapp/src/components/admin/usage/MonthNavigator.stories.tsx
  • webapp/src/components/admin/usage/MonthNavigator.tsx
  • webapp/src/components/admin/usage/SetBudgetDialog.stories.tsx
  • webapp/src/components/admin/usage/SetBudgetDialog.tsx
  • webapp/src/components/admin/usage/SetOwnProviderBudgetDialog.stories.tsx
  • webapp/src/components/admin/usage/SetOwnProviderBudgetDialog.tsx
  • webapp/src/components/admin/usage/fx.test.tsx
  • webapp/src/components/admin/usage/fx.tsx
  • webapp/src/components/admin/usage/usage-search.test.ts
  • webapp/src/components/admin/usage/usage-search.ts
  • webapp/src/components/admin/usage/usage-utils.test.ts
  • webapp/src/components/admin/usage/usage-utils.ts
  • webapp/src/components/auth/DevSignInForm.tsx
  • webapp/src/components/common/ConfirmDialog.stories.tsx
  • webapp/src/components/common/ConfirmDialog.test.tsx
  • webapp/src/components/common/ConfirmDialog.tsx
  • webapp/src/components/common/DetailRow.tsx
  • webapp/src/components/common/FacetMultiSelect.stories.tsx
  • webapp/src/components/common/FacetMultiSelect.tsx
  • webapp/src/components/common/RelativeTime.stories.tsx
  • webapp/src/components/common/RelativeTime.tsx
  • webapp/src/components/common/TablePagination.stories.tsx
  • webapp/src/components/common/TablePagination.tsx
  • webapp/src/components/core/Footer.tsx
  • webapp/src/components/core/Header.stories.tsx
  • webapp/src/components/core/Header.tsx
  • webapp/src/components/core/sidebar/AppSidebar.stories.tsx
  • webapp/src/components/core/sidebar/AppSidebar.tsx
  • webapp/src/components/core/sidebar/NavAdmin.tsx
  • webapp/src/components/core/sidebar/NavMentor.tsx
  • webapp/src/components/core/sidebar/NavMentorThreads.tsx
  • webapp/src/components/core/sidebar/NavSuperAdmin.tsx
  • webapp/src/components/core/sidebar/WorkspaceSwitcher.stories.tsx
  • webapp/src/components/core/sidebar/WorkspaceSwitcher.tsx
  • webapp/src/components/core/sidebar/admin-nav-labels.ts
  • webapp/src/components/settings/DangerZoneSection.tsx
  • webapp/src/components/settings/LinkedAccountsSection.tsx
  • webapp/src/components/settings/SessionsSection.tsx
  • webapp/src/components/ui/alert-dialog.tsx
  • webapp/src/components/ui/dialog.tsx
  • webapp/src/components/ui/sonner.tsx
  • webapp/src/environment/index.ts
  • webapp/src/hooks/use-mentor-chat.test.tsx
  • webapp/src/hooks/use-mentor-chat.ts
  • webapp/src/hooks/use-pending-mutation-ids.test.tsx
  • webapp/src/hooks/use-pending-mutation-ids.ts
  • webapp/src/hooks/use-workspace-access.ts
  • webapp/src/hooks/useMentorChat.test.tsx
  • webapp/src/integrations/auth/AuthContext.tsx
  • webapp/src/integrations/auth/account-deleted-notice.ts
  • webapp/src/integrations/auth/auth-client.test.ts
  • webapp/src/integrations/auth/auth-client.ts
  • webapp/src/integrations/auth/guard.ts
  • webapp/src/integrations/auth/index.ts
  • webapp/src/integrations/auth/session-expiry.test.ts
  • webapp/src/integrations/auth/session-expiry.ts
  • webapp/src/integrations/auth/session-refresh.ts
  • webapp/src/integrations/auth/use-session-keep-alive.test.tsx
  • webapp/src/integrations/auth/use-session-keep-alive.ts
  • webapp/src/lib/admin-llm-model-save.test.ts
  • webapp/src/lib/admin-llm-model-save.ts
  • webapp/src/lib/dates.test.ts
  • webapp/src/lib/dates.ts
  • webapp/src/lib/llm-form-validation.test.ts
  • webapp/src/lib/llm-form-validation.ts
  • webapp/src/lib/llm-pricing.test.ts
  • webapp/src/lib/llm-pricing.ts
  • webapp/src/lib/llm-provider-type.test.ts
  • webapp/src/lib/llm-provider-type.ts
  • webapp/src/lib/money.test.ts
  • webapp/src/lib/money.ts
  • webapp/src/lib/page-title.ts
  • webapp/src/lib/problem-detail.test.ts
  • webapp/src/lib/problem-detail.ts
  • webapp/src/lib/relative-time.test.ts
  • webapp/src/lib/relative-time.ts
  • webapp/src/lib/version.test.ts
  • webapp/src/lib/version.ts
  • webapp/src/lib/workspace-roles.test.ts
  • webapp/src/lib/workspace-roles.ts
  • webapp/src/main.tsx
  • webapp/src/routeTree.gen.ts
  • webapp/src/routes/__root.tsx
  • webapp/src/routes/_authenticated/-admin-login-providers-route.test.tsx
  • webapp/src/routes/_authenticated/-admin-models-route.test.tsx
  • webapp/src/routes/_authenticated/-admin-route.test.ts
  • webapp/src/routes/_authenticated/-admin-usage-route.test.tsx
  • webapp/src/routes/_authenticated/admin.audit.tsx
  • webapp/src/routes/_authenticated/admin.login-providers.tsx
  • webapp/src/routes/_authenticated/admin.models.tsx
  • webapp/src/routes/_authenticated/admin.usage.tsx
  • webapp/src/routes/_authenticated/admin.users.tsx
  • webapp/src/routes/_authenticated/admin.workspaces.tsx
  • webapp/src/routes/_authenticated/w/$workspaceSlug/admin/-models-route.test.tsx
  • webapp/src/routes/_authenticated/w/$workspaceSlug/admin/-route.test.ts
  • webapp/src/routes/_authenticated/w/$workspaceSlug/admin/-settings-route.test.tsx
  • webapp/src/routes/_authenticated/w/$workspaceSlug/admin/-usage-route.test.tsx
  • webapp/src/routes/_authenticated/w/$workspaceSlug/admin/achievement-designer.tsx
  • webapp/src/routes/_authenticated/w/$workspaceSlug/admin/achievements.tsx
  • webapp/src/routes/_authenticated/w/$workspaceSlug/admin/ai/agents.tsx
  • webapp/src/routes/_authenticated/w/$workspaceSlug/admin/ai/practice-detection.tsx
  • webapp/src/routes/_authenticated/w/$workspaceSlug/admin/audit.tsx
  • webapp/src/routes/_authenticated/w/$workspaceSlug/admin/integrations.tsx
  • webapp/src/routes/_authenticated/w/$workspaceSlug/admin/integrations/index.tsx
  • webapp/src/routes/_authenticated/w/$workspaceSlug/admin/integrations/outline.tsx
  • webapp/src/routes/_authenticated/w/$workspaceSlug/admin/integrations/scm.tsx
  • webapp/src/routes/_authenticated/w/$workspaceSlug/admin/integrations/slack.tsx
  • webapp/src/routes/_authenticated/w/$workspaceSlug/admin/members.tsx
  • webapp/src/routes/_authenticated/w/$workspaceSlug/admin/models.tsx
  • webapp/src/routes/_authenticated/w/$workspaceSlug/admin/practices.tsx
  • webapp/src/routes/_authenticated/w/$workspaceSlug/admin/practices/$practiceSlug.tsx
  • webapp/src/routes/_authenticated/w/$workspaceSlug/admin/practices/index.tsx
  • webapp/src/routes/_authenticated/w/$workspaceSlug/admin/practices/new.tsx
  • webapp/src/routes/_authenticated/w/$workspaceSlug/admin/practices/runs.tsx
  • webapp/src/routes/_authenticated/w/$workspaceSlug/admin/practices/settings.tsx
  • webapp/src/routes/_authenticated/w/$workspaceSlug/admin/route.tsx
  • webapp/src/routes/_authenticated/w/$workspaceSlug/admin/settings.tsx
  • webapp/src/routes/_authenticated/w/$workspaceSlug/admin/teams.tsx
  • webapp/src/routes/_authenticated/w/$workspaceSlug/admin/usage.tsx
  • webapp/src/routes/_authenticated/w/$workspaceSlug/mentor/$threadId.tsx
  • webapp/src/routes/login.tsx
  • webapp/src/styles.css
  • webapp/src/test/budget-amount-field.ts
  • webapp/src/test/controls.ts
  • webapp/src/test/reflow.test.tsx
  • webapp/src/test/reflow.tsx
  • webapp/src/test/router-harness.tsx
  • webapp/src/test/setup-msw.ts
  • webapp/src/test/toast-politeness.test.tsx

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

📝 Walkthrough

Walkthrough

Adds a unified LLM usage ledger with monthly workspace caps, enforcement for agent and mentor flows, audited budget administration, reporting APIs, and admin/workspace usage pages with generated client bindings and tests.

Changes

LLM budget governance

Layer / File(s) Summary
Ledger and budget data model
server/src/main/java/.../usage/*, server/src/main/resources/db/..., server/src/main/java/.../Workspace.java, server/openapi.yaml
Adds append-only usage events, monthly workspace caps, UTC rollups, audit metadata, API schemas, and idempotent historical backfills.
Usage recording and enforcement
server/src/main/java/.../AgentJobExecutor.java, server/src/main/java/.../MentorTurnPersistence.java, server/src/main/java/.../LlmUsageRecorder.java, server/src/main/java/.../LlmBudgetService.java
Records agent and mentor usage, resolves missing pricing, detects cap exhaustion, and blocks new work when exhausted.
Job lifecycle separation
server/src/main/java/.../AgentJobLifecycleService.java, server/src/main/java/.../AgentJobService.java, server/src/main/java/.../AgentJobController.java
Moves cancellation and delivery retry operations into a lifecycle service while adding submission budget gating.
Usage reporting APIs
server/src/main/java/.../usage/*, server/src/test/java/.../usage/*
Adds workspace reports, instance-admin rollups, budget updates, audit recording, validation, authorization, and integration coverage.
Web usage experience
webapp/src/api/*, webapp/src/routes/.../usage.tsx, webapp/src/components/admin/usage/*, webapp/src/components/core/sidebar/*
Adds generated client bindings, authenticated usage routes, month navigation, budget editing, report views, spend tables, alerts, and Storybook scenarios.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related issues

Possibly related PRs

Suggested labels: infrastructure

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 1368-workspace-production-readiness

@github-actions github-actions Bot added documentation Improvements or additions to documentation application-server Spring Boot server: APIs, business logic, database webapp React app: UI components, routes, state management size:XXL feature New feature or enhancement labels Jul 17, 2026
@github-actions

github-actions Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

📚 Documentation Preview

Preview has been removed (PR closed)

…tion-readiness

# Conflicts:
#	server/openapi.yaml
#	server/src/main/resources/db/master.xml
#	server/src/test/java/de/tum/cit/aet/hephaestus/agent/job/AgentJobServiceTest.java
#	webapp/src/api/@tanstack/react-query.gen.ts
#	webapp/src/api/index.ts
#	webapp/src/api/sdk.gen.ts
#	webapp/src/api/transformers.gen.ts
#	webapp/src/api/types.gen.ts
#	webapp/src/components/core/sidebar/NavAdmin.tsx
#	webapp/src/routeTree.gen.ts
#	webapp/src/routes/_authenticated/w/$workspaceSlug/admin/usage.tsx
@FelixTJDietrich FelixTJDietrich changed the title feat(workspace): per-workspace LLM cost rollup and monthly budget cap feat(workspace): per-workspace AI spend rollup and monthly budget cap Jul 20, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (6)
server/src/main/java/de/tum/cit/aet/hephaestus/agent/job/AgentJobLifecycleService.java (1)

190-201: 🚀 Performance & Scalability | 🔵 Trivial | ⚖️ Poor tradeoff

External/blocking calls run inside the @Transactional cancel.

Unlike retryDelivery, which deliberately pushes delivery outside the transaction, cancel performs the WSS dispatch(...) and sandboxManager.cancel(...) while the method-level transaction is still open, holding a DB connection during a potentially slow container stop / network call. Cancel is admin-initiated and infrequent, so impact is limited, but consider moving the best-effort sandbox/worker signalling after the transaction commits for consistency with the delivery path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@server/src/main/java/de/tum/cit/aet/hephaestus/agent/job/AgentJobLifecycleService.java`
around lines 190 - 201, Move the best-effort cancellation calls from the
transactional cancel flow into an after-commit path, matching the existing
retryDelivery pattern. Ensure workerJobCancelDispatcher.dispatch and
sandboxManager.cancel run only after the database cancellation transition
commits, while preserving their current no-op and exception-handling behavior.
server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/LlmUsageEventRepository.java (1)

66-80: 🚀 Performance & Scalability | 🔵 Trivial

Consider pagination for the cross-tenant admin rollup.

aggregateByWorkspace (and the /admin/llm-usage endpoint behind it) returns one row per workspace with no limit. Fine at small instance scale, but worth paging (similar to the existing /admin/config-audit page/size pattern) before instance workspace counts grow, to avoid loading the full workspace table into memory on every admin usage view.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/LlmUsageEventRepository.java`
around lines 66 - 80, Update LlmUsageEventRepository.aggregateByWorkspace and
the underlying /admin/llm-usage flow to support page/size pagination, following
the existing /admin/config-audit pattern. Apply pagination to the grouped
workspace aggregate query while preserving the current date filtering,
zero-usage LEFT JOIN behavior, and cost-descending order.
server/src/test/java/de/tum/cit/aet/hephaestus/agent/usage/LlmUsageLedgerIntegrationTest.java (1)

71-214: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

New test methods across the ledger/budget test files don't follow the should[ExpectedBehavior]When[Condition] naming convention. All three new test files name their test methods descriptively (e.g. recordAppendsOneLedgerRow, spendAtBudgetIsExhausted, overBudgetFlagFlipsWhenSpendReachesTheCap) rather than using the repository's mandated pattern.

  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/usage/LlmUsageLedgerIntegrationTest.java#L71-L214: rename methods such as recordAppendsOneLedgerRowshouldAppendLedgerRowWhenUsageIsRecorded, crossingTheBudgetFiresTheExhaustedCounterOnceshouldIncrementExhaustedCounterOnceWhenBudgetIsCrossed, etc.
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/usage/LlmBudgetServiceTest.java#L51-L103: rename methods such as spendAtBudgetIsExhaustedshouldBeExhaustedWhenSpendEqualsBudget, windowIsHalfOpenUtcCalendarMonthshouldReturnHalfOpenUtcWindowWhenGivenAMonth.
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/usage/LlmUsageControllerIntegrationTest.java#L66-L165: rename methods such as reportRollsUpTheRequestedMonthByJobTypeAndDayshouldRollUpUsageByJobTypeAndDayWhenMonthIsRequested, plainMemberIsForbiddenshouldReturnForbiddenWhenPlainMemberRequestsReport.

As per path instructions, "Name tests using should[ExpectedBehavior]When[Condition]."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@server/src/test/java/de/tum/cit/aet/hephaestus/agent/usage/LlmUsageLedgerIntegrationTest.java`
around lines 71 - 214, Rename every new test method to follow the
should[ExpectedBehavior]When[Condition] convention. Update methods in
server/src/test/java/de/tum/cit/aet/hephaestus/agent/usage/LlmUsageLedgerIntegrationTest.java
(lines 71-214), LlmBudgetServiceTest.java (lines 51-103), and
LlmUsageControllerIntegrationTest.java (lines 66-165), preserving each test’s
behavior and using names such as shouldAppendLedgerRowWhenUsageIsRecorded,
shouldBeExhaustedWhenSpendEqualsBudget, and
shouldReturnForbiddenWhenPlainMemberRequestsReport.

Source: Path instructions

server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/LlmUsageAdminController.java (1)

46-59: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract the shared month @Pattern/parse logic. Both LlmUsageAdminController.list and LlmUsageController.getReport repeat the identical "\\d{4}-(0[1-9]|1[0-2])" regex and YearMonth.parse(month) : YearMonth.now(ZoneOffset.UTC) fallback — a single shared constant/helper would prevent the two validators from drifting apart.

  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/LlmUsageAdminController.java#L46-L59: replace the inline @Pattern regex and YearMonth.parse/now fallback in list(...) with a call to a shared helper (e.g. a small MonthParam utility or a custom @ValidMonth annotation).
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/LlmUsageController.java#L36-L48: apply the same shared helper in getReport(...).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/LlmUsageAdminController.java`
around lines 46 - 59, Extract the duplicated month validation and parsing into
one shared helper or value type, then update LlmUsageAdminController.list in
server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/LlmUsageAdminController.java
lines 46-59 and LlmUsageController.getReport in
server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/LlmUsageController.java
lines 36-48 to use it. The shared logic must retain the ISO yyyy-MM validation
and UTC current-month fallback.
server/src/test/java/de/tum/cit/aet/hephaestus/agent/job/AgentJobExecutorTest.java (1)

61-63: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

No test verifies usageRecorder is actually invoked.

The mock is wired into every constructor call, but none of the tests (e.g. shouldCompleteJobSuccessfully) assert that usageRecorder.record(...) is called with the expected job/workspace/cost data after a completed run. This is exactly the new ledger-writing behavior this cohort adds to AgentJobExecutor.

Want me to draft a verify(usageRecorder).record(...) assertion for the success/failure paths once AgentJobExecutor's exact call site is confirmed?

Also applies to: 348-376

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@server/src/test/java/de/tum/cit/aet/hephaestus/agent/job/AgentJobExecutorTest.java`
around lines 61 - 63, Add assertions to AgentJobExecutorTest, especially
shouldCompleteJobSuccessfully and the relevant failure-path test, verifying
usageRecorder.record(...) is invoked with the expected job, workspace, and cost
data after execution. Use Mockito verify against the existing usageRecorder mock
and cover both successful and failed runs as applicable.
server/src/main/java/de/tum/cit/aet/hephaestus/agent/mentor/chat/MentorChatMetrics.java (1)

110-114: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider a dedicated Outcome.BUDGET_BLOCKED instead of falling through to ERROR.

recordBudgetBlocked() is a good specific signal, but the turn's terminal outcome (recorded in MentorChatService.dispatchTurn's generic catch (RuntimeException e)) still lands in Outcome.ERROR, diluting that bucket with expected budget-cap rejections instead of real failures.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@server/src/main/java/de/tum/cit/aet/hephaestus/agent/mentor/chat/MentorChatMetrics.java`
around lines 110 - 114, Add a dedicated Outcome.BUDGET_BLOCKED and update
MentorChatService.dispatchTurn so budget-cap rejections are classified with that
outcome rather than Outcome.ERROR. Ensure the generic RuntimeException path
continues to record Outcome.ERROR for genuine failures, while the existing
recordBudgetBlocked metric remains intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.changeset/per-workspace-llm-budget.md:
- Around line 5-17: Add an “Operators:” section to the changeset explicitly
stating whether deployment, migration, or configuration action is required; if
none is needed, state that no operator action is required.

In
`@server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/LlmBudgetExhaustedException.java`:
- Around line 10-17: Update the LlmBudgetExhaustedException constructor message
to remove the raw workspaceId from the user-facing text, while preserving the
existing budget guidance; keep workspace identification available only through
server-side logging if needed.

In
`@server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/LlmUsageService.java`:
- Around line 38-53: Update the aggregation mapping in LlmUsageService to
tolerate unknown or renamed job_type values instead of calling
LlmUsageJobType.valueOf(row.getJobType()) directly. Add the required database
validation or use an established fallback/nullable conversion so one
unrecognized value cannot fail the entire workspace usage report, while
preserving normal enum mapping for known values.

In
`@server/src/test/java/de/tum/cit/aet/hephaestus/agent/usage/LlmUsageAdminControllerIntegrationTest.java`:
- Around line 58-148: Rename the four test methods in the admin usage
integration test to follow the should[ExpectedBehavior]When[Condition]
convention: adminSeesPerWorkspaceSpendIncludingZeroSpendWorkspaces,
adminSetsAndClearsTheBudgetCap, negativeBudgetIsRejectedWith400, and
nonAdminIsForbidden. Preserve each test’s behavior and assertions.

---

Nitpick comments:
In
`@server/src/main/java/de/tum/cit/aet/hephaestus/agent/job/AgentJobLifecycleService.java`:
- Around line 190-201: Move the best-effort cancellation calls from the
transactional cancel flow into an after-commit path, matching the existing
retryDelivery pattern. Ensure workerJobCancelDispatcher.dispatch and
sandboxManager.cancel run only after the database cancellation transition
commits, while preserving their current no-op and exception-handling behavior.

In
`@server/src/main/java/de/tum/cit/aet/hephaestus/agent/mentor/chat/MentorChatMetrics.java`:
- Around line 110-114: Add a dedicated Outcome.BUDGET_BLOCKED and update
MentorChatService.dispatchTurn so budget-cap rejections are classified with that
outcome rather than Outcome.ERROR. Ensure the generic RuntimeException path
continues to record Outcome.ERROR for genuine failures, while the existing
recordBudgetBlocked metric remains intact.

In
`@server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/LlmUsageAdminController.java`:
- Around line 46-59: Extract the duplicated month validation and parsing into
one shared helper or value type, then update LlmUsageAdminController.list in
server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/LlmUsageAdminController.java
lines 46-59 and LlmUsageController.getReport in
server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/LlmUsageController.java
lines 36-48 to use it. The shared logic must retain the ISO yyyy-MM validation
and UTC current-month fallback.

In
`@server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/LlmUsageEventRepository.java`:
- Around line 66-80: Update LlmUsageEventRepository.aggregateByWorkspace and the
underlying /admin/llm-usage flow to support page/size pagination, following the
existing /admin/config-audit pattern. Apply pagination to the grouped workspace
aggregate query while preserving the current date filtering, zero-usage LEFT
JOIN behavior, and cost-descending order.

In
`@server/src/test/java/de/tum/cit/aet/hephaestus/agent/job/AgentJobExecutorTest.java`:
- Around line 61-63: Add assertions to AgentJobExecutorTest, especially
shouldCompleteJobSuccessfully and the relevant failure-path test, verifying
usageRecorder.record(...) is invoked with the expected job, workspace, and cost
data after execution. Use Mockito verify against the existing usageRecorder mock
and cover both successful and failed runs as applicable.

In
`@server/src/test/java/de/tum/cit/aet/hephaestus/agent/usage/LlmUsageLedgerIntegrationTest.java`:
- Around line 71-214: Rename every new test method to follow the
should[ExpectedBehavior]When[Condition] convention. Update methods in
server/src/test/java/de/tum/cit/aet/hephaestus/agent/usage/LlmUsageLedgerIntegrationTest.java
(lines 71-214), LlmBudgetServiceTest.java (lines 51-103), and
LlmUsageControllerIntegrationTest.java (lines 66-165), preserving each test’s
behavior and using names such as shouldAppendLedgerRowWhenUsageIsRecorded,
shouldBeExhaustedWhenSpendEqualsBudget, and
shouldReturnForbiddenWhenPlainMemberRequestsReport.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a451215b-27e4-454f-8aeb-3cd1562bdb72

📥 Commits

Reviewing files that changed from the base of the PR and between 8e3618c and a5987bc.

📒 Files selected for processing (54)
  • .changeset/per-workspace-llm-budget.md
  • docs/contributor/erd/schema.mmd
  • server/openapi.yaml
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/job/AgentJobController.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/job/AgentJobExecutor.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/job/AgentJobLifecycleService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/job/AgentJobService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/mentor/chat/MentorChatMetrics.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/mentor/chat/MentorChatService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/mentor/chat/MentorTurnPersistence.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/LlmBudgetExhaustedException.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/LlmBudgetService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/LlmUsageAdminController.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/LlmUsageAdminService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/LlmUsageController.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/LlmUsageDTOs.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/LlmUsageEvent.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/LlmUsageEventRepository.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/LlmUsageJobType.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/LlmUsageRecorder.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/LlmUsageService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/core/audit/spi/ConfigAuditEntityType.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/workspace/Workspace.java
  • server/src/main/resources/db/changelog/1784534949268_changelog.xml
  • server/src/main/resources/db/master.xml
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/job/AgentJobExecutorTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/job/AgentJobLifecycleServiceTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/job/AgentJobServiceTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/mentor/chat/MentorChatServiceTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/mentor/chat/MentorTurnPersistenceCostTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/usage/LlmBudgetServiceTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/usage/LlmUsageAdminControllerIntegrationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/usage/LlmUsageControllerIntegrationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/usage/LlmUsageLedgerIntegrationTest.java
  • webapp/src/api/@tanstack/react-query.gen.ts
  • webapp/src/api/index.ts
  • webapp/src/api/sdk.gen.ts
  • webapp/src/api/transformers.gen.ts
  • webapp/src/api/types.gen.ts
  • webapp/src/components/admin/config-audit/configAuditFormat.ts
  • webapp/src/components/admin/usage/AdminInstanceLlmUsageTable.stories.tsx
  • webapp/src/components/admin/usage/AdminInstanceLlmUsageTable.tsx
  • webapp/src/components/admin/usage/AdminLlmUsagePage.stories.tsx
  • webapp/src/components/admin/usage/AdminLlmUsagePage.tsx
  • webapp/src/components/admin/usage/MonthNavigator.stories.tsx
  • webapp/src/components/admin/usage/MonthNavigator.tsx
  • webapp/src/components/admin/usage/SetBudgetDialog.stories.tsx
  • webapp/src/components/admin/usage/SetBudgetDialog.tsx
  • webapp/src/components/admin/usage/usageUtils.ts
  • webapp/src/components/core/sidebar/NavAdmin.tsx
  • webapp/src/components/core/sidebar/NavSuperAdmin.tsx
  • webapp/src/routeTree.gen.ts
  • webapp/src/routes/_authenticated/admin.usage.tsx
  • webapp/src/routes/_authenticated/w/$workspaceSlug/admin/usage.tsx

Comment thread .changeset/per-workspace-llm-budget.md Outdated
Comment on lines +5 to +17
Instance administrators can now see what each workspace spent on AI in a given month, and set a
monthly spending cap per workspace. Once a workspace reaches its cap, practice detection and mentor
replies pause for the rest of the month — so one runaway workspace can no longer quietly consume the
whole instance's AI budget — and they resume on their own when the next month begins or when an
administrator raises the cap. Changes to a cap are recorded in the audit log alongside other
administrative changes.

Workspace administrators get a matching view for their own workspace under Administration →
"Usage": total spend for the month, a breakdown by day and by kind of work (pull-request reviews,
issue reviews, conversation reviews, and mentor conversations), and their current cap, which they can
see but not raise. Mentor conversations are included in these totals for the first time. Where a
model has no price on record, the affected calls are counted separately and flagged, so it is clear
when a total is understated rather than silently wrong.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add the required **Operators:** line.

The changeset must state any deployment, migration, or configuration action required from operators. If none is required, explicitly say so.

Proposed addition
 Where a
 model has no price on record, the affected calls are counted separately and flagged, so it is clear
 when a total is understated rather than silently wrong.
+
+**Operators:** Run the standard database migrations; no additional configuration is required.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Instance administrators can now see what each workspace spent on AI in a given month, and set a
monthly spending cap per workspace. Once a workspace reaches its cap, practice detection and mentor
replies pause for the rest of the month — so one runaway workspace can no longer quietly consume the
whole instance's AI budget — and they resume on their own when the next month begins or when an
administrator raises the cap. Changes to a cap are recorded in the audit log alongside other
administrative changes.
Workspace administrators get a matching view for their own workspace under Administration →
"Usage": total spend for the month, a breakdown by day and by kind of work (pull-request reviews,
issue reviews, conversation reviews, and mentor conversations), and their current cap, which they can
see but not raise. Mentor conversations are included in these totals for the first time. Where a
model has no price on record, the affected calls are counted separately and flagged, so it is clear
when a total is understated rather than silently wrong.
Instance administrators can now see what each workspace spent on AI in a given month, and set a
monthly spending cap per workspace. Once a workspace reaches its cap, practice detection and mentor
replies pause for the rest of the month — so one runaway workspace can no longer quietly consume the
whole instance's AI budget — and they resume on their own when the next month begins or when an
administrator raises the cap. Changes to a cap are recorded in the audit log alongside other
administrative changes.
Workspace administrators get a matching view for their own workspace under Administration →
"Usage": total spend for the month, a breakdown by day and by kind of work (pull-request reviews,
issue reviews, conversation reviews, and mentor conversations), and their current cap, which they can
see but not raise. Mentor conversations are included in these totals for the first time. Where a
model has no price on record, the affected calls are counted separately and flagged, so it is clear
when a total is understated rather than silently wrong.
**Operators:** Run the standard database migrations; no additional configuration is required.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.changeset/per-workspace-llm-budget.md around lines 5 - 17, Add an
“Operators:” section to the changeset explicitly stating whether deployment,
migration, or configuration action is required; if none is needed, state that no
operator action is required.

Source: Coding guidelines

FelixTJDietrich and others added 2 commits July 20, 2026 11:25
Native `min`/`step` constraint validation silently blocked submit before the
form's own handler ran, so the field's explanation of *why* a value was
rejected never rendered — the browser showed a bubble instead. The form now
carries `noValidate` and owns every rule (empty, negative, sub-cent), so all
rejections surface through the same `FieldError`.

Caught by the Storybook test tier, which the earlier UI pass hadn't run.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Found by deploying to the test instance: the runtime reported `costUsd: 0`
for a job that burned 4.3M input tokens, and `PiResultParser` also defaults
an *absent* cost to 0.0. The recorder trusted that zero, so real spend landed
in the ledger as $0 and the monthly cap could never fire — the exact blind
spot the ledger exists to remove.

A zero alongside non-zero tokens now falls through to the model pricing table,
and to the uncosted path when the model has no price on record. A genuine zero
with no tokens is still recorded as zero.

Verified live: the same review now resolves $0.513384 from the pricing table.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@server/src/test/java/de/tum/cit/aet/hephaestus/agent/usage/LlmUsageLedgerIntegrationTest.java`:
- Around line 199-232: Rename the affected test methods in
LlmUsageLedgerIntegrationTest to follow the
should[ExpectedBehavior]When[Condition] convention, including
reportedZeroCostWithRealTokensFallsBackToPricingInsteadOfBillingZero and
reportedZeroCostWithNoTokensStaysZero. Preserve each test’s existing behavior
and assertions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 719c8ce5-12e3-4f48-80fa-e00fe8b26e65

📥 Commits

Reviewing files that changed from the base of the PR and between a5987bc and f6cf745.

📒 Files selected for processing (4)
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/LlmUsageRecorder.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/usage/LlmUsageLedgerIntegrationTest.java
  • webapp/src/components/admin/usage/SetBudgetDialog.stories.tsx
  • webapp/src/components/admin/usage/SetBudgetDialog.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
  • webapp/src/components/admin/usage/SetBudgetDialog.stories.tsx
  • webapp/src/components/admin/usage/SetBudgetDialog.tsx
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/usage/LlmUsageRecorder.java

Comment on lines +199 to +232
@Test
void reportedZeroCostWithRealTokensFallsBackToPricingInsteadOfBillingZero() {
Workspace workspace = setupWorkspace("ledger-zero-cost");
ModelPricing pricing = new ModelPricing();
pricing.setModelId("zero-reporting-model");
pricing.setPer1kInputUsd(new BigDecimal("0.001000"));
pricing.setPer1kOutputUsd(new BigDecimal("0.002000"));
pricing.setPer1kCacheReadUsd(BigDecimal.ZERO);
pricing.setPer1kCacheWriteUsd(BigDecimal.ZERO);
pricing.setValidFrom(Instant.now().minusSeconds(60));
pricingRepository.save(pricing);

recorder.record(
workspace.getId(),
new LlmUsageRecorder.LlmUsageSample(
LlmUsageJobType.PULL_REQUEST_REVIEW,
UUID.randomUUID(),
"zero-reporting-model",
1000,
1000,
0,
0,
77,
BigDecimal.ZERO, // runtime reported "free" for a job that clearly was not
Instant.now()
)
);

assertThat(budgetService.monthToDateCost(workspace.getId())).isEqualByComparingTo("0.003000");
}

/** A genuine zero — no tokens burned — is still recorded as zero, not re-derived. */
@Test
void reportedZeroCostWithNoTokensStaysZero() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Rename test methods to follow the should[ExpectedBehavior]When[Condition] convention.

The current test names do not comply with the repository's test naming convention. As per coding guidelines, tests should be named using the should[ExpectedBehavior]When[Condition] format.

♻️ Proposed rename
-    void reportedZeroCostWithRealTokensFallsBackToPricingInsteadOfBillingZero() {
+    void shouldFallbackToPricingWhenReportedCostIsZeroAndTokensAreReal() {
         Workspace workspace = setupWorkspace("ledger-zero-cost");
         ModelPricing pricing = new ModelPricing();
         pricing.setModelId("zero-reporting-model");
         pricing.setPer1kInputUsd(new BigDecimal("0.001000"));
         pricing.setPer1kOutputUsd(new BigDecimal("0.002000"));
         pricing.setPer1kCacheReadUsd(BigDecimal.ZERO);
         pricing.setPer1kCacheWriteUsd(BigDecimal.ZERO);
         pricing.setValidFrom(Instant.now().minusSeconds(60));
         pricingRepository.save(pricing);
 
         recorder.record(
             workspace.getId(),
             new LlmUsageRecorder.LlmUsageSample(
                 LlmUsageJobType.PULL_REQUEST_REVIEW,
                 UUID.randomUUID(),
                 "zero-reporting-model",
                 1000,
                 1000,
                 0,
                 0,
                 77,
                 BigDecimal.ZERO, // runtime reported "free" for a job that clearly was not
                 Instant.now()
             )
         );
 
         assertThat(budgetService.monthToDateCost(workspace.getId())).isEqualByComparingTo("0.003000");
     }
 
     /** A genuine zero — no tokens burned — is still recorded as zero, not re-derived. */
     `@Test`
-    void reportedZeroCostWithNoTokensStaysZero() {
+    void shouldStayZeroWhenReportedCostIsZeroAndNoTokensBurned() {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@Test
void reportedZeroCostWithRealTokensFallsBackToPricingInsteadOfBillingZero() {
Workspace workspace = setupWorkspace("ledger-zero-cost");
ModelPricing pricing = new ModelPricing();
pricing.setModelId("zero-reporting-model");
pricing.setPer1kInputUsd(new BigDecimal("0.001000"));
pricing.setPer1kOutputUsd(new BigDecimal("0.002000"));
pricing.setPer1kCacheReadUsd(BigDecimal.ZERO);
pricing.setPer1kCacheWriteUsd(BigDecimal.ZERO);
pricing.setValidFrom(Instant.now().minusSeconds(60));
pricingRepository.save(pricing);
recorder.record(
workspace.getId(),
new LlmUsageRecorder.LlmUsageSample(
LlmUsageJobType.PULL_REQUEST_REVIEW,
UUID.randomUUID(),
"zero-reporting-model",
1000,
1000,
0,
0,
77,
BigDecimal.ZERO, // runtime reported "free" for a job that clearly was not
Instant.now()
)
);
assertThat(budgetService.monthToDateCost(workspace.getId())).isEqualByComparingTo("0.003000");
}
/** A genuine zero — no tokens burned — is still recorded as zero, not re-derived. */
@Test
void reportedZeroCostWithNoTokensStaysZero() {
`@Test`
void shouldFallbackToPricingWhenReportedCostIsZeroAndTokensAreReal() {
Workspace workspace = setupWorkspace("ledger-zero-cost");
ModelPricing pricing = new ModelPricing();
pricing.setModelId("zero-reporting-model");
pricing.setPer1kInputUsd(new BigDecimal("0.001000"));
pricing.setPer1kOutputUsd(new BigDecimal("0.002000"));
pricing.setPer1kCacheReadUsd(BigDecimal.ZERO);
pricing.setPer1kCacheWriteUsd(BigDecimal.ZERO);
pricing.setValidFrom(Instant.now().minusSeconds(60));
pricingRepository.save(pricing);
recorder.record(
workspace.getId(),
new LlmUsageRecorder.LlmUsageSample(
LlmUsageJobType.PULL_REQUEST_REVIEW,
UUID.randomUUID(),
"zero-reporting-model",
1000,
1000,
0,
0,
77,
BigDecimal.ZERO, // runtime reported "free" for a job that clearly was not
Instant.now()
)
);
assertThat(budgetService.monthToDateCost(workspace.getId())).isEqualByComparingTo("0.003000");
}
/** A genuine zero — no tokens burned — is still recorded as zero, not re-derived. */
`@Test`
void shouldStayZeroWhenReportedCostIsZeroAndNoTokensBurned() {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@server/src/test/java/de/tum/cit/aet/hephaestus/agent/usage/LlmUsageLedgerIntegrationTest.java`
around lines 199 - 232, Rename the affected test methods in
LlmUsageLedgerIntegrationTest to follow the
should[ExpectedBehavior]When[Condition] convention, including
reportedZeroCostWithRealTokensFallsBackToPricingInsteadOfBillingZero and
reportedZeroCostWithNoTokensStaysZero. Preserve each test’s existing behavior
and assertions.

Source: Coding guidelines

FelixTJDietrich and others added 14 commits July 20, 2026 20:31
Foundation for the LLM configuration redesign (#1368, scope-expanded). Introduces two
credential-owning scopes for talking to a model, both first-class:

- INSTANCE catalog (app_admin-owned, GLOBAL): llm_connection -> llm_model -> temporal
  llm_model_price, with llm_model_workspace_grant for per-workspace sharing.
- WORKSPACE catalog (tenant-scoped, workspace-admin-owned): workspace_llm_connection ->
  workspace_llm_model (inline price). Isolation enforced by same-workspace composite FKs.

agent_config is demoted to a binding (instance_model_id XOR workspace_model_id). The usage
ledger gains pricing_state (PRICED/FREE/UNPRICED — "unknown" stops silently counting as $0),
reasoning_tokens, funding_source, and applied-price provenance.

LlmModelResolver collapses instance / workspace / legacy configs into one non-secret
ResolvedLlmModel; the credential is resolved live and never frozen or logged. There is one
credential path only — no egress toggle; the key never enters the sandbox.

Migration validated on a fresh Postgres (836 changesets); tenancy + data-isolation arch
tests green. Additive and auto-applied; no operator action.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Instance-admin (app_admin) CRUD for provider connections under /admin/llm/connections:
create/list/get/update/delete + a "test & fetch models" probe. The write-only API key is
never serialized (hasApiKey + last4 only); delete is refused with 409 while a model still
references the connection.

EgressPolicy is the shared SSRF guard reused by the probe (and later the proxy): every
base URL must be a public HTTPS host, private/link-local/metadata addresses are blocked,
and when the instance egress allowlist is set the host must be on it. The probe validates
egress first, uses a dedicated 5s client, and is advisory — a 4xx/timeout returns
reachable=false, never an error, and never the raw upstream body.

Also adds GET/PUT /admin/llm/settings (egress allowlist, workspace-BYO toggle, default
unpriced policy). Corrects the foundation changelog: the catalog subjects were wrongly
added to the workspace-scoped config_audit CHECK — config-audit needs a workspace id, so
instance-global catalog changes audit to auth_event instead (wiring in a later slice).

Compile + audit-by-default arch test green; migration re-validated (835 changesets).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Instance-admin (app_admin) CRUD for catalog models under /admin/llm: create on a
connection (per-connection slug, 409 on conflict incl. the concurrent-create race),
list/get with batched current-price + grant projections (no N+1), metadata update,
and delete guarded by a 409 while any agent_config still binds the model.

Pricing is temporal supersede-on-insert: repricing closes the open llm_model_price
row and inserts the new open row in one transaction (ux_llm_model_price_open holds
the invariant). PRICED requires input+output per-1M rates; FREE/UNPRICED reject
rates, and FREE requires a funding note. Sharing is one "Share with" operation:
all workspaces (clears grants) or a selected set (grant diff, unknown ids 400).

14 new unit tests (supersede ordering, pricing validation, sharing diff, in-use
guard); architecture tier green (216/216).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…dels

Workspace-admin surface for the two-scope LLM catalog (#1368):

- BYO connection CRUD + probe under the workspace scope, gated by the instance
  allow_workspace_connections setting (403 when off). Key is write-only
  (hasApiKey + last4), base_url passes the shared EgressPolicy SSRF guard, and
  the probe answers workspace-framed — reachable + model count, never a raw
  model dump. Delete refuses with 409 while a model references the connection.
- BYO model CRUD with inline pricing; PRICED/FREE/UNPRICED validation extracted
  into LlmPriceValidation and shared with the instance catalog service.
- agent_config binding: set exactly one of instanceModelId / workspaceModelId
  (both = 400); bind-time validation requires the instance model to be visible
  (public or granted), enabled, on an enabled connection, and the workspace
  model to belong to the calling workspace. Legacy (null,null) rows tolerated.
- Available-models projection: union of visible shared models and own BYO
  models behind one deliberately narrow DTO — display names + price framing
  only, never upstream ids, base URLs, or auth material.

106 unit tests green; architecture tier green (tenancy, audit-by-default,
snapshot secret-detector, dependency ceilings).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The runtime now speaks to models exclusively through the two-scope catalog
(#1368): one credential path, key never in the sandbox.

- ConfigSnapshot v4 freezes resolved behaviour at dispatch (api protocol,
  base URL, upstream model id, capability, connection ref); the credential is
  resolved live per proxy request, so rotation and revocation reach in-flight
  jobs. Pre-v4 payloads are translated explicitly, not Jackson-defaulted.
- LlmProxyController drops the /internal/llm/{provider} path segment — the
  connection comes from the authenticated job token (mentor turns use a
  process-local credential registry), so a sandbox can no longer pick which
  instance credential the proxy injects. EgressPolicy re-checked per request;
  Azure param quirks and stream_options.include_usage keyed on api protocol.
- Deleted the last "key in sandbox" paths: CredentialMode, ProviderProxyConfig,
  LlmProxyAuthShell, LlmProxyProperties, WorkerProperties.Llm and the
  AgentJobExecutor override branch. Proxy beans gate on job-execution
  capability — "jobs on, proxy off" is unexpressible.
- One shared pi-provider.mjs registration for both runners, driven by a
  per-job pi-provider.json (kills the cacheControlFormat / context-window /
  cost:{0,0,0,0} drift). PiResultParser now records reasoning tokens.

Unit tier 5233/5233, architecture tier 216/216 green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The usage ledger is now the single cost authority (#1368):

- LlmUsageRecorder derives every cost server-side from the bound catalog
  price (instance open price row, BYO inline rates, or legacy model_pricing
  fallback); the runner-reported-cost branch and COST_USD_SANITY_CAP are gone.
  Every priced event freezes the applied per-1M rates (and price row / BYO
  model refs) onto the event, so a dollar stays falsifiable after repricing.
  cost_usd is guarded for NUMERIC(12,6): HALF_EVEN to 6dp, non-zero never
  truncates to zero, >=1e6 clamps with a WARN.
- pricing_state per event: PRICED, FREE (declared, costs 0), or UNPRICED
  (cost NULL — unknown no longer masquerades as $0).
- Budget verdict {WITHIN, EXHAUSTED, UNVERIFIABLE}: the cap compares only
  priced, instance-funded spend; BYO spend is reported separately and never
  capped. Unpriced instance-funded usage in the window makes the month
  UNVERIFIABLE — surfaced and logged, never blocking (v1 warns, not fails).
  Job-type and daily breakdowns carry the same split.
- Dead Pi-reported cost path deleted (the runner structurally cannot price
  anymore); the mentor turn estimate keeps its ModelPricingService fallback,
  and agent_job.llm_cost_usd is no longer written with a fake $0.

Migration changeset validated on a fresh Postgres (836 changesets clean).
Unit tier 5249/5249, architecture tier 216/216 green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Every LLM catalog mutation is now recorded in the ledger that matches its
scope (#1368):

- Instance-global actions (connections, models, pricing, sharing, settings)
  write auth_event entries — nine new event types — through narrow SPI ports
  implemented by one server-role adapter, keeping the Modulith boundary to
  core.auth intact. Detail payloads carry ids and non-secret fields only.
- Workspace BYO mutations record config_audit entries with two new entity
  types; connection snapshots redact the API key to a set/unset boolean and
  strip userinfo/query from base URLs, passing the snapshot secret detector.
- Probe endpoints are @AuditExempt (they test a credential, store nothing);
  instance controllers now carry "auth_event LLM_*" markers, satisfying the
  audit-by-default rule with real wiring instead of placeholders.
- Both CHECK constraints (auth_event type, config_audit entity type) widened
  in the branch changelog; validated on a fresh Postgres (838 changesets).

Unit tier 5259/5259, architecture tier 216/216 green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Regenerates server/openapi.yaml and the webapp TS client for the new catalog,
BYO, available-models, and reshaped usage endpoints. Fourteen catalog
request/result records were renamed to the mandatory *DTO suffix — the spec
post-processor drops non-DTO schemas while paths still reference them, which
broke client generation with unresolved refs.

Webapp mechanically repaired to the new contract: usage pages read
pricedTotalCostUsd / byoTotalCostUsd / unpricedEventCount / verdict, render
the "at least $X — some usage has no price set" framing and an Unverified
badge; audit-log label maps gained the nine new instance event types and the
two workspace config-audit entity types.

Typecheck, 418 webapp tests, build, and format all green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…icker

Instance admins get an "AI models" console section (/admin/llm): provider
connections with a write-only key field and an advisory "Test & fetch models"
probe that never gates saving, per-connection models with capability fields,
a price editor (per-1M rates / Free with a required note / No price set), a
"Share with" all-or-selected control, and instance settings (allowed provider
hosts, workspace own-provider toggle).

Workspace admins pick models instead of typing provider credentials: the
agent-config form's hardcoded provider list is gone, replaced by a picker
grouped into "Shared models" and "Your provider" with price framing; legacy
unbound configs show a read-only hint. A new "Your AI provider" tab lets a
workspace connect its own provider ("Connected — N models available", never
a raw model dump) and respects the instance-level disable. Usage screens
render own-provider spend outside the budget bar and use the "Budget
reached" / "at least $X" wording.

Typecheck, 419 webapp tests, 913 Storybook interaction tests, build, and
format all green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
51 controller integration tests (instance catalog CRUD incl. key redaction
and price supersede, workspace BYO tenancy and kill-switch, available-models
leak assertions, binding validation) uncovered and fix two real defects:
DTO conversion after the transaction closed threw LazyInitializationException
(500) on model reads — repositories now JOIN FETCH the connection — and the
four 409 conflict guards were shadowed to 500 by the global catch-all advice,
fixed with a dedicated highest-precedence catalog advice.

Creating an agent config with a model binding no longer demands a vestigial
legacy provider value: either a binding or the legacy provider is required,
and the webapp stops sending a placeholder. Interactive mentor sandboxes now
revoke their proxy credential on every close path instead of letting it live
out a 12h TTL. The e2e setup script drops the removed credentialMode field.

Unit tier 5265/5265, architecture 216/216, targeted integration 51/51 green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…talog

Removes dead LLM env wiring (LLM_PROXY_*, HEPHAESTUS_WORKER_LLM_*) from the
compose files and env examples, points runtime-role and setup docs at the
admin console's AI models section, amends ADRs 0005/0006 per their own
amendment precedents, adds the MIGRATION.md entry for the removed variables,
regenerates the ERD (seven new llm tables + the agent_config binding), and
ships the release changeset for the catalog + honest-budget feature.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adversarial review of the catalog branch found five exploitable gaps, all
closed here:

- EgressPolicy rejects provider URLs carrying userinfo, query strings, or
  fragments (a ?api-key=... gateway URL would otherwise ride into snapshots
  and logs), and the loopback exception is now opt-in via
  hephaestus.llm.egress.allow-loopback (default false) instead of an
  unconditional production SSRF hole. The guard finally has behavioral
  tests — 34 table-driven cases.
- The connection probe no longer follows redirects, so a 302 from a probed
  host can't walk x-api-key/api-key headers to another origin.
- The proxy resolves base URL and credential together from the live
  connection row (the frozen snapshot pins only the connection identity), so
  repointing a connection can no longer send the new key to the old host.
- A keyless connection on an enabled, egress-valid host now forwards without
  auth injection instead of failing with 502 — self-hosted vLLM/Ollama
  gateways work as advertised.
- Workspace-visible job DTOs redact an instance connection's base URL to
  scheme://host, AgentJob.toString() excludes the snapshot, and proxy logs
  carry hosts, not full URLs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Review found the legacy backfill actively harmful: it defaulted every blank
base URL to api.openai.com (sending legacy Anthropic keys to OpenAI), and
rebinding legacy configs as workspace-funded exempted every pre-existing
workspace from the budget cap this release introduces. The backfill binding
is removed entirely — legacy configs stay unbound and the runtime fallback
carries them with per-provider defaults and instance funding, which is what
MIGRATION.md now accurately promises.

Also: the mentor-history cost/token JSON casts are guarded so one malformed
metadata row can no longer abort the whole migration; CHECK-widening
changesets get empty rollbacks (re-narrowing fails once new-type rows
exist); (connection_id, upstream_model_id) is now unique per scope with a
friendly 409, closing the duplicate-upstream-id billing ambiguity; and
cost_usd widens to NUMERIC(18,6) so the recorder no longer silently clamps
a huge event downward.

Validated on a fresh Postgres: 840 changesets clean, rollback/reapply of the
tail changesets clean, malformed-JSON battery produces NULLs, duplicate
insert rejected by the new unique index.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The unverifiable month renders as a warning line ("Some usage has no price
set") instead of a badge, agent-config cards use the locked "Model for
Practice detection" / "Model for Mentor" task framing, and a workspace whose
budget is exhausted now shows a "Budget reached" alert on the AI models page
itself — detection no longer pauses invisibly for admins who never open the
usage screen. Operator docs gain a workspace-budget section (set/clear
semantics, what pauses at the cap, the "at least $X" framing, and the
llm.budget.* metrics as alerting hooks, incl. the mentor reasoning-token
limitation); a stale runner comment naming a nonexistent test now describes
the real by-construction guarantee.

419 webapp tests and 914 Storybook interaction tests green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This branch had accumulated ~12,000 lines of added comment. Much of it restated
the code, counted sibling files, or narrated a workaround instead of removing it.

Server main sources lose 41% of their added commentary, the webapp 28%. Where a
comment was carrying something real, the fix was usually to put it in the code
instead: a named predicate, an extracted method, a type that makes the state it
described unrepresentable. Where a comment explained a workaround, the workaround
went with it.

What survives is the part a reader cannot derive: an ordering requirement, a
transaction boundary, a money-exactness rule, a security reason, or a real
external constraint that would otherwise be "simplified" into a bug.

No behaviour change. 5683 unit, 223 architecture, 682 webapp and 1043 Storybook
tests pass unchanged.
Cutting the commentary in half meant reading every claim it made. Several were
false, and three of those were hiding real defects.

- **A workspace could lose control of providers it owns.** When an instance
  turned own-providers off, a workspace that had already connected one lost the
  whole section — no Edit, no Test, no Disconnect — while the banner beside it
  promised those keep working. Only the mutations were ever gated server-side,
  so the section now always mounts.
- **A rate-limit gauge read zero forever.** No GraphQL document selects the
  field it was bound to, so it decoded to zero and overwrote the correct value
  seeded from REST.
- **The retired-env-var warning could not fire.** It watched dotted property
  names for variables that never had the prefix Spring needs, so three of the
  four were unreachable — and its test passed because it set the dotted form
  directly.
- The GDPR record of processing claimed no component writes an HTTP access log.
  nginx does, and one route carries a contributor's login in the URL. It also
  understated retention coverage and mis-stated when sandboxes run.

Test commentary drops 49% and webapp 50%. Fifty-four tests were renamed to carry
what their javadoc used to, and where a comment described a workaround, the
workaround went with it.

New: stubbed GraphQL responses are now validated against the checked-in schema,
so a test can no longer assert against a reply the vendor could never send —
the defect class behind the merge-request approval that had never once worked.
The documentation pressure test found ten places where a page existed to warn
about something the code got wrong. Each is fixed at the source, and the
paragraph it needed goes with it.

- **No layer writes an HTTP access log any more.** nginx did — one line per
  request, and one route carries a contributor's login in the URL — while the
  record of processing said nothing did. Both nginx containers now disable it at
  the server level, so the strong claim is restored rather than qualified.
- **A held run no longer looks queued.** The queue exposes why a job is parked
  and when it is next due, a new agent.queue.held metric separates "paused" from
  "idle", and the runs table and detail panel show both. The runbook step that
  told operators to go and check another page is gone.
- **The retired-setting warning can actually fire.** It watched dotted property
  names for variables that never had the prefix Spring needs, and its test
  passed because it set those names by hand. It now watches the real variable
  names, compose forwards them for one release so a stale value is visible, and
  the test resolves them the way a container would.
- **Feature flags work outside the shipped compose files**, the GitLab server
  follows your GitLab login instead of a pinned default, and the encryption-key
  error says how many bytes it got and from how many characters.

Documentation: one page owns the install and one owns integrations; a new page
documents connecting an AI provider from the controllers, with a worked pricing
example. ADR 0025 loses the fix-log it had accumulated. Every orphaned page is
in a sidebar, and the practice catalogue's counts now match the catalogue.

The GraphQL commit-enrichment query moved into a checked-in document, closing
the last place a query escaped both schema guards.
AgentJobRepository agentJobRepository,
MentorProxyCredentialRegistry mentorRegistry,
ObjectMapper objectMapper
) throws Exception {
Static analysis flags the csrf.disable() on the proxy filter chain and cannot
see what makes it safe: the chain is stateless and its filter reads a credential
only from the Authorization header, so a forged cross-site request has nothing
ambient to ride on. That was an argument in a comment; it is now three tests.

The negative cases send a token the third case proves is good, so "authenticates
nobody" cannot pass for the trivial reason that the token was bad, and they
assert the repository is never consulted at all.

The alert itself predates this branch and is open on main for this same file and
three others.
The test deployment's nginx config disabled the access log only for /assets/,
so every page request was logged — including /w/<slug>/user/<username>, which
names a person, on an internet-exposed route. Same defect the shipped webapp
config had; same fix, at server level where it covers everything.
@FelixTJDietrich
FelixTJDietrich merged commit 7dc852a into main Jul 28, 2026
45 checks passed
@FelixTJDietrich
FelixTJDietrich deleted the 1368-workspace-production-readiness branch July 28, 2026 02:12
FelixTJDietrich added a commit that referenced this pull request Jul 28, 2026
…nd AI-config work

main has since rewritten SECURITY.md and MIGRATION.md (#1394, #1392, #1400), so
this branch's versions of both are dropped in favour of theirs — they say the
same things, better. What remains is the public-surface repositioning.

Also carries the changeset the release gate now requires, points the README's
workspace bullet at the shipped AI-model configuration, and replaces the invented
features in the FeatureCard stories with the shipped ones.
FelixTJDietrich added a commit that referenced this pull request Jul 28, 2026
Integrates the console shell with what landed on main since this branch opened.

Silent mode now satisfies main's evaluation contract from #1402: a withheld unit
must record WHY, or an evaluator cannot tell "model missed" from "policy
withheld". Adds FeedbackSuppressionReason.INSTANCE_SILENCED and records it from
both delivery gates, so suppressed feedback is auditable instead of invisible.

The toggle is now audited. #1401's audit-by-default rule requires every admin
mutation endpoint to declare its audit status; PUT /admin/settings/silent-mode
records SILENT_MODE_CHANGED on the auth_event trail (instance-level, so not
config_audit_event, whose workspace_id is NOT NULL). An incident review can now
see who silenced the instance and why.

Console nav absorbs #1400's new surfaces: AI models and AI usage join the
grouped sidebar (Access / AI / Operations) instead of main's flat list, reusing
the shared ADMIN_NAV_LABELS so both consoles name things the same way.

The audit module moved on main (auditFormat.ts -> audit-format.ts plus
audit-shared/), so the overview's activity card now uses refLabel and the shared
self-ticking RelativeTime component rather than local helpers.

The changelog is renamed to keep timestamps monotonic and its include appended
last, per the changelog-immutability gate added in #1390. It also widens
chk_feedback_suppression_reason for the new value. Re-verified on a fresh
Postgres: 864 changesets apply, the seed row reads back, and there is no drift.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

application-server Spring Boot server: APIs, business logic, database ci GitHub Actions, workflows, build pipeline changes documentation Improvements or additions to documentation feature New feature or enhancement infrastructure Docker, containers, and deployment infrastructure security Authentication, authorization, vulnerability fixes webapp React app: UI components, routes, state management

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(server,webapp): per-workspace LLM cost rollup and monthly budget cap

2 participants