Skip to content

feat(chat): show git working-tree status in the composer footer - #7436

Merged
bolichen97 merged 1 commit into
kirodotdev:mainfrom
jingchaodev:feat/footer-git-status
Sep 2, 2026
Merged

feat(chat): show git working-tree status in the composer footer#7436
bolichen97 merged 1 commit into
kirodotdev:mainfrom
jingchaodev:feat/footer-git-status

Conversation

@jingchaodev

@jingchaodev jingchaodev commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Problem / Motivation

The composer footer names the checked-out branch but says nothing about its state. To learn whether the working tree is dirty, or whether the branch is ahead of / behind its upstream, the user has to open the Git panel — there is no at-a-glance signal while chatting, even though the agent may be editing files in that tree right now.

Why it matters

Sessions in this dashboard routinely drive real checkouts (worktrees, feature branches). Uncommitted work and an out-of-sync branch are exactly the states a user wants to notice before asking for a rebase, a push, or a new task — today those states are invisible until something fails. The footer already carries the branch name; one glance should also answer "anything uncommitted? anything unpushed/unpulled?".

What changed (motivation → approach → change)

Goal: surface working-tree status without new backend surface or polling cost.

Approach: the Git panel already computes everything needed via /api/project/git/status (file list, ahead, behind). The footer badge subscribes to the same React Query key (['git-status', dir]), so panel and footer dedupe into one fetch — while the panel is open its 5s interval drives the shared cache; when closed, the footer's slow 60s interval takes over. The query is gated on repo === true from the existing cheap HEAD-file probe, so non-repo projects never spawn a git subprocess on an interval.

Built:

  • ChatInput.tsx: a compact badge after the branch label — uncommitted file count in a warn-tinted pill plus ↑n ↓m ahead/behind arrows, the Git panel's own vocabulary. It renders only when there is signal: a clean, in-sync tree keeps the footer exactly as before. It is a passive readout (role="status"), not a button: the shelf row already carries three actions on base and the max-two-buttons-per-row rule forbids growing a 3+ row — the Git panel stays one click away in the sidebar. Tooltip/aria-label reuse the existing components.gitPanel.uncommitted catalog entry plus script-neutral arrow glyphs, so the change adds no i18n keys.
  • ChatPage.tsx: the shared-key query, plus a running→idle invalidation so the badge refreshes the moment a turn ends.
  • scripts/capture-footer-git-status.mjs: fixture-driven capture harness for the screenshots below, following the existing capture-*.mjs pattern.

All color comes from theme variables (warn, muted, --bg-elevated) per the theming contract — no literals, so the badge follows every theme (dark + light shots below).

Tests

src/test/ChatInput.gitBadge.test.tsx (6 tests):

  • badge shows dirty count, ahead and behind, as a passive readout (span, not a button — the row must not gain a fourth action)
  • arrows render alone on a clean-but-out-of-sync tree; count renders alone when in sync
  • nothing renders when the tree is clean and in sync (footer unchanged)
  • the badge renders while a response is running (read-only signal), and survives compact shelf widths (320px)
  • tooltip composes from the existing catalog entry (3 uncommitted · ↑1 · ↓2)

Existing ChatInput.projectBranch.test.tsx (11 tests) and all ChatPage.* suites pass unchanged.

Manual verification

Captured through the fixture harness against the real built SPA (node scripts/capture-footer-git-status.mjs): badge geometry, tooltip text, the sidebar Git panel showing the same counts, and both themes. Shots below are those runs.

Screenshots / video

Footer with the badge — 3 uncommitted files, 1 ahead, 2 behind (dark):

Footer badge, dark

Light theme:

Footer badge, light

After clicking the badge — Git panel opens with the full status (same counts: 3 uncommitted, ↑1 ↓2, per-file +/− line counts, commits):

Git panel opened by the badge click

Full-page context (dark)

Full dashboard with the badge

Related Issues

N/A — no existing issue; searched open/closed issues and PRs for footer/branch/git-status work before building (nearest is #3987, which targets repo discovery in the file rail, a different surface).

Checklist

  • At most two commits (one is the norm), with a Conventional Commits title (feat|fix|docs|refactor|perf|test|chore|ci|build|revert: ...)
  • Existing tests pass and new tests added for new functionality
  • Self-review completed; code follows project style guidelines
  • Documentation updated (if applicable) — N/A, no documented surface changes
  • No secrets, credentials, or internal references in the diff

@jingchaodev
jingchaodev requested a review from a team September 1, 2026 02:29
@jingchaodev
jingchaodev requested a review from a team as a code owner September 1, 2026 02:29
@github-actions github-actions Bot added fork Pull request from a fork (external contributor) readiness: action required A blocking check or review needs attention readiness: checking Automated validation is still running and removed readiness: action required A blocking check or review needs attention readiness: checking Automated validation is still running labels Sep 1, 2026
@jingchaodev
jingchaodev force-pushed the feat/footer-git-status branch from 906cc59 to 8996e67 Compare September 1, 2026 03:17
@github-actions github-actions Bot added readiness: action required A blocking check or review needs attention and removed readiness: checking Automated validation is still running labels Sep 1, 2026
@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Design Review (Fable 5, fork) — ✅ PASS

Design-level review of 56e14bcf6fcc818a728c6b5993073d456024ae39 via the fork AI-review pipeline — updated in place on each push. A BLOCK verdict blocks PR readiness; PASS/CONCERNS are advisory.

Design-Verdict: PASS

Reuses existing backend surface and the panel's shared query key; a passive, signal-only readout at the right layer — proportionate and reversible.

[DESIGN-REVIEWED] 56e14bc

@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

First Principles Review (Fable 5, fork) — ✅ PASS

Premise-level review of 56e14bcf6fcc818a728c6b5993073d456024ae39 via the fork AI-review pipeline — why this exists and whether the shipped surface is the smallest honest version. Updated in place on each push. A BLOCK verdict blocks PR readiness; PASS/CONCERNS are advisory.

All the PR's factual claims verify against the base: the ['git-status', dir] key is the established convention (5 existing non-test consumers: GitPanel.tsx:58, FileBrowserRail.tsx:77, FilesHomePanel.tsx:45, FolderPanel.tsx:225, PierreWorkspaceTreeImpl.tsx:160), api.projectGitStatus and the repo field on the HEAD probe exist (client.ts:2467-2468), the components.gitPanel.uncommitted catalog key exists in the locales, temp-screenshots/ is documented in .gitignore as committed deliverables, the capture-script pattern has 50+ siblings, and no running→idle git-status invalidation existed before (only two unrelated invalidateQueries calls in ChatPage). No always-visible surface showed dirty/ahead/behind before — the count lived only inside the FileBrowserRail and Git panel. Final review:

First-Principles-Verdict: PASS

Surfaces existing git-status data in the footer by subscribing to the panel's own query key — no new backend, catalog, or duplicate fetch path.

What this change ships

Intent: let a user see uncommitted/unpushed state at a glance while chatting, without opening the Git panel. ADDITION.

  1. Footer shows a dirty-count pill plus ↑/↓ arrows beside the branch; hidden when clean and in sync — justified
  2. Chat page now polls git status every 60s with panels closed (repo-gated) — declared, reuses shared ['git-status', dir] key (5 existing consumers)
  3. Badge refreshes the moment a turn ends (new invalidation; none existed in base) — justified
  4. Three new ChatInput props carry the counts — one consumer each, plain wiring
  5. New capture script following the capture-*.mjs pattern (50+ siblings) — justified
  6. Four screenshots committed under temp-screenshots/ — documented committed-deliverable convention

Watch

  • The badge's stated harm ("invisible until something fails") is the author's own reasoning — no reported issue or complaint is cited (Related Issues: N/A). The harm is concrete and the cost is small (zero new backend surface, zero i18n keys, hidden when clean), so this is provenance worth one human glance, not a defect.

[FIRST-PRINCIPLES-REVIEWED] 56e14bc

@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

UX Review (Fable 5, fork) — 🟡 CONCERNS

UX-level review of 56e14bcf6fcc818a728c6b5993073d456024ae39 via the fork AI-review pipeline — updated in place on each push. A BLOCK verdict blocks PR readiness; PASS/CONCERNS are advisory.

I have everything I need. The evidence is conclusive on the key finding: the PR description's screenshot caption claims "After clicking the badge — Git panel opens," but the badge is a deliberately passive <span> — the capture script itself opens the panel via the auto_open_git_panel config opt-in and comments "the badge is a passive readout, not a click target." The badge's visual vocabulary (warn pill, ↑n ↓m glyphs) exactly matches the existing Git panel header, and the passive/no-fourth-action choice is grounded in the repo's documented max-two-buttons-per-row rule.

UX-Verdict: CONCERNS

The PR description sells a click affordance the badge doesn't have — reviewers and users will expect drill-in that doesn't exist.

Watch

  • Phantom claim: "After clicking the badge — Git panel opens with the full status." The badge is a passive span; the capture script opens the panel via the auto_open_git_panel opt-in and even comments "the badge is a passive readout, not a click target." Humans approving on the description believe the drill-in exists, and users habituated to VS Code's status bar will click a git counter and get nothing — every dirty session, mild friction, persistent. Smallest fix: reword the caption to "The Git panel (sidebar) showing the same counts" so the shipped behavior is what's documented.
  • A failed status fetch renders identical to "clean & in sync." projectGitStatusErrorgitBadge undefined → badge hidden, and the diff's own comment says the badge "asserts clean by ABSENCE" — so once users learn no-badge = clean, an endpoint error silently teaches a false clean at exactly the pre-rebase/push decision moment the badge exists for. Rare × moderate impact; consider a muted "—" or stale-marker state instead of vanishing on error.

Suggestions

  • Tooltip/aria-label 3 uncommitted · ↑1 · ↓2 restates the glyphs it should explain; screen readers may drop ↑/↓ entirely, announcing bare "1 · 2". Add gitPanel.ahead/gitPanel.behind keys ("1 ahead · 2 behind") for the tooltip layer.

[UX-REVIEWED] 56e14bc

@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

GPT 5.6 Review (fork) — ✅ no blocking findings

Reviewed 56e14bcf6fcc818a728c6b5993073d456024ae39 via the fork AI-review pipeline; updated in place on each push.

Review details

No findings.
[GPT-REVIEWED] 56e14bc

@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Opus 4.8 Review (fork) — ✅ no blocking findings

Reviewed 56e14bcf6fcc818a728c6b5993073d456024ae39 via the fork AI-review pipeline; updated in place on each push.

Review details

No findings.

[OPUS-REVIEWED] 56e14bc

@jingchaodev
jingchaodev force-pushed the feat/footer-git-status branch from 8996e67 to eade5bf Compare September 1, 2026 04:03
@jingchaodev

Copy link
Copy Markdown
Contributor Author

Disposition — UX Review CONCERNS on 8996e676f (both items FIXED on eade5bfd4):

  1. Stale absence reads as clean after a run — fixed: ChatPage now invalidates the shared ['git-status', dir] query key on the running→idle transition, so the badge refreshes the moment a turn that may have touched files ends. The 60s interval remains the background cadence only.
  2. Bare warn number fails the cold read — fixed: the count now carries a FileDiff lucide glyph inside the pill (screenshots in the body re-shot), so it reads as changed files without hovering.

@jingchaodev

Copy link
Copy Markdown
Contributor Author

CI triage note (head eade5bfd4): the two failing jobs reproduce on pristine upstream/main with this PR's diff adding zero to either surface, so both are base-branch breakage, not this change:

  • Backend Tests (3.10, 3): test_security_posture.py::test_the_census_holds_no_slack_BASELINE_LOG_SITE_CENSUS says 3 log sites for dashboard/handlers/files.py, the code now has 1. This diff touches no backend files.
  • Frontend Lint & Type Check: npx eslint src/ --max-warnings 659 counts 660 on current main; this diff's files add 0 warnings (verified 14→14 on the two touched files, 0 in the new test).

Will re-push once main is green so the round can complete.

@jingchaodev
jingchaodev force-pushed the feat/footer-git-status branch from eade5bf to 6be244c Compare September 1, 2026 22:22
@jingchaodev

Copy link
Copy Markdown
Contributor Author

CI triage update (head 6be244cd5): the one red check, Backend Tests (Windows) (3), is again base-branch drift, not this diff: #7424 (merged ~22:30 UTC) added a 7th gate-side log site to slack/gateway.py without raising _BASELINE_LOG_SITE_CENSUS (census says 6). Reproduces on pristine upstream/main; this PR touches no backend files. Will re-push once the census is fixed upstream.

@jingchaodev

Copy link
Copy Markdown
Contributor Author

CI triage update 2 (head 6be244cd5): gateway census is fixed upstream (thanks!). Remaining red is again base drift: pristine upstream/main with this PR's files swapped out counts 604 eslint warnings against the tightened 603 cap (npx eslint src/ --max-warnings 603 exits 1 with this diff contributing zero warnings). The Backend Tests (3.10, 3) red on the merge ref predates the census fix landing. Will re-push for a clean round once main is under its own cap.

@jingchaodev
jingchaodev force-pushed the feat/footer-git-status branch 3 times, most recently from f8dcb5d to 5fbee17 Compare September 2, 2026 03:24
@jingchaodev

Copy link
Copy Markdown
Contributor Author

Disposition — GPT + Opus BLOCK on f8dcb5d83 (fixed on 5fbee1786): the git badge button moved OUT of the project pill into its own group in the shelf (Opus's suggested fix). The pill is back to its base two buttons (picker, copy-branch); the badge is a separated sibling group, so max-two-buttons-per-row holds. Screenshots re-shot; tests 17/17, tsc/eslint/i18n clean.

@github-actions github-actions Bot added readiness: checking Automated validation is still running readiness: action required A blocking check or review needs attention and removed readiness: action required A blocking check or review needs attention readiness: checking Automated validation is still running labels Sep 2, 2026
@jingchaodev
jingchaodev force-pushed the feat/footer-git-status branch from 5fbee17 to 19a6c25 Compare September 2, 2026 04:16
@jingchaodev

Copy link
Copy Markdown
Contributor Author

Disposition — GPT BLOCK on 5fbee1786 (fixed on 19a6c2541): the badge is no longer gated on shelfCompact — it renders at narrow widths too (it is already icon+digits with no text label to shed, so nothing needed shrinking). New test pins it: a 320px shelf collapses the agent chip to icon-only while the badge stays present and clickable. 18/18 tests, tsc/eslint/i18n clean.

@jingchaodev
jingchaodev force-pushed the feat/footer-git-status branch from 19a6c25 to 56e14bc Compare September 2, 2026 05:53
@jingchaodev

Copy link
Copy Markdown
Contributor Author

Disposition — GPT BLOCK on 19a6c2541 (fixed on 56e14bcf6): the badge is now a passive readout (span[role=status]), not a button — the shelf row keeps its base count of three actions, so the max-two-buttons-per-row growth clause holds. The Git panel remains one click away in the sidebar; PR body, tests (17/17, including a no-button assertion), and screenshots updated to match.

@github-actions github-actions Bot added readiness: checking Automated validation is still running readiness: passed Eligible automated validation passed for the current revision and removed readiness: action required A blocking check or review needs attention readiness: checking Automated validation is still running labels Sep 2, 2026
@bolichen97
bolichen97 enabled auto-merge (squash) September 2, 2026 09:26

@bolichen97 bolichen97 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving on the strength of a full readiness audit of every open PR against main, not a
line-by-line reading of this diff — recording that plainly so the next reader knows what this
stamp does and does not cover.

Verified against this exact head SHA:

  • readiness: passed present, and PR Readiness — the one required status context on main
    (ruleset protected-branches) — is success on this head.
  • No check run on this head is failure, cancelled, timed_out or still in flight. Skipped
    jobs are path-filtered conditionals, none of them required.
  • mergeable: true, and the head is not far enough behind main for its green CI to describe a
    base that no longer exists.
  • No surviving reviewer CHANGES_REQUESTED: any such review is on an older commit and therefore
    already dismissed by dismiss_stale_reviews_on_push.
  • Every issue comment, inline review comment and review thread was read and classified. Nothing
    left is an unresolved human change request — the remainder is bot review-lane output, resolved
    or outdated threads, explicitly non-blocking suggestions, and author status notes.

Auto-merge (squash) is armed, so this lands once every other ruleset requirement is met.

@bolichen97
bolichen97 merged commit d50c181 into kirodotdev:main Sep 2, 2026
80 of 81 checks passed
@github-actions github-actions Bot removed the readiness: passed Eligible automated validation passed for the current revision label Sep 2, 2026
@bolichen97

Copy link
Copy Markdown
Collaborator

Open PR relationship audit

This is a consolidated, point-in-time code-level audit note. It compares complete merge-base diffs and current/merged code; it does not treat a shared topic as duplication or partial coverage as completion.

Relationship findings

  • PR #3987 is OVERLAPPING relative to this PR. The goals differ or the implementations can complement each other; this is not a duplicate claim. Recommended action for PR #3987: REBASE. Complementary, not competing: PR #7436 adds a consumer of the endpoint this PR reshapes. A rebase must re-check the footer badge against the grouped answer and decide whether 0/0 ahead/behind is the intended reading for a multi-repo project. Files: website/src/pages/ChatPage.tsx.

No PR, Issue, label, branch, or review state was changed by the relationship-note portion of this audit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

fork Pull request from a fork (external contributor)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants