Skip to content

Give every repository one colour, and show it everywhere - #167

Merged
JSdotNet merged 4 commits into
mainfrom
claude/repository-colors-support-701742
Aug 20, 2026
Merged

JSdotNet merged 4 commits into
mainfrom
claude/repository-colors-support-701742

Conversation

@JSdotNet

@JSdotNet JSdotNet commented Aug 20, 2026 •

Copy link
Copy Markdown
Owner

Summary

A workspace holds several repositories at once, and only the roadmap band said which was which. The filter above the backlog, the entries in it and the agent sessions under them read identically in every project, so telling one project's work from another's meant reading the alias on every row.

This gives each configured repository one colour, chosen once in Settings → Repositories, and shows it as a small marker on every surface where that repository appears.

The design system already sanctioned five identity hues (--color-band-1..5) — it just fenced them to the roadmap band. This widens the fence rather than inventing colour: the palette is unchanged, and the count of distinct colours the product declares stays at twenty-five.

Nothing in the existing design is given up. The mark is a 4px rule down the leading edge, painted as an inset shadow rather than a border, so no element's box, padding or alignment moves. Chips take the extra 4px out of their own leading padding, so a coloured chip is exactly as wide as an uncoloured one beside it. Every surface, border and text token each control already wore is untouched — the hue is added to what was correct, never replacing part of it.

That restraint is why the set may leave the roadmap at all. A band is a region of a chart and can afford a 24% wash; a filter chip and an entry row sit in dense chrome that already spends its contrast on status, priority and selection. A wash there would put an identity hue in competition with colour that carries meaning, which is the one thing the design rule forbids.

Changes

Design rule — a precondition, not a follow-up. .design/color-scheme.md#band-identity-tokens said "Nothing outside a roadmap band may use one" and required a new consumer to justify itself against that section. It is now a named consumer table (filter chip, roadmap, entry row, session group, and the Settings picker) with the identity-edge painting rule and the color-background-raised contrast measurements added. Binding pair is color-band-5 at 4.01:1 as an edge on a raised surface, a third above the 3:1 a non-text mark owes.

Storage and resolution. GitHubRepositoryRef.Colour (int?, 1–5) beside CloneDirectory / Token / KnowledgeFolders, persisted in the per-user github.json and preserved when the repository list is retyped. GitHubSettingsStore.SetRepositoryColour follows the house no-save-button rule. One resolver, RepositoryColours.Resolve: an explicit choice wins, everything else takes its position stepping over the hues already claimed, wrapping after five.

Rendering. A shared .repo-mark / .repo-mark--N utility. Applied to the header scope chip, entry rows, the integration link list's repository group, and the repository cell of a row in the Sessions area. Entry rows get it through a new TaskListView.RowCssClass hook rather than a field on TaskRow — that record is deliberately a task and not an entry, and a list of checklist steps has no repository to answer with.

Settings. A swatch radiogroup plus Reset to default in the per-repository tab, with a status line stating the effective colour in words.

Roadmap. PlannedRepository gains Colour and is told its hues instead of choosing them; RoadmapPlanView no longer reads plan.Bands or runs its own allocator. The in-band Colours picker and the whole ColourBand feature — command, handler, IRoadmapPlanning.ColourBandAsync, RoadmapPlan.ColourBand, its error — are removed. One choice, one place.

Migration. RoadmapBand.CarryOverPlanColours seeds Settings from an existing plan file's bands map, only for repositories with no colour of their own, so a choice made in Settings since is never overwritten.

Two things a reviewer should know

The active scope chip is the one case a contrast ratio cannot answer. It is filled with color-primary, and color-band-1 is color-primary — a band-1 rule there measures 1:1 and disappears. It is held off the fill by a 1px seam in color-background, which clears 3:1 on both sides (11.15:1 each way). Verified live: computed box-shadow reads rgb(242,193,78) 4px inset, rgb(18,18,20) 5px inset with the same colour behind it.

One deliberate behaviour change. The roadmap used to number hues over the bands it happened to draw, so a repository with nothing planned let its neighbour slide to a lower hue. It now keeps the hue Settings gave it whether or not its neighbours have work — otherwise the same project would be one colour on the filter and another on the chart, which is the thing this change exists to fix. HuesAreCountedOverTheBandsDrawn_NotOverEveryConfiguredRepository asserted the old behaviour and was rewritten rather than patched.

Accessibility

Colour is identity and never meaning — it sits beside status, priority and selection rather than behind them. It is never the sole carrier: every marked surface still writes the repository alias and every mark is aria-hidden. A session group with no heading gets no hue at all, because the heading is where its repository is named.

Testing

dotnet build Backlog.sln — 0 errors, 0 warnings.
dotnet test Backlog.sln — 2,356 passed, 0 failed across 11 projects (after merging main). 29 new tests:

Suite Covers
RepositoryColourTests (14) resolution, persistence, reset, refusal, drop-not-clamp, list retype
SettingsRepositoryColourTests (5) picker writes immediately, reset, exactly five offered, state said in words
HomeRepositoryScopeTests (4) chip marks, chosen colour reaches the chip, alias still written, mark survives selection
BacklogPaneRepositoryColourTests (4) row marks by area, chosen colour, unfiled wears none, alias still written
IntegrationLinkListTests (+3) headed group wears the mark, unheaded does not, uncoloured host draws none
SessionsPaneTests (+3) repository cell wears the host's colour, an unplaceable repository wears none, no host means no colour
RoadmapBandDatesAndColoursTests band reads Settings, no picker of its own, migration carries over, Settings wins

node .github/tools/knowledge-meta/build.mjs --check — clean.

Manual QA with Playwright against desktop-web-harness and ui-storybook under Aspire — 8 scenarios, all pass, no console errors and no Aspire errors:

  • One choice in Settings reached the chip, the entry row and the roadmap band together; Dates and Unfiled bands stayed neutral.
  • The choice hit github.json on each click, and Reset cleared it while leaving the repository with its positional hue.
  • The band-1-on-selected-chip seam holds, as above.
  • Four repository groups in the reference list each carried their hue; the unheaded group and "Not in a repository" carried none, with state chips beside them unaffected.
  • The migration was verified against real pre-existing data, not a fixture: the harness workspace already held _roadmap/plan.json with bands: {backlog: 3} from an earlier session, and the app carried it onto the repository on first render.

Running Aspire for this checkout was stopped with build\stop-aspire-before-pr.ps1, scoped to this worktree so parallel sessions were left alone.

Related

  • .design/color-scheme.md#band-identity-tokens — amended by this PR; it forbade the change until it was.
  • .domain/roadmap/features.md#feature-repository-scoped-planning — the roadmap feature the hue set was originally introduced for.

After merging main

main brought a Sessions area with a Repository column, added while this branch was in flight. That is the surface the request named and this branch could not reach at the time — sessions existed only in the shared library. It is now covered: the repository cell carries the mark, beside the name rather than down the row, because a row there is a session and the repository is one of eight things it reports.

The pane is told the colour rather than looking it up, the same way the roadmap and the entry list are — the Sessions context holds owner/name as an opaque string on purpose, and resolving it there would tie sessions to repository management for a decoration.

Merging also resolved conflicts in three derived _meta graphs. Generated files were regenerated rather than hand-merged, and HomeRepositoryScopeTests had its copy of the Home harness refreshed from HomeWorkspaceSurfaceTests after main's storage refactor removed RepositoryBacklogSource.

Follow-up

IntegrationLinkList — the per-entry panel that groups linked issues, PRs and agent sessions by repository — is still not wired into the desktop app. The mark is implemented on the component and demonstrated in the Storybook, so it arrives with the panel when that surface is adopted. This is separate from the Sessions area above.

Checklist

  • Scope is focused and excludes unrelated changes
  • Documentation or instruction files were updated when governance or behavior changed
  • Running Aspire instances for this checkout were stopped before PR creation (build\stop-aspire-before-pr.ps1)
  • Follow-up work is captured when the implementation is intentionally partial

🤖 Generated with Claude Code

JSdotNet and others added 4 commits August 20, 2026 01:14
A workspace holds several repositories at once and only the roadmap band said
which was which. The filter above the backlog, the entries in it and the agent
sessions under them all read the same in every project, so telling one project's
work from another's meant reading the alias on every row.

The design system already sanctioned five identity hues — it just fenced them to
the roadmap band. Widen the fence rather than invent colour, and make the mark
additive: a 4px rule down the leading edge, painted as an inset shadow so no
element's box, padding or alignment moves. Every surface, border and text token
stays exactly as it was, which is the whole reason the set may leave the roadmap.
A band is a region of a chart and can afford a 24% wash; a filter chip and an
entry row sit in dense chrome that already spends its contrast on status,
priority and selection, and a wash there would put identity in competition with
colour that carries meaning.

The choice moves out of the roadmap plan file and into the repository it is about.
It was stored per plan, which made it a fact about one chart — but four surfaces
now read it, and a surface that worked its own out would be a second answer to
which colour a project is. So GitHubRepositoryRef carries the number, one
resolver in RepositoryColours answers for everyone, and the roadmap is told its
hues rather than choosing them. Its in-band picker and the whole ColourBand
feature go with it: one choice, one place.

The active scope chip is the one case a contrast ratio cannot answer. It is
filled with color-primary, and color-band-1 *is* color-primary, so a band-1 rule
there would measure 1:1 and disappear. It is held off the fill by a 1px seam in
color-background, which clears 3:1 on both sides.

Colour is never the sole carrier: every marked surface still writes the alias and
every mark is aria-hidden. A session group with no heading gets no hue at all,
because the heading is where its repository is named.

One deliberate behaviour change. The roadmap used to number hues over the bands
it happened to draw, so a repository with nothing planned let its neighbour slide
to a lower hue. It now keeps the hue Settings gave it — otherwise the same
project would be one colour on the filter and another on the chart, which is the
thing this change exists to fix.

Band colours in an existing plan file are carried onto the repository the first
time the roadmap opens, and never over a choice made in Settings since.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The code change moved the choice out of the roadmap plan and onto the repository,
and two governed chapters still described the old arrangement. The roadmap
sub-feature said the choice "is kept with the plan", which is now the opposite of
what happens and would send the next reader looking for it in the wrong context.

Repository Management gains the sub-feature, because that is where the fact
belongs: one colour per registered repository, recorded as which of the sanctioned
ones rather than as a colour, placed automatically by position when nobody has
chosen, and returnable to that placement.

The roadmap chapter keeps its sub-feature — reading a portfolio plan one project
at a time is still a roadmap concern — but now says the plan is *told* the colour
and stores nothing, for the same reason it holds repository aliases as opaque
strings. It also records what the unfiled and dates bands do, which the prose had
left out: they take no colour, because a colour there means "which repository" and
neither of them is one.

The derived _meta graphs are regenerated with them. They were already stale for
the .design chapter edited in the previous commit — build.mjs --check validates
index ordering rather than graph content, so it passed without noticing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Only the three derived _meta graphs conflicted, and a hand-merged generated file
is a file that disagrees with its generator. Took either side and ran build.mjs,
which is the only thing entitled to write them.

Main's storage refactor took RepositoryBacklogSource away, and this branch's new
HomeRepositoryScopeTests carried a copy of the Home harness written against it.
The harness is copied from HomeWorkspaceSurfaceTests again so it tracks whatever
that one composes now, with only the two-repository setup this test needs on top.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Main brought a Sessions area with a Repository column, which is the surface this
change was asked to cover and could not: when the work started, sessions existed
only in the shared library and the storybook. Now they are on screen, so they get
the same mark as everything else.

Beside the repository name rather than down the row. A row here is a session and
the repository is one of eight things it reports; a rule down its leading edge
would read as if the session itself belonged to that project, which is not what
the row is about. On the cell, the name is right there — which is also where the
design rule wants the mark, since it is never the sole carrier.

The pane is told the colour rather than looking it up, the same way the roadmap
and the entry list are. This context holds owner/name as an opaque string on
purpose, and resolving it here would tie sessions to repository management for a
decoration. The shell answers, so all five surfaces read one answer.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@JSdotNet
JSdotNet merged commit e730d9f into main Aug 20, 2026
5 checks passed
@JSdotNet
JSdotNet deleted the claude/repository-colors-support-701742 branch August 20, 2026 05:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant