Skip to content

feat(server): expose practice-group standings and feedback responses - #1486

Merged
FelixTJDietrich merged 18 commits into
mainfrom
feat/practice-area-server
Aug 27, 2026
Merged

feat(server): expose practice-group standings and feedback responses#1486
FelixTJDietrich merged 18 commits into
mainfrom
feat/practice-area-server

Conversation

@ValentinGruener

@ValentinGruener ValentinGruener commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

Description

Adds the authenticated developer API for understanding how reviewed work reflects on each practice and practice group: current standings and guidance, recent direction, the observations behind those conclusions, and the review runs that produced them. Developers can also record, replace, or withdraw their current response to delivered practice feedback.

Direction compares opportunities to apply a practice rather than elapsed time. A practice can therefore recover after two problem-free reviews instead of remaining mixed because of older observations in the 90-day evidence window.

This PR intentionally ships the server contract before the new profile UI. #1487 will be rebased onto main and updated to consume this contract as an independent follow-up.

REST resources

GET    /workspaces/{workspaceSlug}/practices/standings
GET    /workspaces/{workspaceSlug}/practices/reviewed
GET    /workspaces/{workspaceSlug}/practice-groups/standings
GET    /workspaces/{workspaceSlug}/practice-groups/{groupSlug}/trend
GET    /workspaces/{workspaceSlug}/practice-groups/{groupSlug}/review-runs
GET    /workspaces/{workspaceSlug}/practices/feedback/resolution-counts
GET    /workspaces/{workspaceSlug}/practices/feedback/{feedbackId}/response
PUT    /workspaces/{workspaceSlug}/practices/feedback/{feedbackId}/response
DELETE /workspaces/{workspaceSlug}/practices/feedback/{feedbackId}/response

All developer-facing reads require authentication and return data only for the signed-in developer. Review runs retain undecided observations without turning them into positive or negative verdicts, and invalid pagination is rejected with 400 rather than silently normalized.

Feedback responses

A response can contain usefulness (HELPFUL or UNHELPFUL), resolution (ADDRESSED, DISPUTED, or NOT_APPLICABLE), or both. A dispute requires an explanation. PUT replaces the current response, GET reads it, and idempotent DELETE withdraws it. Storage remains append-only for auditability while the HTTP API exposes one current resource.

Compatibility and migration

This release replaces practice-area and learner-projection terminology with practice-group and reviewed-practice contracts, and replaces the older reaction endpoints with the feedback-response resource. Direct API consumers must migrate as documented in MIGRATION.md; the bundled generated client is updated in the same release.

Liquibase applies the schema changes automatically. Existing feedback-response history and append-only configuration-audit history are preserved, and historical audit values are translated when read rather than rewritten. No manual data migration or new configuration is required.

server/openapi.yaml, webapp/src/api/**, and the ERD ship with their source changes because repository gates reject generated drift.

How to test

Run the complete local repository gates:

pnpm run format
pnpm run check

Manual API smoke test as an authenticated workspace member:

  1. Read practice standings, group standings, one group trend, and one page of group review runs.
  2. Confirm the responses contain data only for the signed-in developer.
  3. Replace a feedback response with usefulness and resolution, then replace it with usefulness only; verify GET no longer returns a resolution.
  4. Delete the response twice; verify both requests succeed and GET reports no current response.
  5. Request a negative or oversized review-run page; verify the API returns 400.

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

@coderabbitai

coderabbitai Bot commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Too many files!

This PR contains 404 files, which is 304 over the limit of 100.

To get a review, reduce the PR to 100 files or fewer by splitting it into smaller PRs or changing its base branch.

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: f8807785-4bc3-49a9-8364-68aface5294e

📥 Commits

Reviewing files that changed from the base of the PR and between b511341 and d329c81.

📒 Files selected for processing (404)
  • .changeset/a-practice-says-why-it-has-nothing-to-say.md
  • .changeset/an-answer-keeps-the-words-that-came-with-it.md
  • .changeset/areas-get-an-icon-and-criteria-render.md
  • .changeset/calm-practices-adopt.md
  • .changeset/feedback-says-which-kind-of-good-or-bad-it-is.md
  • .changeset/feedback-survives-an-interrupted-review.md
  • .changeset/one-recovery-for-a-stale-plan.md
  • .changeset/practice-standing-feedback-api.md
  • .changeset/practice-standing-recent-evidence.md
  • .changeset/practice-trend-evidence.md
  • .changeset/removing-an-area-asks-what-to-keep.md
  • .gitignore
  • MIGRATION.md
  • docs/admin/dsms/artifact-source-governance.md
  • docs/admin/dsms/dpia-prescreen.md
  • docs/admin/dsms/record-of-processing.md
  • docs/admin/practice-catalog.mdx
  • docs/admin/practice-review.mdx
  • docs/contributor/erd/schema.mmd
  • docs/contributor/evaluation-provenance.md
  • docs/contributor/practice-catalogue.md
  • docs/contributor/practice-feedback-language.md
  • docs/contributor/practice-feedback-schema.md
  • docs/contributor/practice-review-glossary.mdx
  • docs/contributor/practice-review-pipeline.mdx
  • docs/decisions/0022-observation-presence-assessment-and-schema-cleanup.md
  • scripts/validate-artifact-source-contracts.ts
  • server/AGENTS.md
  • server/openapi.yaml
  • server/pom.xml
  • server/src/main/java/de/tum/cit/aet/hephaestus/account/AccountCurrentDeveloperAdapter.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/achievement/AchievementCategory.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/context/providers/ReviewHistoryContentSource.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/context/providers/mentor/ObservationHistoryContentSource.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/context/providers/mentor/PracticeCatalogContentSource.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/handler/DeliveryComposer.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/handler/FeedbackResponseSuppressionFilter.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/handler/JobTypeHandlerConfiguration.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/handler/PracticeCatalogInjector.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/composition/FeedbackCompositionResultParser.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/handler/conversation/FeedbackChannelRouter.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/handler/inapp/InAppCompositionListener.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/handler/package-info.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/agent/job/AgentJobService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/core/audit/ConfigAuditEntityTypeConverter.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/spi/ConfigAuditEntityType.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/core/audit/spi/ConfigAuditFilter.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/core/tenancy/WorkspaceScopedTables.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/CatalogOrigin.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/CatalogOriginPresenter.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/GroupAttributes.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/GroupDefinition.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/GroupDefinitionDigest.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/PracticeAreaSnapshot.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/PracticeCatalogController.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/PracticeCatalogInstallationManager.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/PracticeDefinition.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/PracticeDefinitionDigest.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/PracticeDefinitionSnapshot.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/PracticeGroupController.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/PracticeGroupRepository.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/PracticeGroupService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/PracticeGroupSlugConflictException.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/PracticeGroupSnapshot.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/PracticeRepository.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/PracticeService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/PracticesControllerAdvice.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/ReviewRuleFingerprint.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/adapter/PracticesWorkspacePurgeAdapter.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/curated/BundledPracticeCatalog.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/curated/BundledPracticeCatalogLoader.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/curated/CatalogEntry.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/curated/CatalogProvenanceBackfill.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/curated/CuratedCatalogAdminController.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/curated/CuratedCatalogModel.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/curated/CuratedCatalogService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/curated/CuratedDefinitionDigest.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/curated/CuratedGroupOverride.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/curated/CuratedGroupOverrideRepository.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/curated/CuratedGroupSnapshot.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/curated/CuratedPracticeOverride.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/curated/CuratedPracticeSnapshot.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/curated/EffectiveCatalog.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/curated/adoption/CatalogAdoptionAreaDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/curated/adoption/CatalogAdoptionController.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/curated/adoption/CatalogAdoptionGroupDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/curated/adoption/CatalogAdoptionPlan.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/curated/adoption/CatalogAdoptionPlanAssembler.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/curated/adoption/CatalogAdoptionService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/curated/adoption/CatalogAreaAdoptionPreviewDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/curated/adoption/CatalogAreaPracticeActionDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/curated/adoption/CatalogGroupAdoptionPlan.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/curated/adoption/CatalogGroupAdoptionPreviewDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/curated/adoption/CatalogGroupAdoptionResultDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/curated/adoption/CatalogGroupDisposition.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/curated/adoption/CatalogGroupPracticeAction.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/curated/adoption/CatalogGroupPracticeActionDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/curated/adoption/CatalogPracticePreviewDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/curated/adoption/CatalogPracticeSummaryDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/curated/dto/CreateCuratedGroupRequestDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/curated/dto/CuratedCatalogDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/curated/dto/CuratedGroupDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/curated/dto/CuratedGroupRequestDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/curated/dto/CuratedPracticeDefinitionDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/curated/dto/CuratedPracticeRequestDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/curated/dto/CuratedPracticeSummaryDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/dto/AutonomyRollupDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/dto/BindPracticeGroupRequestDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/dto/CreatePracticeGroupRequestDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/dto/CreatePracticeRequestDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/dto/FeedbackSourceCountDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/dto/GroupAutonomyRollupDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/dto/LearnerPracticeDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/dto/PlacePracticeRequestDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/dto/PracticeAreaDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/dto/PracticeDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/dto/PracticeGroupDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/dto/PracticeGroupStandingDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/dto/ReorderPracticeGroupsRequestDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/dto/ReorderPracticesRequestDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/dto/ReviewedPracticeDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/dto/UpdatePracticeAutonomyRequestDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/dto/UpdatePracticeGroupRequestDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/dto/UpdatePracticeRequestDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/feedback/DeveloperTextSanitizer.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/feedback/FeedbackObservationRepository.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/feedback/FeedbackRepository.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/feedback/FeedbackResolution.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/feedback/FeedbackResponseController.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/feedback/FeedbackResponseService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/feedback/FeedbackUsefulness.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/feedback/dto/FeedbackResolutionCountsDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/feedback/dto/FeedbackResponseDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/feedback/dto/FeedbackResponseRequestDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/feedback/dto/package-info.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/feedback/inapp/InAppFeedbackController.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/feedback/inapp/InAppFeedbackService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/feedback/inapp/dto/InAppFeedbackDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/groupdetail/PracticeGroupDetailController.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/groupdetail/PracticeGroupReviewRunFilterParams.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/groupdetail/PracticeGroupReviewRunService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/groupdetail/PracticeGroupTrendQueryService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/groupdetail/dto/PracticeGroupReviewObservationDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/groupdetail/dto/PracticeGroupReviewRunDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/groupdetail/dto/PracticeGroupReviewRunsPageDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/groupdetail/dto/PracticeGroupReviewedWorkDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/groupdetail/dto/package-info.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/groupdetail/package-info.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/model/ObservationOutcome.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/model/Practice.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/model/PracticeGroup.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/model/PracticeRevision.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/observation/DeterministicGroupGuidanceComposer.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/observation/ObservationController.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/observation/ObservationFeedFilterParams.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/observation/ObservationFeedQuery.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/observation/ObservationQueryFilter.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/observation/ObservationRepository.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/observation/ObservationService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/observation/ObservationTrendService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/observation/ObservationVisibilityPolicy.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/observation/PracticeGroupStandingController.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/observation/PracticeGroupStandingService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/observation/PracticeStandingController.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/observation/PracticeStandingService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/observation/StandingScale.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/observation/TrendDelta.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/observation/dto/ObservationDetailDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/observation/dto/PracticeStandingDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/observation/dto/PracticeStandingObservationDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/observation/dto/ReflectionPracticeDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/observation/reaction/Reaction.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/observation/reaction/ReactionAction.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/observation/reaction/ReactionController.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/observation/reaction/ReactionRepository.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/observation/reaction/ReactionService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/observation/reaction/dto/CreateReactionDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/observation/reaction/dto/ReactionDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/observation/reaction/dto/ReactionEngagementDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/observation/reaction/dto/package-info.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/observation/reaction/package-info.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/observation/trend/BetaPosterior.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/observation/trend/EvidenceOpportunity.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/observation/trend/GroupTrendAggregator.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/observation/trend/OpportunityBundler.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/observation/trend/OutcomeVector.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/observation/trend/PracticeTrend.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/observation/trend/PracticeTrendCalculator.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/observation/trend/PracticeTrendService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/observation/trend/TrendBundle.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/observation/trend/TrendDirection.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/observation/trend/TrendDirectionRule.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/observation/trend/TrendProperties.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/observation/trend/TrendScope.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/observation/trend/TrendSupport.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/observation/trend/TrendSupportFactory.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/observation/trend/dto/OutcomeVectorDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/observation/trend/dto/PracticeGroupTrendDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/observation/trend/dto/PracticeTrendDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/observation/trend/dto/TrendOpportunityDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/observation/trend/dto/TrendSupportDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/observation/trend/dto/package-info.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/observation/trend/package-info.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/package-info.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/PracticeReviewProperties.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/UpdatePracticeReviewSettingsRequestDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/review/autonomy/AutonomyResolver.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/review/autonomy/AutonomyRollupService.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/review/autonomy/AutonomySource.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/reviewoutput/ReviewFeedbackFilterParams.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/reviewoutput/ReviewObservationFilterParams.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/reviewoutput/dto/ReviewBoundObservationDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/reviewoutput/dto/ReviewObservationDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/reviewoutput/dto/ReviewObservationDetailDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/reviewoutput/dto/ReviewPracticeGroupDTO.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/spi/CurrentDeveloperLookup.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/web/QueryFilterSupport.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/practices/web/package-info.java
  • server/src/main/java/de/tum/cit/aet/hephaestus/workspace/settings/PracticeReviewField.java
  • server/src/main/resources/agent/mentor/system.md
  • server/src/main/resources/agent/pi-orchestrator.md
  • server/src/main/resources/agent/pi-runner.ts
  • server/src/main/resources/application.yml
  • server/src/main/resources/db/changelog/1787822548164_changelog.xml
  • server/src/main/resources/db/changelog/1787844107869_changelog.xml
  • server/src/main/resources/db/master.xml
  • server/src/main/resources/practices/default-catalog.json
  • server/src/main/resources/practices/default-catalog.schema.json
  • server/src/main/resources/practices/precompute/leaves-the-code-clean-with-intent-revealing-comments.ts
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/context/providers/ReviewHistoryContentSourceTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/context/providers/mentor/ObservationHistoryContentSourceTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/handler/FeedbackResponseSuppressionFilterTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/handler/JobTypeHandlerRegistryTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/handler/PracticeCatalogInjectorTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/handler/PullRequestReviewHandlerTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/job/AgentJobServiceTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/agent/usage/fx/FxRateFetchSchedulerTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/architecture/ActivityModuleBoundaryTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/architecture/CodeQualityTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/architecture/DataIsolationArchitectureTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/architecture/PracticeTrendBoundaryArchitectureTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/architecture/PracticesIntegrationBoundaryTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/architecture/SpringTestContextArchitectureTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/core/audit/ConfigAuditEntityTypeConverterTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/integration/schema/ProductionSchemaContractIntegrationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/integration/scm/GraphQlOperationDocumentValidationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/practices/CatalogCriteriaSlugReferenceTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/practices/CatalogOriginPresentationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/practices/CatalogOriginTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/practices/PracticeAutomatedReviewValidationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/practices/PracticeCatalogControllerIntegrationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/practices/PracticeCatalogInstallationManagerTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/practices/PracticeGroupControllerIntegrationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/practices/PracticeGroupReviewRunIntegrationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/practices/PracticeGroupServiceTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/practices/PracticeGroupStandingIntegrationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/practices/PracticeRepositoryIntegrationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/practices/adapter/PracticesWorkspacePurgeAdapterTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/practices/curated/BundledPracticeCatalogLoaderTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/practices/curated/CatalogEntryTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/practices/curated/CatalogInstallationConcurrencyIntegrationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/practices/curated/CatalogProvenanceBackfillIntegrationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/practices/curated/CuratedCatalogAdminControllerIntegrationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/practices/curated/CuratedCatalogFixtures.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/practices/curated/CuratedCatalogModelTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/practices/curated/CuratedCatalogOrderingTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/practices/curated/EffectiveCatalogTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/practices/curated/adoption/CatalogAdoptionControllerIntegrationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/practices/feedback/FeedbackResponseControllerIntegrationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/practices/feedback/FeedbackResponseServiceTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/practices/groupdetail/PracticeGroupTrendControllerIntegrationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/practices/model/ObservationOutcomeTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/practices/model/PracticeRevisionTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/practices/observation/DeterministicGroupGuidanceComposerTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/practices/observation/ObservationControllerIntegrationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/practices/observation/ObservationRepositoryIntegrationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/practices/observation/PracticeStandingServiceTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/practices/observation/PracticeStandingVisibilityTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/practices/observation/dto/PracticeStandingObservationDTOTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/practices/observation/reaction/ReactionServiceTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/practices/observation/trend/BetaPosteriorTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/practices/observation/trend/GroupTrendAggregatorTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/practices/observation/trend/OpportunityBundlerTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/practices/observation/trend/OutcomeVectorTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/practices/observation/trend/PracticeTrendCalculatorTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/practices/review/autonomy/AutonomyResolverTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/practices/review/autonomy/AutonomyRollupIntegrationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/practices/reviewoutput/PracticeReviewOutputControllerIntegrationTest.java
  • server/src/test/java/de/tum/cit/aet/hephaestus/workspace/CrossTenantIsolationIntegrationTest.java
  • webapp/e2e/group-visuals.spec.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/admin/ai/story-mock-data.ts
  • webapp/src/components/admin/config-audit/config-audit-format.ts
  • webapp/src/components/admin/curated-catalog/CuratedCatalog.stories.tsx
  • webapp/src/components/admin/curated-catalog/CuratedCatalog.tsx
  • webapp/src/components/admin/curated-catalog/CuratedCatalogTree.tsx
  • webapp/src/components/admin/curated-catalog/CuratedEntryBadges.stories.tsx
  • webapp/src/components/admin/curated-catalog/CuratedEntryBadges.tsx
  • webapp/src/components/admin/curated-catalog/CuratedFormLevel.tsx
  • webapp/src/components/admin/curated-catalog/CuratedGroupForm.stories.tsx
  • webapp/src/components/admin/curated-catalog/CuratedGroupForm.tsx
  • webapp/src/components/admin/curated-catalog/CuratedPracticeForm.stories.tsx
  • webapp/src/components/admin/curated-catalog/CuratedPracticeForm.test.tsx
  • webapp/src/components/admin/curated-catalog/CuratedPracticeForm.tsx
  • webapp/src/components/admin/curated-catalog/HephaestusVersionPanel.stories.tsx
  • webapp/src/components/admin/curated-catalog/HephaestusVersionPanel.tsx
  • webapp/src/components/admin/curated-catalog/curated-catalog-cache.test.ts
  • webapp/src/components/admin/curated-catalog/curated-catalog-cache.ts
  • webapp/src/components/admin/curated-catalog/curated-catalog-search.ts
  • webapp/src/components/admin/curated-catalog/curated-entry-state.ts
  • webapp/src/components/admin/practice-adoption/AvailablePracticeList.stories.tsx
  • webapp/src/components/admin/practice-adoption/AvailablePracticeList.tsx
  • webapp/src/components/admin/practice-adoption/GroupAdoptionPanel.stories.tsx
  • webapp/src/components/admin/practice-adoption/GroupAdoptionPanel.tsx
  • webapp/src/components/admin/practice-adoption/PracticeAdoptionPanel.stories.tsx
  • webapp/src/components/admin/practice-adoption/PracticeAdoptionPanel.tsx
  • webapp/src/components/admin/practice-adoption/PracticeDefinitionPreview.stories.tsx
  • webapp/src/components/admin/practice-catalog/AreaPill.tsx
  • webapp/src/components/admin/practice-catalog/GroupDetailsDialog.stories.tsx
  • webapp/src/components/admin/practice-catalog/GroupDetailsDialog.tsx
  • webapp/src/components/admin/practice-catalog/GroupPill.stories.tsx
  • webapp/src/components/admin/practice-catalog/GroupPill.tsx
  • webapp/src/components/admin/practice-catalog/GroupVisualPicker.stories.tsx
  • webapp/src/components/admin/practice-catalog/GroupVisualPicker.tsx
  • webapp/src/components/admin/practice-catalog/PracticeDefinitionForm.test.tsx
  • webapp/src/components/admin/practice-catalog/PracticeDefinitionForm.tsx
  • webapp/src/components/admin/practice-catalog/SortableCatalogTree.stories.tsx
  • webapp/src/components/admin/practice-catalog/SortableCatalogTree.tsx
  • webapp/src/components/admin/practice-catalog/area-visuals.test.ts
  • webapp/src/components/admin/practice-catalog/catalog-tree-dnd.test.ts
  • webapp/src/components/admin/practice-catalog/catalog-tree-dnd.ts
  • webapp/src/components/admin/practice-catalog/group-visuals.test.ts
  • webapp/src/components/admin/practice-catalog/group-visuals.ts
  • webapp/src/components/admin/practice-reviews/FeedbackDetailPage.tsx
  • webapp/src/components/admin/practice-reviews/ObservationDetailPage.tsx
  • webapp/src/components/admin/practice-reviews/ObservationFilters.stories.tsx
  • webapp/src/components/admin/practice-reviews/ObservationFilters.tsx
  • webapp/src/components/admin/practice-reviews/ObservationResults.stories.tsx
  • webapp/src/components/admin/practice-reviews/ObservationResults.tsx
  • webapp/src/components/admin/practice-reviews/ObservationsListPage.stories.tsx
  • webapp/src/components/admin/practice-reviews/ObservationsListPage.tsx
  • webapp/src/components/admin/practice-reviews/ProposalReviewPage.tsx
  • webapp/src/components/admin/practice-reviews/ReviewPracticeLink.stories.tsx
  • webapp/src/components/admin/practice-reviews/ReviewPracticeLink.tsx
  • webapp/src/components/admin/practice-reviews/review-search.ts
  • webapp/src/components/admin/practice-reviews/story-mock-data.ts
  • webapp/src/components/admin/practice-reviews/story-mock-server.ts
  • webapp/src/components/admin/practices/CatalogOriginBadge.stories.tsx
  • webapp/src/components/admin/practices/CatalogOriginBadge.tsx
  • webapp/src/components/admin/practices/PracticeCatalog.stories.tsx
  • webapp/src/components/admin/practices/PracticeCatalog.tsx
  • webapp/src/components/admin/practices/PracticeForm.stories.tsx
  • webapp/src/components/admin/practices/PracticeForm.tsx
  • webapp/src/components/admin/practices/PracticeSkeletons.tsx
  • webapp/src/components/admin/practices/WorkspacePracticePanel.stories.tsx
  • webapp/src/components/admin/practices/WorkspacePracticePanel.tsx
  • webapp/src/components/admin/practices/practice-autonomy/PracticeAutonomyPage.stories.tsx
  • webapp/src/components/admin/practices/practice-autonomy/PracticeAutonomyPage.tsx
  • webapp/src/components/admin/practices/practice-autonomy/practice-autonomy-model.test.ts
  • webapp/src/components/admin/practices/practice-autonomy/practice-autonomy-model.ts
  • webapp/src/components/admin/practices/practice-autonomy/story-mock-data.ts
  • webapp/src/components/admin/practices/practice-search.ts
  • webapp/src/components/admin/practices/review/review-sections.ts
  • webapp/src/components/admin/practices/story-mock-data.ts
  • webapp/src/components/core/detail-drawer/DetailDrawerHeader.stories.tsx
  • webapp/src/components/core/detail-drawer/DetailDrawerStack.stories.tsx
  • webapp/src/components/core/detail-drawer/detail-stack.test.ts
  • webapp/src/components/core/detail-drawer/detail-stack.ts
  • webapp/src/components/leaderboard/LeagueIcon.stories.tsx
  • webapp/src/components/practice-vocabulary/catalog-group-action-defs.ts
  • webapp/src/components/practice-vocabulary/dashboard-visibility-defs.ts
  • webapp/src/hooks/practice-catalog-cache.test.ts
  • webapp/src/hooks/practice-catalog-cache.ts
  • webapp/src/hooks/use-practice-autonomy-mutations.ts
  • webapp/src/hooks/use-practice-catalog-mutations.test.tsx
  • webapp/src/hooks/use-practice-catalog-mutations.ts
  • webapp/src/hooks/use-practice-editor.ts
  • webapp/src/hooks/use-practice-review-settings.ts
  • webapp/src/mocks/fixtures/practice-catalog.ts
  • webapp/src/routeTree.gen.ts
  • webapp/src/routes/_authenticated/-CuratedGroupCreateLevel.tsx
  • webapp/src/routes/_authenticated/-CuratedGroupEditLevel.tsx
  • webapp/src/routes/_authenticated/-CuratedPracticeCreateLevel.tsx
  • webapp/src/routes/_authenticated/-CuratedPracticeEditLevel.tsx
  • webapp/src/routes/_authenticated/-admin-curated-catalog-route.test.tsx
  • webapp/src/routes/_authenticated/admin.catalog.groups.$groupSlug.tsx
  • webapp/src/routes/_authenticated/admin.catalog.groups.new.tsx
  • webapp/src/routes/_authenticated/admin.catalog.index.tsx
  • webapp/src/routes/_authenticated/w/$workspaceSlug/admin/practices/-adoption-route.test.tsx
  • webapp/src/routes/_authenticated/w/$workspaceSlug/admin/practices/-catalog-route.test.tsx
  • webapp/src/routes/_authenticated/w/$workspaceSlug/admin/practices/-review-route.test.tsx
  • webapp/src/routes/_authenticated/w/$workspaceSlug/admin/practices/index.tsx
  • webapp/src/routes/_authenticated/w/$workspaceSlug/admin/practices/review.tsx
  • webapp/src/routes/_authenticated/w/$workspaceSlug/admin/practices/reviews/-lists-route.test.tsx
  • webapp/src/routes/_authenticated/w/$workspaceSlug/admin/practices/reviews/observations/index.tsx
  • webapp/src/stories/decorators.tsx

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


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added documentation Improvements or additions to documentation application-server Spring Boot server: APIs, business logic, database feature New feature or enhancement dependencies Package updates, version bumps, lock file changes webapp React app: UI components, routes, state management size:XXL This PR changes 1000+ lines, ignoring generated files. labels Aug 22, 2026
@github-actions

github-actions Bot commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

📚 Documentation Preview

Preview has been removed (PR closed)

@ValentinGruener
ValentinGruener force-pushed the feat/practice-area-server branch 4 times, most recently from 382c636 to 87e2d3b Compare August 24, 2026 05:29
@ValentinGruener
ValentinGruener force-pushed the feat/practice-area-server branch from 90c618a to 1c14fee Compare August 25, 2026 10:03
@ValentinGruener
ValentinGruener force-pushed the feat/practice-area-server branch 2 times, most recently from ba92937 to 7135992 Compare August 27, 2026 11:23
@ValentinGruener
ValentinGruener marked this pull request as ready for review August 27, 2026 11:24
@ValentinGruener
ValentinGruener requested a review from a team as a code owner August 27, 2026 11:24
@FelixTJDietrich FelixTJDietrich changed the title feat(server): expose practice-area standing, trend and review history feat(server): expose practice standings and feedback responses Aug 27, 2026
@FelixTJDietrich FelixTJDietrich changed the title feat(server): expose practice standings and feedback responses feat(server): expose practice-group standings and feedback responses Aug 27, 2026
Valentin Grüner and others added 4 commits August 27, 2026 18:25
Adds the API a developer profile needs to explain a practice area: its
current standing and guidance, the direction its recent evidence points,
the feedback sources behind it, and the review moments that produced it.

Trend direction is evidence-weighted and opportunity-indexed rather than
time-boxed, so a practice recovers after two problem-free reviews instead
of staying marked mixed for the rest of the window.

Delivered feedback can be marked helpful or not helpful.

Includes the regenerated OpenAPI spec and client, which must ship with the
API change: the openapi-validation gate regenerates both and fails on drift.
Both levels now answer the same question — what share of the evidence was
positive — and read the answer off the same scale, so an area can no longer
contradict the cards it is built from.

A practice standing is the recency-weighted share of positive outcomes across
its four newest evidence opportunities. That replaces a pair of rules that
disagreed about both unit and denominator: an existence test over items that
could not tell one problem from fifty, plus a clean-streak override that could.
The decay is derived rather than picked, so two problem-free work items in a row
still acknowledge a fixed habit — and a fresh regression registers just as fast.

An area averages the continuous practice standings and classifies once, instead
of rounding each practice to a label and averaging those.

A practice can now say why it has nothing to say: NOT_OBSERVED when no review
reached it, NO_OPPORTUNITY when the reviews ran and found nothing to report.
Carrying that on the practice lets the area be a pure roll-up and removes the
separate evidence census that existed only to compensate.

Also fixes the latest-run correlation on the learner surfaces: it now matches on
practice and subject, not the artifact alone, so a review that never examined a
practice no longer supersedes what an earlier one found there. The mentor
aggregates keep the old grain deliberately and say so.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017XBuU2tfPFntHoE34Cmw1Z
An area summary treats every practice as equally important, which is rarely
true: a team that cares most about test coverage cannot say so. Practices now
carry an area weight that applies to both halves of the area view — the standing
and the direction — so an area reads as the practices its workspace actually
weighs.

The trend already accepted per-practice weights and folded them into its
inverse-variance precision; both call sites passed an empty map. They now pass
the weights from the reflection snapshot, so the area status and the trend
detail cannot disagree about how much a practice counts. The area standing moves
from a plain average to the weighted one.

Weight 0 withdraws a practice from its area's summary while leaving it fully
reviewed and fully visible: not counting toward the area is a different
statement from not being looked at, and the autonomy ladder already makes the
latter. The default is neutral, so an unconfigured workspace is unaffected.

Nothing about a practice's own card changes — a practice is never judged more
or less harshly for being weighted, only listened to more or less when its area
speaks for it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017XBuU2tfPFntHoE34Cmw1Z
Valentin Grüner and others added 12 commits August 27, 2026 18:25
The package was called reviewhistory but held both halves of the area detail
page: the review history AND the trend query service behind
/practice-areas/{areaSlug}/trend. Its own controller already told the truth —
PracticeAreaDetailController — so the package name was the odd one out, and a
reader opening reviewhistory had no reason to expect a trend service there.

Renamed to areadetail, with a package-info that records where the boundary
runs: the trend QUERY lives here because it answers for a screen, while the
trend MATHEMATICS stays in practices.observation.trend. Nothing outside the
package references any of these types, so the move is closed under the compiler
and no module boundary shifts.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017XBuU2tfPFntHoE34Cmw1Z
The rebase conflicted on openapi.yaml and the generated client, which are
regenerated rather than merged. Resolving them to this branch's side dropped
main's catalog-adoption endpoints and the deletePractices parameter; running
both generators against the merged Java restores them alongside this branch's
own additions.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017XBuU2tfPFntHoE34Cmw1Z
Three states were measured against the evidence this surface actually has and
removed, and one boundary was moved to where the meaning changes.

STABLE is gone. Claiming equivalence is a stronger claim than claiming
difference: the posterior mass inside the practical-equivalence band has to
clear the credibility threshold, which at four opportunities a bundle peaks
around 0.70 and cannot. Reaching it needs roughly 12 opportunities a bundle for
a practice at the extremes and 58 for one in the middle — against the four
available. UNCERTAIN already carries the case honestly: no direction is
supported, which is not the same as asserting there is none.

SupportLevel is gone with it. Once both bundles must be full for a comparison
to happen at all, it has one reachable value; and across all twenty
combinations reachable at this bundle size, a three-item previous bundle never
once changed a verdict against a four-item one, because the posterior already
widens for the smaller sample. TrendSupport keeps its counts, which say the
same thing without grading it.

minBundleSize therefore rises to the bundle size, and the rule states in one
line: the last four against the four before them.

The standing's lower boundary moves from one half to 0.37, which is where the
meaning changes rather than where the number is round. One problem on the
newest work item, everything before it clean, scores 0.384; two in a row score
0.138. A boundary at one half put both on the same side, so for a developer
working at an 80% success rate one review in five read as "needs attention"
while the trend beside it stayed silent — correctly, since a single item is no
evidence of change. Simulated over 20000 runs that fired 19.6% of the time; it
now fires 9.6%, and at a 90% success rate 2.8% rather than 10.1%. The scale is
symmetric again in the unit it reasons about: two clean opportunities earn a
strength, two problems cost one, and a single event of either kind moves the
standing without settling it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017XBuU2tfPFntHoE34Cmw1Z
…ody uses

Rebasing onto main surfaced a break git could not: main removed the ArtifactKind
import from ObservationService while this branch still reads the type at the
artifact-kind filter. Both sides touched different lines of the same file, so the
merge was clean and the compile was not — which is why it only appeared in CI,
where the merge with main is what gets built.

The twenty unused imports either side left behind now fail the build rather than
warn, after main made the Java lint reject violations.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017XBuU2tfPFntHoE34Cmw1Z
Main now runs NullAway in JSpecify mode, so every package states its nullness
and every dereference has to be justified. The rebase itself was almost free —
two conflicts, both unions — and the work was in the annotations the analysis
then demanded.

Five packages this branch added or renamed were missing their @NullMarked
package-info and defaulted to unannotated, which silently exempts them from the
very check that was just turned on. PracticeTrend now says which of its fields
are genuinely absent: both outcome vectors while the evidence is not comparable,
and the posterior difference for INSUFFICIENT_EVIDENCE and for an area result,
which aggregates rather than diffs.

Where a flag and the value it guards were correlated only by convention, the
correlation is now written down: a trend that produced a card is required to
exist, partitioningBy is documented to always yield both keys, and the two
filter-parameter records expose a pageable() that normalises once instead of
leaving each controller to reconstruct it from components the compact
constructor had already defaulted.

Repository query parameters that the JPQL reads as ":x IS NULL OR ..." are
marked nullable, because that is what the query says about them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017XBuU2tfPFntHoE34Cmw1Z
The reflection query dropped every observation whose presence carried no
verdict, so a second query had to count them back for the area status, with
predicates that had to be kept identical by hand. It now takes a verdictsOnly
flag: the mentor and agent context providers still ask for verdicts only, and
the reflection surface asks for everything and filters one layer up, where the
same pass that classifies every other row can also count these.

That pass now groups on ObservationOutcome once instead of testing it two to
four times per row, which makes the split visibly exhaustive: five outcomes,
five buckets, nothing falling through.

Feeding the verdictless rows onward revives the trend package's coverage
handling, which could not fire while they never arrived. Two guards had to
follow: the trail cap now counts opportunities that produced a verdict, so a
quiet stretch cannot push real evidence out of the window a standing reads
from, and the reported calendar span is dated from those same opportunities,
so "these comparisons span N days" keeps meaning comparisons.

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

A developer had two ways to answer one piece of feedback: a reaction saying
what they would do about it, and a rating saying whether it helped. The two
disagreed about their own history — reactions accumulated, ratings were
overwritten — so the same screen could show a dispute from Thursday next to a
rating that had silently replaced Monday's.

They are now one endpoint carrying two independent questions. Either may be
answered alone, and a row records exactly what was said at that moment. That
is what the research record needs: someone who rated a unit helpful on Monday
and disputed it on Thursday said both things, and inventing a Monday dispute to
make the newest row complete would be a worse record than none.

The consequence is that the current answer is not the newest row. It is the
newest value of each question independently, and every read had to learn that:
the response endpoint, the engagement counts, the review-history card, and —
the one that mattered — re-nag suppression, which would otherwise have started
re-delivering a disputed finding the moment its recipient gave it a thumbs-up.
That rule is written once as ReactionRepository.STILL_SPEAKS rather than four
times, because the fourth copy is the one that gets forgotten.

Append-only tables can only accumulate, so a mis-click would have been
permanent. A row carrying neither answer is a withdrawal: it ends the run of
rows before it and leaves nothing on record. It is a flag on the wire rather
than an empty body, so a client that lost its state cannot erase an answer by
asking for nothing.

The reaction column keeps the name `action` — it shipped under that name, and a
released column is renamed across two releases, not in passing. The field says
what the value means.

FeedbackResponseService reaches the caller through CurrentDeveloperLookup like
its neighbours, which retires two frozen module-boundary violations rather than
carrying them forward under a new class name.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017XBuU2tfPFntHoE34Cmw1Z
A working drawing, not a contributor doc. It joins the two research notes that
are already local-only.

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

Three findings from reviewing the branch, none of which local runs had shown.

The full-context budget failed in CI and not here, because the local suite never
runs SpringTestContextArchitectureTest — `pnpm run test:server:verification` is
the command that does, and it is what CI runs. PracticeAreaStatusIntegrationTest
earns a second application context: its nested class registers an
AreaGuidanceProvider bean, and "a provider is present" cannot be tested from the
same context as "none is registered". That is a reviewed cost, so it is now
recorded as one with its justification rather than left to fail the gate.

FeedbackResponseService read the caller through the throwing lookup on all three
methods, including its two reads. CurrentDeveloperLookup says the opposite in as
many words: absence is a normal answer for a read, and only a write has no
meaningful empty result. Its two neighbours already keep that; this one now does
too, so a signed-in person who was never synced as a developer sees no response
and zero counts instead of an error.

ObservationService still declared the visibility policy it stopped using when
the reflection surface moved out. The field was injected and read nowhere.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017XBuU2tfPFntHoE34Cmw1Z
A changeset lands in CHANGELOG.md verbatim, so it may only describe what an
operator can actually do.

"practices can count for more in their area" says a practice can be weighted.
areaWeight exists as a column and both halves of the area view read it, but
nothing writes it — no endpoint, no DTO, no screen. Announcing a setting nobody
can set would be the release note lying.

"an area says which kind of silence it is" says the same thing as "a practice
says why it has nothing to say", which already covers both levels and states
that an area's standing is read straight off its practices.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017XBuU2tfPFntHoE34Cmw1Z
The explanation subquery looked for the newest row carrying a resolution,
so a comment written alongside usefulness alone was stored and then never
read back — and a later resolution overwrote the reading with its own empty
comment. It now looks for the newest row carrying an explanation, which is
the column it was always after. Both readers had the same filter.

Area trends pooled their practices with 1/Sum(w/v), which is the variance of
an inverse-variance mean only when every weight is 1. A weight of 2 halved
the variance it reported and turned an uncertain area into a confident
verdict on the strength of an admin setting. The numerator now carries
Sum(w^2/v) so a uniform rescaling leaves the direction alone.

GraphQlOperationDocumentValidationTest scoped its GitHub carve-out by
matching the English wording of the validation message. graphql-java
localises those, so the build went red on a German workstation while CI
stayed green. It now reads the two field paths, which are part of the query
rather than of the translation.

Also folds the shared query-filter parsing into practices/web and renames
the branch changelog so its timestamp sorts after the released ones.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
PracticeAreaReviewArtifact.title is @nullable on the server and no longer
required in openapi.yaml, but the generated client still declared it as
always present. CI regenerates the client and fails on any diff, so the
two had to be brought back in step.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@FelixTJDietrich
FelixTJDietrich force-pushed the feat/practice-area-server branch from 7135992 to b164e38 Compare August 27, 2026 16:31
@FelixTJDietrich
FelixTJDietrich enabled auto-merge (squash) August 27, 2026 22:14
@FelixTJDietrich
FelixTJDietrich merged commit fc1dbdd into main Aug 27, 2026
39 checks passed
@FelixTJDietrich
FelixTJDietrich deleted the feat/practice-area-server branch August 27, 2026 22:16
FelixTJDietrich added a commit that referenced this pull request Aug 31, 2026
…ping MIGRATION.md

The Version PR fails on every main push with 'MIGRATION.md already
contains ### v0.75.0'. The latest published release is v0.74.0, but
feature PRs merged before the fragment flow hand-wrote '### v0.75.0'
(#1486) and '### v0.76.0' (#1614) headings for work that was never
released, so the version changesets compute now collides with them.

A version can only gain a history section by being released, and
changeset version computes the lowest unreleased version — so any
section at or above it, sitting directly below the '### Next release'
anchor, is unreleased content that ships in the release being cut.
Stamping now folds those sections into the new version section instead
of failing, which also makes regeneration idempotent over the script's
own prior output. The duplicate guard still rejects a same-version
heading anywhere below released history, where it is a real conflict.

Verified against main's actual MIGRATION.md: 'changeset version' plus
this script produces one v0.75.0 section carrying all eleven entries
(three pending, six mislabeled, two fragments) with no content loss,
and a second run is byte-idempotent.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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 dependencies Package updates, version bumps, lock file changes documentation Improvements or additions to documentation feature New feature or enhancement size:XXL This PR changes 1000+ lines, ignoring generated files. webapp React app: UI components, routes, state management

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants