feat(my-work): adds my work page, jira and GitHub combined#56
feat(my-work): adds my work page, jira and GitHub combined#56
Conversation
…-time stripping Adds features.json as the single source of truth for every base-app feature flag (38 flags, both base and experimental). A small Node generator emits src/shared/featureFlags.generated.ts which both the main and renderer processes import for the FeatureFlags type and BUILD_FLAGS defaults. The Vite config exposes the same data as a __FEATURES__ define literal so experimental gating becomes a static boolean at build time, letting Rollup tree-shake disabled experimental code paths out of the production bundle. Setting CLEARPATH_E2E_EXPERIMENTAL=1 at build time forces every experimental flag on (used by the experimental-feature e2e crawl). predev / prebuild / pretest hooks regenerate the TS module so it never drifts from features.json. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Removes the duplicated FeatureFlags interface and DEFAULTS constants that lived in featureFlagHandlers.ts and FeatureFlagContext.tsx — both now re-export the type from src/shared/featureFlags.generated.ts and seed defaults from BUILD_FLAGS. Settings UI greys out and disables toggles for experimental flags whose code is compiled out so users see why the toggle is inert. Experimental routes (PR Scores, Backstage Explorer) move to React.lazy behind __FEATURES__ checks. With the flag off, Rollup drops both the import call and the page chunk; with CLEARPATH_E2E_EXPERIMENTAL=1 the chunks return so e2e tests can cover them. featureFlagHandlers also clamps stored overrides for compiled-out experimental flags so a stale override from a prior build can't leave the UI claiming a missing feature is enabled. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…e subfolder Adds e2e/screenshot-crawl-experimental.spec.ts and a matching wdio config that builds the app with CLEARPATH_E2E_EXPERIMENTAL=1, navigates to each experimental-only route, and writes screenshots into the new e2e/screenshots/baseline/experimental-features/ subfolder so they don't pollute the default visual baseline. CI gains a screenshot-regression-experimental job that depends on the default screenshot job (sequential to avoid a force-push race) and runs the same compare+promote policy scoped to experimental-features/. Default wdio.conf.ts excludes the new spec so it only runs via the dedicated npm scripts: npm run e2e:screenshots:experimental npm run e2e:screenshots:experimental:update Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…n off When showPrScores / showBackstageExplorer are compiled out, the previous build made the routes conditional — so navigating to `#/pr-scores` (e.g. via the PR Scores extension's pinned sidebar link) had no matching child route and the parent Layout was unmounted, taking the sidebar with it. This blew up the screenshot crawl mid-run: every Sidebar Pages test passed, but every subsequent describe failed because the sidebar nav was gone for the rest of the session. Always register the experimental paths and let the route element decide between rendering the lazy page or redirecting to /work. The lazy import() is still gated by `__FEATURES__.X`, so disabled experimental chunks remain tree-shaken from the production bundle (verified: no score-pr / backstage-explorer:get-relationships / RelationshipViewer strings in out/renderer/assets/index-*.js). Also addresses first-round AI review comments: - App.tsx: drop dead reference to non-existent features.spec.ts. - features.json: remove `$schema` pointing at a file that doesn't exist yet (re-add when we author features.schema.json). - electron.vite.config.ts: clarify BUILD_FLAGS is a generation-time literal, not a `__FEATURES__` consumer. - screenshot-crawl-experimental.spec.ts: assert each route's marker text is present before the screenshot, so a misconfigured build (no CLEARPATH_E2E_EXPERIMENTAL=1) fails loudly instead of capturing a blank fallback.
wdio-visual-service's autoSaveBaseline copies the actual screenshot to the baseline path with `fs.copyFile`, which fails with ENOENT when the parent directory doesn't exist. The first experimental run had no baselines and the `experimental-features/` subfolder hadn't been created yet — every test failed before the first baseline could be written, leaving us stuck in a chicken-and-egg state. Create the directory at config-load time so the first run can write its initial baselines and let the existing CI promote-and-commit step take over from there.
The experimental build is invoked with CLEARPATH_E2E_EXPERIMENTAL=1, which makes the prebuild hook regenerate src/shared/featureFlags.generated.ts with experimental flags forced on. After the baseline commit step stages the new screenshots, that unrelated regen lingers as an unstaged change — and `git rebase` refuses to run on a dirty tree, so the auto-baseline push died with "cannot rebase: You have unstaged changes." Discard the unstaged delta + any untracked build artifacts after the commit but before the rebase loop. The regular screenshot-regression job doesn't hit this because its build runs without the env var, so the regenerated file matches what's committed.
- App.tsx: drop the `ComponentType` annotation. `React.lazy()` returns `LazyExoticComponent<...>`, which isn't structurally `ComponentType`. Letting TS infer keeps the type honest and unblocks future use of the lazy components' static `preload`-style helpers. - scripts/generate-feature-flags.mjs: rewrite the misleading comment. The generator does NOT emit `as const` — `BUILD_FLAGS` is a typed Readonly literal whose tree-shakeability comes entirely from the Vite `__FEATURES__` define, not from constant inference here. - electron.vite.config.ts: stop swallowing generator failures. Falling back to a stale `featureFlags.generated.ts` while `__FEATURES__` is computed fresh from features.json lets the two sources of truth drift, which surfaces as runtime UI inconsistency vs. the bundled experimental code paths. Always rethrow.
Previous attempt used \`git checkout -- .\` to clean the unstaged
\`featureFlags.generated.ts\` regen, but that re-runs the Git LFS
smudge filter against every tracked binary in the tree. The smudge
choked on 22 LFS-tracked SVG/icon files ("should have been pointers,
but weren't") and exited 1, killing the experimental baseline push.
\`git stash --include-untracked\` is the safer primitive — it captures
unstaged tracked changes + untracked files into a stash without
re-running smudge across the whole worktree. We never pop the stash,
so it dies with the runner.
The previous rebase-loop approach kept colliding with the LFS smudge filter and the experimental-build's regen of featureFlags.generated.ts. Since the experimental job runs with \`needs: screenshot-regression\`, the sibling job has already pushed any auto-baseline commit by the time our \`actions/checkout@v5\` runs — there's nothing to rebase onto. Drop the rebase loop and use the same simple \`git diff --staged --quiet || git commit\` + \`push --force-with-lease\` pattern the regular screenshot-regression job uses. \`--force-with-lease\` is happy to push from a dirty working tree as long as the remote ref matches what we fetched at checkout.
…w fixes - Remove `ext--pr-scores` from the regular screenshot crawl. The PR Scores extension pins a sidebar link to `#/pr-scores`, which is now build-time-gated; with the flag off it redirects to /work and the captured screenshot is just a duplicate of `work--initial`. When the flag is on, screenshot-crawl-experimental.spec.ts already captures the page under `experimental-features/`. - scripts/generate-feature-flags.mjs: rewrite the `isExperimentalFlagEnabledAtBuild` comment to admit that the computed key lookup is NOT statically replaceable, and tell readers to use direct property access on `__FEATURES__` for tree-shaking- sensitive gates. - src/shared/featureFlags.generated.test.ts: branch the disabled- experimental assertion on `process.env.CLEARPATH_E2E_EXPERIMENTAL`, since `pretest` regenerates the module with whatever env it sees. Running `CLEARPATH_E2E_EXPERIMENTAL=1 npm test` was previously a guaranteed failure; now the test mirrors the build behavior.
…verrides Two follow-ups from the latest AI review pass: - `FlagChangeEvent.key` was typed as `string`, even though the only emitter (`emitFlagChanges`) iterates `keyof FeatureFlags`. Narrow the type to `FeatureFlagKey` so subscribers get the same end-to-end type-safety the rest of the module exposes. - `feature-flags:set` previously sanitized compiled-out experimental overrides by writing an explicit `false` into the store. That persists a per-user override that would silently override the new default if the flag later becomes compiled-in / default-on. Delete the key from the sanitized args instead so no override is written for compiled-out flags — `clampToCompiledIn` already keeps the runtime view consistent regardless of any legacy stored value.
* feat(feature-flags): add locked preview mode + FEATURES.md
CLEARPATH_FLAGS_LOCKED=1 produces a build where the runtime treats
features.json as the absolute source of truth: stored overrides are
ignored, the feature-flags:set/apply-preset/reset IPC handlers become
no-ops, and the Settings → Feature Flags page hides off-by-default
flags entirely. On-by-default flags render read-only with a 🔒 banner
explaining the build was locked.
This gives us three orthogonal modes (composable):
- default dev with full UI overrides
- CLEARPATH_FLAGS_LOCKED=1 "what an end user with no
overrides sees, frozen to
features.json"
- CLEARPATH_E2E_EXPERIMENTAL=1 every experimental flag forced
on at generation time
(previously e2e-only)
Plumbed through:
- generator emits BUILD_FLAGS_LOCKED constant
- electron.vite.config.ts adds a __FEATURES_LOCKED__ define
- main IPC short-circuits writes when locked
- FeatureFlagContext skips IPC overrides + bypasses the progressive
preset when locked, exposes `locked: boolean` to consumers
- FeatureFlagSettings hides the locked-mode UI affordances + filters
to only flags whose effective value is true
New scripts:
- npm run dev:preview locked mode in dev
- npm run dev:experimental experimentals forced on in dev
- npm run build:locked / build:experimental
- npm run preview:locked / preview:experimental
FEATURES.md documents the data model, both pipelines, all env vars,
the dev/preview workflow matrix, how to verify tree-shaking with grep,
and how to add or remove a flag.
Stacks on top of #52 (feature/experimental-feature-flags) since the
features.json system that this builds on hasn't merged to main yet.
Verified against the build matrix:
- default build: no PrScores/Backstage chunks in out/
- locked build: BUILD_FLAGS_LOCKED = true literal in out/main/index.js
- experimental build: PrScores (111KB) + BackstageExplorer chunks
present, IPC channel strings appear in their respective chunks
* fix(extensions): hide nav links when their feature flag is off
Reported: clicking PR Scores in the sidebar lands on the Work tab.
Root cause: PR #52's `<Route path="pr-scores">` redirects to /work when
`__FEATURES__.showPrScores` is false (the redirect-arm exists so the
extension-pinned link to `#/pr-scores` doesn't unmount the Layout
mid-test). But the PR Scores extension's manifest never set
`featureGate` on its nav entry, so the sidebar always rendered the
link — clicking it advertised a feature that just bounced you away.
The Sidebar already filters extension nav items on `nav.featureGate`
(Sidebar.tsx:315). The fix is purely in the extension manifests:
- PR Scores: featureGate: ["showPrScores"]
- Backstage Explorer: featureGate: ["showBackstageExplorer"]
- Efficiency Coach: featureGate: ["showEfficiencyCoach"]
With this in place:
- npm run dev — links hidden, no broken nav.
- npm run dev:experimental — links shown, pages reachable.
- npm run dev:preview — links hidden (locked + flag off).
The redirect-to-/work in App.tsx stays as defense-in-depth for
direct-URL navigation (bookmarks, deep links, etc.).
FEATURES.md updated with a "if your flag gates an extension-contributed
page" note pointing at this manifest field.
* feat(feature-flags): gate MCP Servers tab behind showMcpServers (experimental, off)
Adds a new build-time-gated experimental flag for the MCP integration
surface (Connect → MCP Servers tab + the McpCatalogGrid / McpRegistryList /
sync hooks beneath it).
- features.json: showMcpServers, experimental: true, enabled: false, 1.9.0
- Connect.tsx: lazy(import('../components/mcp/McpTab')) gated on
__FEATURES__.showMcpServers so Rollup tree-shakes the MCP chunks
out of default builds. The sub-tab is filtered out of the tablist
via flags.showMcpServers; URL `?tab=mcp` falls back to integrations
when the flag is off (keeps the legacy /connections redirect safe).
- FeatureFlagSettings.tsx: surfaced under "Experimental Features".
Verified build matrix:
- npm run build → no McpTab-*.js chunk; renderer bundle
shrinks ~67 KB (4145 → 4078)
- npm run build:experimental → McpTab-*.js chunk emitted (lazy)
- 21/21 tests pass after a clean regen
* feat(feature-flags): gate Extensions tab behind showExtensions (experimental, off)
Adds the ExtensionManager UI to the same build-time-gating pattern as
showMcpServers. Already-installed extensions continue to load and
render via the /ext/:id/* route — only the install / enable / manage
surface (Connect → Extensions) is gated.
- features.json: showExtensions, experimental: true, enabled: false, 1.9.0
- Connect.tsx: lazy(import('../components/extensions/ExtensionManager'))
gated on __FEATURES__.showExtensions; tab filtered out of the tablist
via flags.showExtensions; URL `?tab=extensions` falls back to
integrations when gated off.
- FeatureFlagSettings.tsx: surfaced under "Experimental Features".
Verified build matrix:
- npm run build → no ExtensionManager-*.js (or McpTab-*.js)
chunk; renderer bundle 4055 KB (down
from 4078 with just MCP gated, 4145
pre-gating)
- npm run build:experimental → both ExtensionManager-*.js and
McpTab-*.js chunks emitted (lazy)
- 21/21 tests pass after a clean regen
* Update features.json
… crawl The renderer code reads `__FEATURES__` (a Vite `define` constant) at import time. Vitest uses its own vite config separate from electron.vite.config.ts, so any test that transitively imported Connect.tsx or App.tsx threw `__FEATURES__ is not defined` and exploded before the first assertion. Configure.test.tsx was the canonical victim. Mirror the same env-aware loadFeatures logic from electron.vite.config.ts in vitest.config.ts so renderer tests see identical flag substitution. Both `__FEATURES__` and `__FEATURES_LOCKED__` are now defined in tests. Verified: full suite (205 files / 3675 tests) passes. Also extends the experimental e2e crawl to cover the two new flag- gated Connect sub-tabs: - experimental-features/connect--tab-mcp (showMcpServers) - experimental-features/connect--tab-extensions (showExtensions) These weren't surfaced before because they live as ?tab= search params on /connect rather than full routes. The spec helper sets `window.location.hash = '#/connect?tab=mcp'`; HashRouter parses the search half via useSearchParams and Connect.tsx's URL-fallback effect honors the flag-gate. Marker text "Custom server" / "Permissions" is unique to the McpTab / ExtensionManager bodies — guards against silent fallback capture if the build is ever produced without CLEARPATH_E2E_EXPERIMENTAL=1. Note: PR #53's CI does not currently fire because it targets feature/experimental-feature-flags (the stack base) rather than main. That resolves automatically when #52 merges and #53 retargets.
Regular screenshot-regression failed because the crawl was still
clicking #connect-tab-extensions / #connect-tab-mcp, which are now
flag-gated and hidden in default builds. Drop them from CONNECT_TABS
(experimental crawl already covers them under
experimental-features/connect--tab-{mcp,extensions}). Also delete the
two stale baselines that were captured before the gating shipped.
Also addresses the four AI review comments from this round:
1. featureFlags.generated.ts had `BUILD_FLAGS.showCostTracking = true`
from a CLEARPATH_E2E_EXPERIMENTAL=1 regen leaking forward into the
committed file. Regenerated with a clean env.
2. FeatureFlagSettings.test.tsx's `ALL_ON: FeatureFlags = { ... }`
literal was missing showMcpServers / showExtensions and would have
silently broken on the next typed-fixture-using test. Refactor to
derive from FEATURE_FLAG_KEYS so future flag adds are absorbed
automatically.
3. Connect.tsx: when a user toggles showExtensions / showMcpServers
off at runtime while the corresponding tab is active (only
reachable when the chunk is compiled in but flipped off via UI),
`tab` state lingered and the page rendered blank. Added a guard
effect that bounces to integrations whenever the active tab
becomes invisible.
4. features.json: revert `showCostTracking.experimental` true → false.
The cost-tracking code is interleaved across many surfaces (turn
metadata, badges, analytics) — it's not a tree-shakeable chunk, so
`experimental: true` doesn't gain anything and only conflicts with
its placement in the non-experimental "Session Features" group +
progressive presets. Stays runtime-toggleable.
3675 tests pass.
The previous marker "Permissions" only appears on extension cards that have been expanded — and the screenshot test never expands them, so 3 passed but `captures experimental page: connect?tab=extensions` timed out waiting for text that the page never renders in its default state. Switch to "Install Extension" — the always-visible button at the top of the ExtensionManager body. The DOM dump confirms it renders immediately for both the empty and populated states.
…CKED__ Three of the four review comments from this round. 1. featureFlagHandlers.ts: when an experimental flag's code gets tree-shaken in this build, `feature-flags:set` already drops *new* writes for that key — but any value that was persisted by a previous build (where the flag was compiled in) keeps living in the store. clampToCompiledIn keeps it inert at runtime today, but if the flag becomes compiled-in again later the stale `true` would silently re-enable the feature even when the new build's default is off. Add a one-time `scrubStaleCompiledOutOverrides()` at handler-registration time that purges those keys from the persisted store. Idempotent. 2. electron.vite.config.ts: `__FEATURES_LOCKED__` define had no consumers — locked-mode logic uses `BUILD_FLAGS_LOCKED` from the generated module instead. Drop the dead define + its `flagsLocked` computation so future readers don't go hunting for a second source-of-truth. 3. vitest.config.ts: same dead define in test config; removed. Comment #4 (generator output is env-dependent → committed file mutates) is being deferred — the fix would require migrating every BUILD_FLAGS consumer to read the Vite `__FEATURES__` define instead, which is a larger refactor we'd rather scope as a follow-up. 3675 tests pass.
Introduce the Work launchpad and active-session features with tests and routing tweaks. Adds ActiveSessionsBanner (mounted in Layout) with collapse persistence, session chips, status dot styling, and navigation to /work?id=<sessionId>. Implements ActiveSessionsCard, QuickStartCard (with advanced options persisted to localStorage), RecentSessionsCard, and related unit tests. Adds useActiveSessions hook and Composer support for deep-linking a workflowId to preload steps. Updates Sidebar to clear the /work?id= query when re-clicking Work and updates Work page/e2e tests to cover the new launchpad behavior and banner.
Promote Notes to a first-class peer of Sessions in the sidebar and overhaul
the new-chat Advanced section. Skills picker is per-session only — fixes
the "everything selected, can't deselect" bug where it mutated the global
skill registry. Note bodies never re-render in chat; the user bubble shows
compact agent / skill / note chips frozen at attach time.
Notes (top-level)
- New /notes page with three-pane layout: filters · cards · editor drawer
- Sidebar: Home · Sessions · Notes · Learn · Insights, gated on showNotes
- Removed Notes sub-tab under Sessions; ?tab=notes legacy URLs redirect
- 5-lesson "Capture context with Notes" path in /learn ties to feature unlock
- showNotes auto-unlocks at the `exploring` progressive-disclosure stage
Sessions Advanced redesign
- Stacked sections with search: Agent (single-select), Skills (multi-select),
Notes (multi-select), then Permission mode + Additional directories
- Skills no longer call skills:toggle — per-session selection only
- Removed: Memories config-files picker, Templates dropdown, Attach files
(kept out of scope; will return as dedicated features)
In-chat audit-trail chips
- User bubble shows pill chips: Agent / N skills / N notes
- Names/titles only; note body never reaches the rendered DOM
- Frozen on message metadata so chips survive note deletion + flag toggling
AI context framing
- notes:get-bundle-for-prompt emits a clearly framed block with preamble:
<notes count="N">
<note title="..." category="..." tags="..." source="...">{body}</note>
</notes>
User request: ...
- Titles are the cite handle; UUIDs never leak
Configure → Project Memory rename
- Old "Memory & Context" page renamed; Notes tab dropped (moved to /notes)
- "Starter Memories" tab renamed to "Templates"
Test infrastructure
- jsdom: configured http://localhost/ URL via environmentOptions
- Polyfilled window.localStorage / sessionStorage in setup-coverage.ts so
jsdom's opaque-origin default doesn't break tests
- handlers.test cli:send-input updated for the new attachedNotes arg
- QuickStartCard.test rewritten for the button-based agent picker;
dropped tests for removed features (templates/files/memories/skills:toggle);
added coverage for the new skill + note local multi-select
Sessions launchpad polish (in-flight branch work)
- QuickStartCard: Provider/Transport selectors + advanced state in localStorage
- ActiveSessionsBanner: AA-contrast CLI badges, status labels
- AuthManager + CopilotAdapter touch-ups, expanded modelTiers
- Composer / SessionSettingsModal / NotificationInbox refinements
Version bump: 1.9.0 → 1.13.0
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
E2E screenshot crawl - Sidebar nav helper still searched for the literal "Work" label after the Sessions rename, so the screenshot-crawl spec timed out before capturing the page. Updated all e2e specs (navigation, smoke, work-page, app-lifecycle, screenshot-crawl) to use 'Sessions'. Route is still /work, so screenshot baseline filenames stay `work--*` for backward compat. - Added Notes as a top-level sidebar entry in SIDEBAR_PAGES (optional — gated on showNotes; the spec skips when not rendered). - Trimmed WORK_TABS: dropped `wizard` and `memory` (notes moved to /notes, wizard surface was retired); only `session` / `compose` / `schedule` remain. Notes page flicker - Replaced `loading: boolean` with `initialized: boolean`. The "Loading notes…" message now only renders before the FIRST notes:list resolves. Subsequent refreshes (triggered by the editor drawer's debounced save via `onChange()`) no longer toggle a global flag, so the placeholder stops flashing over the user's typing. - handleNew now optimistically inserts the created note into local state and only does a background refresh, instead of blocking on refresh() before the editor drawer opens. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The chip metadata (attachedAgent, attachedSkills, attachedNotes) was already being persisted on the user message in clear-path-sessions.json, but the renderer's rehydration paths in Work.tsx dropped these fields when mapping the persisted log back into OutputMessage. As a result, leaving a session and coming back showed a bare bubble — the agent / skill / note pills the user had selected at submit time disappeared. Both rehydration paths now pull the fields through: - Active sessions: cli:get-message-log return type extended; mapper copies attachedAgent / attachedSkills / attachedNotes onto each OutputMessage. - Persisted sessions (cli:get-persisted-sessions): same. The chips on the live bubble still come straight from React state at send time, so the only behavior that changes is the recovered view — it now matches what the user saw when they sent the message. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Implement bulk archive/unarchive support for persisted sessions and wire it through IPC, preload and the UI. CLIManager.archivePersistedSessions was added to toggle archived flags for multiple session IDs; ipc handlers and preload allowed channels were updated to expose 'cli:archive-sessions'. The SessionManager UI now surfaces a bulk Archive/Unarchive action and clears selection after the action, and corresponding unit tests were added for the renderer, CLI manager, and IPC handlers. An end-to-end spec (e2e/session-manager.spec.ts) was added to cover bulk operations, and developer docs (.claude/agents/core-developer.md) were expanded to mandate running tests and a Definition of Done. Also toggled BUILD_FLAGS_LOCKED to true in featureFlags.generated.ts.
The bulk-archive commit baked BUILD_FLAGS_LOCKED=true into the committed generated file because npm run build was invoked from a shell that had CLEARPATH_FLAGS_LOCKED=1 exported. Regenerated the snapshot with the env var cleared so normal dev/prod builds keep flag toggles interactive, as the docstring promises. The generator script itself was already correct. Addresses Copilot review feedback on PR #55. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add end-to-end coverage for the new My Work aggregation page: e2e/my-work.spec.ts and a baseline screenshot (my-work--initial.png). Wire the page into the app (src/renderer/src/pages/MyWork.tsx and test) and update related renderer components (App, Sidebar, FeatureFlagSettings) and feature flags (features.json, featureFlags.generated.ts). Adjust integration/ipc/preload bits (atlassian.ts, integrationHandlers.ts, preload/index.ts) as needed. Update the screenshot crawl to include My Work as an optional page and refresh many screenshot baselines (LFS OIDs updated) to reflect UI changes.
There was a problem hiding this comment.
Pull request overview
Adds a new My Work surface (flag-gated, on by default) that aggregates a user’s Jira + GitHub workload, alongside new IPC aggregation endpoints and expanded test coverage (unit + WDIO E2E), with updated visual regression baselines and strengthened contributor validation guidance.
Changes:
- Introduces
/my-workpage + sidebar entry, behindshowMyWork. - Adds aggregated integration IPC handlers (
integration:jira-my-work,integration:github-my-work) and whitelists them in preload. - Adds bulk archive/unarchive sessions IPC + UI, new unit/E2E coverage, and updates screenshot baselines/docs.
Reviewed changes
Copilot reviewed 78 out of 78 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| src/shared/featureFlags.generated.ts | Adds showMyWork key/type/meta/defaults to generated flags. |
| src/renderer/src/pages/MyWork.tsx | Implements the My Work page UI, loading/empty states, and IPC fan-out. |
| src/renderer/src/pages/MyWork.test.tsx | Unit tests for My Work rendering, disconnected/empty/error states, and refresh. |
| src/renderer/src/components/settings/FeatureFlagSettings.tsx | Exposes showMyWork in feature flag settings UI. |
| src/renderer/src/components/Sidebar.tsx | Adds sidebar nav item for /my-work behind showMyWork. |
| src/renderer/src/components/SessionManager.tsx | Adds bulk archive/unarchive action invoking new IPC. |
| src/renderer/src/components/SessionManager.test.tsx | Unit tests for bulk archive/unarchive UI + IPC invocation behavior. |
| src/renderer/src/App.tsx | Registers the /my-work route. |
| src/preload/index.ts | Whitelists new IPC channels (cli:archive-sessions, integration:*my-work). |
| src/main/ipc/integrationHandlers.ts | Adds integration:github-my-work aggregated GitHub search handler. |
| src/main/ipc/handlers.ts | Adds cli:archive-sessions IPC handler wiring. |
| src/main/ipc/handlers.test.ts | Extends IPC handler tests to include cli:archive-sessions. |
| src/main/integrations/atlassian.ts | Adds Jira /search/jql helper, updates jira-search, and adds integration:jira-my-work. |
| src/main/cli/CLIManager.ts | Adds archivePersistedSessions bulk archive implementation. |
| src/main/cli/CLIManager.test.ts | Tests for bulk archive/unarchive behavior in the session store. |
| features.json | Declares the showMyWork feature flag metadata. |
| e2e/session-manager.spec.ts | WDIO E2E coverage for bulk archive/unarchive in SessionManager modal. |
| e2e/screenshot-crawl.spec.ts | Adds My Work to screenshot crawl (optional, disconnected baseline). |
| e2e/my-work.spec.ts | WDIO E2E coverage for My Work sidebar navigation + disconnected CTAs. |
| .claude/agents/core-developer.md | Strengthens validation workflow + definition-of-done checklist. |
| e2e/screenshots/baseline/work--tab-session.png | Updates visual baseline. |
| e2e/screenshots/baseline/work--tab-schedule.png | Updates visual baseline. |
| e2e/screenshots/baseline/work--tab-compose.png | Updates visual baseline. |
| e2e/screenshots/baseline/work--initial.png | Updates visual baseline. |
| e2e/screenshots/baseline/notes--initial.png | Updates visual baseline. |
| e2e/screenshots/baseline/my-work--initial.png | Adds new visual baseline for My Work. |
| e2e/screenshots/baseline/learn--initial.png | Updates visual baseline. |
| e2e/screenshots/baseline/insights--tab-pr-health.png | Updates visual baseline. |
| e2e/screenshots/baseline/insights--tab-efficiency.png | Updates visual baseline. |
| e2e/screenshots/baseline/insights--tab-compliance.png | Updates visual baseline. |
| e2e/screenshots/baseline/insights--tab-catalog-insights.png | Updates visual baseline. |
| e2e/screenshots/baseline/insights--tab-activity.png | Updates visual baseline. |
| e2e/screenshots/baseline/insights--initial.png | Updates visual baseline. |
| e2e/screenshots/baseline/home--initial.png | Updates visual baseline. |
| e2e/screenshots/baseline/connect--tab-webhooks.png | Updates visual baseline. |
| e2e/screenshots/baseline/connect--tab-plugins.png | Updates visual baseline. |
| e2e/screenshots/baseline/connect--tab-integrations.png | Updates visual baseline. |
| e2e/screenshots/baseline/connect--tab-environment.png | Updates visual baseline. |
| e2e/screenshots/baseline/connect--initial.png | Updates visual baseline. |
| e2e/screenshots/baseline/configure--tab-workspaces.png | Updates visual baseline. |
| e2e/screenshots/baseline/configure--tab-workspaces--sub-settings.png | Updates visual baseline. |
| e2e/screenshots/baseline/configure--tab-workspaces--sub-repos.png | Updates visual baseline. |
| e2e/screenshots/baseline/configure--tab-workspaces--sub-broadcast.png | Updates visual baseline. |
| e2e/screenshots/baseline/configure--tab-workspaces--sub-activity.png | Updates visual baseline. |
| e2e/screenshots/baseline/configure--tab-wizard.png | Updates visual baseline. |
| e2e/screenshots/baseline/configure--tab-tools.png | Updates visual baseline. |
| e2e/screenshots/baseline/configure--tab-team.png | Updates visual baseline. |
| e2e/screenshots/baseline/configure--tab-team--sub-wizard.png | Updates visual baseline. |
| e2e/screenshots/baseline/configure--tab-team--sub-sync.png | Updates visual baseline. |
| e2e/screenshots/baseline/configure--tab-team--sub-marketplace.png | Updates visual baseline. |
| e2e/screenshots/baseline/configure--tab-team--sub-activity.png | Updates visual baseline. |
| e2e/screenshots/baseline/configure--tab-skills.png | Updates visual baseline. |
| e2e/screenshots/baseline/configure--tab-setup.png | Updates visual baseline. |
| e2e/screenshots/baseline/configure--tab-settings.png | Updates visual baseline. |
| e2e/screenshots/baseline/configure--tab-settings--sub-profiles.png | Updates visual baseline. |
| e2e/screenshots/baseline/configure--tab-settings--sub-notifications.png | Updates visual baseline. |
| e2e/screenshots/baseline/configure--tab-settings--sub-model.png | Updates visual baseline. |
| e2e/screenshots/baseline/configure--tab-settings--sub-limits.png | Updates visual baseline. |
| e2e/screenshots/baseline/configure--tab-settings--sub-features.png | Updates visual baseline. |
| e2e/screenshots/baseline/configure--tab-settings--sub-data.png | Updates visual baseline. |
| e2e/screenshots/baseline/configure--tab-scheduler.png | Updates visual baseline. |
| e2e/screenshots/baseline/configure--tab-policies.png | Updates visual baseline. |
| e2e/screenshots/baseline/configure--tab-policies--sub-violations.png | Updates visual baseline. |
| e2e/screenshots/baseline/configure--tab-policies--sub-editor.png | Updates visual baseline. |
| e2e/screenshots/baseline/configure--tab-memory.png | Updates visual baseline. |
| e2e/screenshots/baseline/configure--tab-memory--sub-instructions.png | Updates visual baseline. |
| e2e/screenshots/baseline/configure--tab-memory--sub-context.png | Updates visual baseline. |
| e2e/screenshots/baseline/configure--tab-memory--sub-config-files.png | Updates visual baseline. |
| e2e/screenshots/baseline/configure--tab-memory--sub-cli-memory.png | Updates visual baseline. |
| e2e/screenshots/baseline/configure--tab-branding.png | Updates visual baseline. |
| e2e/screenshots/baseline/configure--tab-branding--sub-ui-colors.png | Updates visual baseline. |
| e2e/screenshots/baseline/configure--tab-branding--sub-surfaces.png | Updates visual baseline. |
| e2e/screenshots/baseline/configure--tab-branding--sub-preview.png | Updates visual baseline. |
| e2e/screenshots/baseline/configure--tab-branding--sub-identity.png | Updates visual baseline. |
| e2e/screenshots/baseline/configure--tab-branding--sub-colors.png | Updates visual baseline. |
| e2e/screenshots/baseline/configure--tab-agents.png | Updates visual baseline. |
| e2e/screenshots/baseline/configure--tab-accessibility.png | Updates visual baseline. |
| e2e/screenshots/baseline/configure--initial.png | Updates visual baseline. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const { response, error } = await authenticatedFetch(url) | ||
| if (error) return { issues: [], error } | ||
| if (!response || !response.ok) { | ||
| const status = response?.status ?? 0 | ||
| let body = '' | ||
| try { body = (await response?.text()) ?? '' } catch { /* ignore */ } | ||
| return { issues: [], error: `search/jql HTTP ${status}: ${body.slice(0, 200)}` } | ||
| } | ||
|
|
||
| const data = (await response.json()) as { | ||
| issues?: Array<{ |
There was a problem hiding this comment.
searchJiraIssuesViaJql can throw (e.g., network failure from authenticatedFetch, invalid JSON from response.json()), which will bubble up and reject IPC handlers like integration:jira-search/integration:jira-my-work. Please wrap the fetch + JSON parsing in a try/catch and return { issues: [], error: ... } on exceptions so renderer callers always get a structured error response.
| ipcMain.handle( | ||
| 'integration:jira-search', | ||
| async ( | ||
| _e, | ||
| args: { jql: string; startAt?: number; maxResults?: number; fields?: string[] }, | ||
| ) => { |
There was a problem hiding this comment.
integration:jira-search still accepts startAt in its args type, but it is no longer used (the new /search/jql call only uses maxResults). This is misleading for callers and suggests pagination still works. Consider removing startAt from the handler signature (and any callsites/types) or explicitly documenting/validating that it is ignored.
| const boardsUrl = `${meta.siteUrl}/rest/agile/1.0/board?maxResults=50` | ||
| const { response: boardsResp } = await authenticatedFetch(boardsUrl) | ||
| if (!boardsResp || !boardsResp.ok) { | ||
| // Agile API may be unavailable (Jira Software disabled) — non-fatal | ||
| result.sprintError = boardsResp ? `Boards API HTTP ${boardsResp.status}` : 'Boards API unreachable' | ||
| log.warn('[atlassian] jira-my-work: %s', result.sprintError) | ||
| } else { | ||
| const boardsData = (await boardsResp.json()) as { | ||
| values?: Array<{ id?: number; name?: string; type?: string }> | ||
| } | ||
| const candidateBoards = (boardsData.values ?? []).filter((b) => typeof b.id === 'number') | ||
|
|
||
| for (const board of candidateBoards) { | ||
| if (!board.id) continue | ||
| const sprintUrl = `${meta.siteUrl}/rest/agile/1.0/board/${board.id}/sprint?state=active` | ||
| const { response: sprintResp } = await authenticatedFetch(sprintUrl) | ||
| // Boards that don't support sprints (kanban without backlog) return | ||
| // 400; we just skip them and keep scanning. | ||
| if (!sprintResp || !sprintResp.ok) continue |
There was a problem hiding this comment.
The Jira sprint scan does up to 1 + N sequential Agile API requests (boards list, then one sprint request per board until a hit). With maxResults=50, worst-case is 50+ HTTP calls per refresh, which can be slow and may trip Atlassian rate limits. Consider bounding the scan (e.g., first N boards), caching the last successful boardId, and/or parallelizing a limited number of sprint checks with a small concurrency cap.
| // Pull this sprint's issues (capped to 50; the sprint shouldn't be huge) | ||
| const issuesUrl = `${meta.siteUrl}/rest/agile/1.0/sprint/${active.id}/issue?maxResults=50` | ||
| const { response: issuesResp } = await authenticatedFetch(issuesUrl) | ||
| if (!issuesResp || !issuesResp.ok) break | ||
|
|
There was a problem hiding this comment.
If fetching sprint issues fails (issuesResp missing or non-OK), the handler breaks out without setting sprintError, but it may already have set activeSprint. This can make the UI show an apparently valid sprint with an empty issues list ("No issues…") instead of an error. Please set result.sprintError (e.g., HTTP status / unreachable) before breaking so the renderer can render an accurate failure state.
| <div className="grid grid-cols-1 lg:grid-cols-2 gap-5"> | ||
| {/* ── Sprint card (Jira) ───────────────────────────────────────── */} | ||
| {jiraConnected ? ( | ||
| <SprintCard | ||
| sprint={jira?.activeSprint ?? null} | ||
| issues={jira?.sprintIssues ?? []} | ||
| siteUrl={status?.atlassian?.siteUrl ?? ''} | ||
| error={jira?.sprintError ?? null} | ||
| /> | ||
| ) : ( | ||
| <DisconnectedCard service="jira" onConnect={() => navigate('/connect?tab=integrations')} /> | ||
| )} |
There was a problem hiding this comment.
During the initial load, status is set before the Jira/GitHub data resolves, so jiraConnected/githubConnected can flip to true while jira/github are still null. In that window the cards render “No active sprint.” / other empty states even though data is still loading (and the separate loading shell is also shown). Consider gating the card bodies on loading (or on jira/github being non-null) and rendering per-card skeletons/spinners to avoid misleading empty-state flicker.
Initial my work page.
This pull request introduces comprehensive end-to-end (E2E) test coverage for the new "My Work" page, updates screenshot baselines, and significantly strengthens the documented validation and "definition of done" process for development tasks. The changes ensure that new features are properly tested and that contributors explicitly verify passing tests and correct behavior before marking tasks as complete.
Key changes:
E2E Test Coverage
e2e/my-work.spec.tsto cover the "My Work" aggregation page, verifying sidebar navigation, disconnected state handling, and CTA routing for integrations.e2e/screenshot-crawl.spec.tsto include the "My Work" page in the screenshot crawl, capturing its initial (disconnected) state for regression testing.Screenshot Baselines
e2e/screenshots/baseline/to reflect recent UI changes, ensuring visual regression tests remain accurate. [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17]Developer Workflow & Validation
.claude/agents/core-developer.mdto require that all new or modified tests are run and pass before reporting a task as complete, with explicit instructions for unit, E2E, typecheck, and build validation.These changes together improve the reliability of new features, enforce best practices for test-driven development, and help prevent regressions in both functionality and UI.