feat(server): expose practice-area standing, trend and review history - #1486
Draft
ValentinGruener wants to merge 8 commits into
Draft
feat(server): expose practice-area standing, trend and review history#1486ValentinGruener wants to merge 8 commits into
ValentinGruener wants to merge 8 commits into
Conversation
Contributor
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: trueThanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Contributor
📚 Documentation PreviewThis PR includes documentation changes. A preview has been deployed: Preview for commit 1c14fee. Updates automatically on new commits. |
This was referenced Aug 22, 2026
ValentinGruener
force-pushed
the
feat/practice-area-server
branch
4 times, most recently
from
August 24, 2026 05:29
382c636 to
87e2d3b
Compare
This was referenced Aug 24, 2026
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
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
ValentinGruener
force-pushed
the
feat/practice-area-server
branch
from
August 25, 2026 10:03
90c618a to
1c14fee
Compare
2 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
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. Delivered feedback can be marked helpful or not helpful.
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 90-day window.
Bottom of stack #1488. #1487 builds the profile surfaces on this API. Nothing here is reachable from the UI yet — that is deliberate, so this half can be reviewed on its own.
This replaces the abandoned #1448, which carried the whole feature as one 49-file PR against a
mainit had drifted 20 commits behind.Why the generated client is in this PR
webapp/src/api/**ships withopenapi.yamlrather than with the UI layer above. Theopenapi-validationgate regenerates both and fails on any drift, so splitting them would leave this PR red by construction.How to test
CI covers the server side. To exercise the endpoints by hand:
Checklist
.changeset/README.md**Operators:** …) andMIGRATION.mdis updatedThree changesets ride along: the profile API, evidence-based trends, and the two-clean-reviews recovery rule. The Liquibase changelog runs automatically; no operator action.