feat(gears): read the delivery board and show it as four Development lenses - #3014
Draft
hello1101n wants to merge 23 commits into
Draft
feat(gears): read the delivery board and show it as four Development lenses#3014hello1101n wants to merge 23 commits into
hello1101n wants to merge 23 commits into
Conversation
…ty schedule The board's status ladder, milestones and estimates live across two bronze relations; this reads them as one row set and answers /v1/gear-roadmap with both the per-gear view and a lane schedule. Capacity is assumed at one man-day per person per day and stated in the response, so a consumer renders the assumption rather than hiding it. A milestone that has already passed is placed as overdue rather than folded into the backlog, and an unassigned gear takes a lane of its own instead of queueing behind every other unassigned one. Signed-off-by: hello1101n <hello1101n@users.noreply.github.com>
Four views over the board endpoint: a per-subsystem rollup, the gear table, a month grid and the assumed-capacity schedule. The grid keeps overdue work in its own column rather than mixing it with unscheduled work, and every view states the capacity the schedule assumed instead of presenting it as measured. Signed-off-by: hello1101n <hello1101n@users.noreply.github.com>
The project number is deployment-specific, so it arrives as a config leaf rather than a literal. Non-positive means no board is named and the endpoint answers with a failed precondition instead of an empty roadmap. Signed-off-by: hello1101n <hello1101n@users.noreply.github.com>
The pages were standalone routes reached from the legacy sidebar, so they sat outside the shell every other surface lives in. They are now a portal rail zone with four pane items, dispatched by ZoneContent like the other zones, and the legacy sidebar entry is gone. Signed-off-by: hello1101n <hello1101n@users.noreply.github.com>
A rail zone of its own put the board beside the org zones, which is not what it is: gear delivery is part of the Development direction. The lens registry gains a board entry kind for a lens whose content is the board rather than metric sections, so it contributes no measure keys to the direction's person grid. The schedule also gets the treatment its data deserves: a month axis with gridlines, a sticky assignee column, one colour per subsystem, and labels that truncate instead of overflowing. The grid and both tables pick up the same colour key, aligned numerals, readiness meters and a totals row. Signed-off-by: hello1101n <hello1101n@users.noreply.github.com>
A bar was a coloured rectangle with a native tooltip; hovering one now opens a card naming the gear, the days it was scheduled for, what is left of its estimate, its implementation share and its milestone. Issue and account links resolve through the external-source registry rather than a hardcoded host, so a deployment that configures no source renders plain text instead of a wrong link. The registry gains account_href for the account page a git host puts under a stable path; trackers resolve to nothing. The gear table gains one toggle per subsystem, counted, colour-keyed and combinable with the text filter — an empty choice means every subsystem. Also fixes a read that could not decode: the repository column comes through the CTE as a GROUP BY key, so it keeps its source nullability and needs a default in the projection, which a guard test now states. Signed-off-by: hello1101n <hello1101n@users.noreply.github.com>
…rson The subsystem filter was a row of toggles as wide as the board had prefixes, which read as content rather than as a control. It is now a single select beside the search box, carrying each subsystem's colour and gear count. Schedule lanes name a login, so the account page is resolved through a gear that person is on — that is where the source id is recorded. A lane with no configured source stays plain text. Signed-off-by: hello1101n <hello1101n@users.noreply.github.com>
Four cleanup passes over the feature. The lens view now owns the query and the pending/error branch; the four boards take resolved data. They had each re-run the same query and re-implemented the branch, and the copies had already drifted apart. Link-or-text, the assignee list and the bar-plus-percentage widget were written two to four times each; they are now the existing RecordLink plus two small shared parts. Man-days and percentages go through lib/format's helpers and its absent-value sentinel instead of ad hoc toFixed and a bare dash. Month stepping uses date-fns, as the timeseries model already does, rather than a third hand-rolled UTC loop. A gear's placement is one tagged value instead of a label plus a slot that only one label could carry, so a reader narrows once and an overdue gear cannot hold a month index. A bar keeps its dates only and the view derives its geometry. Two efficiency shapes: the subsystem rollup pushes instead of rebuilding each group's array per gear, and lane account links read one login-to-source index instead of scanning every gear per lane. The lens registry gains one exported guard for entries that carry no metric sections, replacing three ad hoc shape checks — including the second one bolted onto an existing condition. Signed-off-by: hello1101n <hello1101n@users.noreply.github.com>
… late The two odd lens names are now parallel with the rest: Gear summary and Gear list say what shape they are, the way Gear roadmap and Gear schedule do. Three locale keys the portal move had orphaned are gone with them. Ordering belongs to the endpoint, not the table: /v1/gear-roadmap takes sort and direction, refuses a column it does not know, and puts gears carrying no value last in both directions. Milestone order leads with the worst overrun, because an overdue gear is not merely an earlier month. The column and its direction ride in the URL, so an ordered view is a link. A summary row opens the gear list narrowed to that subsystem, and the chosen subsystem is in the URL too, which makes any filtered list linkable. A milestone names a month, so overdue counts days from the day after that month ended, and the count rides on the overdue placement itself — no other state can carry one. Signed-off-by: hello1101n <hello1101n@users.noreply.github.com>
…ss boards A column now cycles largest-first, smallest-first, off, so a reader puts a table back the way they found it with the control they already used. Cleared means the request carries no order at all and the server's own applies. Milestone was doing two jobs in one column. It now says only what the board promised — with how many days it is already past — and a Forecast column says which month the schedule lands the gear in, marked when that is later than the promise. That is the split the spreadsheet makes, and the forecast comes from the schedule that already runs for the lanes. The subsystem in the URL now reaches every board, not just the list: the roadmap grid narrows to it and the schedule keeps the whole board's span while drawing only the bars that subsystem owns. Overdue chips in the grid carry their day count too. Signed-off-by: hello1101n <hello1101n@users.noreply.github.com>
Placement compared a gear's milestone to today and never asked whether the work was done, so every delivered gear whose month had passed was counted late — red in the grid's overdue column and marked as days late in the list. Delivered is now its own placement, set when the board says Done or the tracker says closed, since the two disagree in practice. The grid gives it a column of its own rather than dropping it, so finished work stays visible as history instead of vanishing or masquerading as a miss. Signed-off-by: hello1101n <hello1101n@users.noreply.github.com>
It named a source id no sync produces, so it resolved no links for anyone but the machine that stamped that id by hand. Signed-off-by: hello1101n <hello1101n@users.noreply.github.com>
…view Signed-off-by: hello1101n <hello1101n@users.noreply.github.com> # Conflicts: # src/backend/services/analytics/src/api/mod.rs # src/backend/services/analytics/src/api/openapi_tests.rs # src/frontend/src/lib/portal/portal-nav.ts
|
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: trueComment |
Clippy under -D warnings fails the test target with unfulfilled_lint_expectations: three test modules expect unwrap_used but only call expect(), and three expect both lints while calling neither. Keep only the expectation each module actually fulfils. Signed-off-by: hello1101n <hello1101n@users.noreply.github.com>
Written through scripts/ci/openapi_spec.py update so the on-disk form matches what the drift gate compares against. Signed-off-by: hello1101n <hello1101n@users.noreply.github.com>
tests/generate_schemas.py derives them from the committed analytics OpenAPI document, which now describes the gear roadmap response. Signed-off-by: hello1101n <hello1101n@users.noreply.github.com>
Signed-off-by: hello1101n <hello1101n@users.noreply.github.com>
The board number was a config leaf only docker-compose set, so every Helm and GitOps install deserialized 0 and the endpoint answered BOARD_UNSET. Plumbing it through four more config surfaces would have kept a value the data already knows. GET /v1/gear-roadmap now takes a required project parameter, and GET /v1/gear-roadmap/boards lists the boards bronze holds so the lens can offer them. One board is read without asking; a second turns on a picker. Also declares sort and direction on the operation, which the handler accepted and the frontend sent but the contract never described. Signed-off-by: hello1101n <hello1101n@users.noreply.github.com>
…e board project_items is a ReplacingMergeTree keyed per card, so an archived re-read and its older active version coexist until a merge. Filtering is_archived in the WHERE dropped the archived latest and grouped the stale active row, so an archived card came back until ClickHouse merged. Resolve the flag with argMax and filter the resolved value instead. The issues expression carried no predicate, so it aggregated every issue of every repository and tenant before the join narrowed it. Restrict it to the repositories the board actually references. Cap the read, matching the connector-health precedent: an unbounded response is a bug however unlikely the input, and reaching the cap logs rather than silently serving a partial board. Signed-off-by: hello1101n <hello1101n@users.noreply.github.com>
A gear closed on the tracker below 100% on the board was delivered to the scheduler — no lane, no forecast — while remaining_man_days still billed the unfinished ladder, and the frontend summary counted done by percent alone. Both now read the delivered state the rest of the panes read. The gantt built dates at UTC midnight and then did month arithmetic in the host's local calendar. West of UTC that midnight is the previous local day, so the labels started a month early and could skip a month outright. The arithmetic no longer touches a zone-bearing Date. The test timezone was pinned to Kiritimati at UTC+14, where a UTC midnight is still the same local day and the mixed implementation reads as correct. Pinned west of UTC instead, which fails on the old code, and covered monthLabels, which had no test at all. Signed-off-by: hello1101n <hello1101n@users.noreply.github.com>
The handler calls require_admin on every gear roadmap read, but the four lenses were listed for anyone who could reach Directions, so a non-admin manager saw four ordinary navigation entries that answered 403 and rendered the generic load failure. The lens list is bare strings with no metadata slot, so the gate reads the registry entry instead: a board lens is exactly one carrying a board, which is also exactly the set the backend admin-gates. Signed-off-by: hello1101n <hello1101n@users.noreply.github.com>
…lone
A GitHub project board is org-scoped, so repo-a#7 and repo-b#7 can both
sit on it. The forecast map, the schedule lookup and the React keys all
used the issue number, so one card's forecast and schedule bar silently
stood in for the other's and two rows shared a key.
The row already carried repo_full_name and the wire contract dropped it.
Gears now carry {repository}#{issue} end to end: the response keys on it,
the schedule resolves bars through it, and the tables key rows by it.
The number stays on the DTO because it is what a reader recognises.
Signed-off-by: hello1101n <hello1101n@users.noreply.github.com>
…board number as non-null Two faults that only a running stand shows, both green through CI. The gear roadmap registered its response DTO as GearDto, which the gear-orchestrator gear already owns — a name it took when this branch merged main. The host panics on the collision at boot and never binds, so every request 502s. The offline OpenAPI test and both drift gates build the analytics route table alone and never load the orchestrator, so none of them can see it. Renamed to RoadmapGearDto, and the contract now asserts it claims no name a host gear owns. The boards read projected toInt64(project_number) while the column is nullable, so the row decoder refused it and the endpoint 500'd on every call. The predicate already excludes nulls; the projection now says so, guarded the same way the gear read guards its repository column. Signed-off-by: hello1101n <hello1101n@users.noreply.github.com>
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.
Part of #2906.
Why. Gear delivery is tracked on a GitHub project board and read through a spreadsheet run by hand, so nothing in Insight answers where a gear stands or what is late.
What changed. An admin-gated
GET /v1/gear-roadmap?project=<n>reads the board out of bronze — the board's own fields joined to the issues carrying milestone, assignee and estimate — and four Development lenses render it: a per-subsystem rollup, the gear list, a month grid, and a schedule of what is left.The caller names the board, and
GET /v1/gear-roadmap/boardslists the ones bronze holds. It began as deployment config, which only compose ever set, so every Helm and GitOps install answered 412 while four more config surfaces waited to be plumbed. Which board exists is a fact about the synced data, so the data answers it. One board is read without asking; a second turns on a picker.Capacity is assumed at one man-day per person per day, and every pane says so. Real capacity lives outside Insight, so the schedule states its assumption rather than presenting it as measured.
Ordering belongs to the endpoint.
sortanddirectionare query parameters, an unknown column is refused, and gears carrying no value sort last in both directions — "unestimated" is not a small number.Delivered is its own placement. Done on the board or closed on the tracker settles it, so finished work never reads as overdue, carries no remaining effort, and counts as done in every pane alike.
Worth knowing before you read the diff
bronze_github.issues, not the board item's own field values: the connector returns mirror-typed values as bare typenames.{repository}#{issue}end to end — the number alone collides.project_itemsis aReplacingMergeTree, sois_archivedis resolved withargMaxand filtered after. Filtering it first drops the archived latest and keeps a stale active row.presentation_rogets itsbronze_*grants fromprovision-presentation-access.sh, which enumerates them on every deploy. A newly synced bronze database becomes readable at the next one.Open after this
Out of scope. Modelling the board into a granted database, and moving the rollup into metric definitions once org-grain metrics land.
Verified. 859 backend and 2454 frontend tests on this branch; clippy, eslint, tsc, rustfmt, and the OpenAPI and generated-model drift gates clean. Every lens driven in a browser against a local stand. No screenshots — this repo bans production-derived content and the stand holds real board rows.