feat: visual indicator for stateless-mode PRs#545
Merged
Conversation
Indicator pill next to iteration tabs surfacing stateless mode, with an actionable sign-in variant when the cause is an anonymous session. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the free-text statelessReason string with a typed
StatelessReason union ('unauthenticated' | 'no-artifact') in both the
canonical types.ts and the store's local IterationState copy.
loadIterations now sets enum values instead of sentences.
Validated red->green: typecheck flagged the old string assignments as
errors against the new union before they were converted; the new
useIterationStore test asserting 'unauthenticated' fails when the
assertion is flipped to 'no-artifact', proving it discriminates the
artifact-present-but-signed-out branch from the no-artifact branch.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Renders null in stateful mode, a neutral info pill ("Stateless") for the
no-artifact reason, and an actionable sign-in pill for unauthenticated —
the latter wired to useOAuthFlow's initiateOAuth. Both variants are
focusable react-aria Buttons with an aria-label (state is never conveyed
by colour alone) and a TooltipTrigger explaining the cause.
Subscribes to mode + statelessReason via store selectors so the pill
reacts to live store transitions, not just prop rerenders. Exported from
the component and feature barrels.
Validated red->green: the unit test (StatelessModeIndicator.test.tsx) and
the tester's integration test both failed with "cannot resolve
./StatelessModeIndicator" before the component existed; all 9 pass after.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds .stateless-indicator base + --info (badge-merged) and --action (error-fg, hover/focus brightness + focus outline) variants, plus the tooltip surface. The tooltip reuses the proven .field-tip token set (contextmenu-bg/main-fg/combobox-border) rather than the plan's translucent --menu-hover/--watermark-text, which would have rendered a near-transparent, low-contrast surface. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Render <StatelessModeIndicator /> as a sibling of <IterationSelector /> in both .diff-header-iterations blocks (description view + sticky file header). Add an 8px gap to .diff-header-iterations so the pill is spaced from the iteration tabs. In stateful mode the indicator returns null, so the container's :empty hide rule is unaffected. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Covers the stateless-reason matrix end-to-end via real user journeys:
- no-artifact (signed in, default mock PR): neutral info pill, /stateless/i +
class stateless-indicator--info, asserted NOT the action variant.
- unauthenticated (signed out + a CodjiFlo artifact-reference PR comment whose
artifact download is rejected): sign-in action pill, /sign in/i + class
stateless-indicator--action, asserted NOT the info variant.
- pressing the sign-in pill initiates the GitHub OAuth flow (intercepts the
github.com/login/oauth/authorize redirect to prove initiateOAuth fired from a
real click).
Both reasons are reached through real input paths (mock GitHub API + real
navigation), not test-only state. RED was confirmed before the component
existed (getByTestId('stateless-indicator') not found); GREEN after the
component + DiffView wiring landed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The new unauthenticated stateless test depends on a signed-out auth store. Reset useAuthStore.token to null in the top-level beforeEach so that dependency is explicit and cannot bleed into order-dependent sibling tests (per code review). The per-test setState is now redundant and removed; the comment points at the beforeEach guarantee. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Task-1 enum change had a second consumer the plan missed:
useIterationAwareFiles' stateless fallback console.warn interpolated
statelessReason raw, so it went from a sentence to the terse enum token
("no-artifact"). That broke the Issue #186 E2E, which asserts the
warning carries a human-meaningful reason (.toContain("CodjiFlo
artifact")).
Keep the enum canonical in the store; map it to human-readable copy at
the emission point via STATELESS_REASON_DESCRIPTION (mirrors how
StatelessModeIndicator maps the enum to tooltip copy). Update the hook's
integration test to drive enum values and assert the mapped sentence,
and type the mock store's statelessReason as StatelessReason | null so an
invalid value would fail typecheck.
Verified: full `npm run test:all` is green — lint, typecheck,
spec:validate (11), vitest coverage (1544), E2E (124, incl. #186),
storybook (31). Grep of src confirms this was the last raw consumer of
statelessReason; the only other reader is StatelessModeIndicator, which
already branches on the enum.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CodjiFlo Iteration TrackingIterations captured: 1 What is this?This comment is automatically updated by the CodjiFlo GitHub Action to enable force-push resilient code review with iteration tracking. The artifact referenced above contains iteration data that the CodjiFlo frontend uses to:
|
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
codjiflo | 5d56d0f | Commit Preview URL Branch Preview URL |
Jun 05 2026, 12:22 AM |
Coverage Report
File Coverage
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
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.
Summary
StatelessModeIndicatorpill next to the iteration tabs so reviewers can see at a glance when a PR's iteration data is in stateless mode (the GitHub Timeline-API fallback) rather than the full GitHub-Action artifact mode.statelessReasonfrom a free-textstringto a typedStatelessReason = 'unauthenticated' | 'no-artifact'enum, and maps that enum to human-readable copy at each presentation point (tooltip + the existing fallback console warning).Details
StatelessModeIndicatoris built from react-ariaButton+Tooltip, lucide icons, with anaria-labelso state isn't conveyed by colour alone. Rendered as a sibling of<IterationSelector />in both.diff-header-iterationsblocks; returnsnullin stateful mode.statelessReason(useIterationAwareFilesfallback warning) is updated to map the enum to a human sentence, preserving the Issue Add logging when GitHub API is used as fallback in iteration mode #186 warning contract.Test Plan
npm run test:allgreen: lint, typecheck, spec:validate (11), vitest+coverage (1544), E2E (124), storybook (31)🤖 Generated with Claude Code
🔍 Review in CodjiFlo