Swap raw <input type="checkbox"> for the shadcn Checkbox component so
the "only between certain hours" and "weekdays only" toggles get the
same focus ring, shadow, and dark-mode behaviour as every other
checkbox in the app.
Replace the always-visible "Add trigger" inline form + inline per-trigger
editor with a clearer pattern: a header + "Add trigger" button, a list of
compact TriggerListCard entries, and an add/edit dialog. Enable/disable
becomes a switch on each card (single-click toggle, no save step).
ScheduleEditor gains first-class support for the shapes users actually
want: every-N-minutes (with optional hour window and weekdays-only),
every-N-hours, hourly at a minute offset, daily with one or more times
per day, selected-days-of-week with one or more times, and monthly with
one or more dates. The legacy parseCronToPreset/describeSchedule exports
remain backwards-compatible; describeSchedule now unfolds multi-value
hour and day lists into readable sentences instead of returning the raw
cron. Falls back to the raw cron for named-token forms it can't describe.
The CRUD wiring reuses the existing create/update/delete/rotate-secret
mutations, query invalidations, and the secret-material banner. Delete
now goes through a ConfirmDialog instead of firing on trash click.
Files:
- ui/src/components/ScheduleEditor.tsx — richer internal preset model
- ui/src/components/ScheduleEditor.test.ts — cover the new describers
- ui/src/components/TriggerListCard.tsx — new card view per trigger
- ui/src/components/TriggerDialog.tsx — add/edit modal wrapping the editor
- ui/src/components/ConfirmDialog.tsx — small confirm-dialog primitive
- ui/src/pages/RoutineDetail.tsx — swap the triggers tab to the new flow
The ScheduleEditor's cron parser previously matched `0 9,13,17 * * *` as the
`every_day` preset with hour `"9,13,17"`. The hour <Select> couldn't render
that value, so it visually fell back to a default — and saving the form then
called buildCron("every_day", "10", ...) and overwrote the trigger with
`0 10 * * *`, silently collapsing three daily fires into one.
parseCronToPreset now checks every field (minute, hour, day-of-month, month,
day-of-week) for comma lists, ranges, steps, and unknown tokens up front.
If any field is complex — except the specific `dow === "1-5"` that already
maps to `weekdays` — the parser returns `custom` so the raw cron string
shows in the custom input and can be edited directly without a lossy
round-trip. Also routes non-wildcard month fields (which no preset encodes)
to custom.
Adds ScheduleEditor.test.ts covering the regression cases that exposed the
bug (`0 9,13,17 * * *`, `0 */4 * * *`, `0 10,16 * * *`) plus the existing
preset patterns to guard against regressions in the other direction.
## Thinking Path
> - Paperclip orchestrates AI agents for zero-human companies.
> - Board users and agents collaborate on issue-scoped documents such as
plans and revisions need to be trustworthy because they are the audit
trail for those artifacts.
> - The issue document UI now supports revision history and restore, so
the UI has to distinguish the current revision from historical revisions
correctly even while multiple queries are refreshing.
> - In `PAPA-72`, the newest content could appear under an older
revision label because the current document snapshot and the
revision-history query could temporarily disagree after an edit.
> - That made the UI treat the newest revision like a historical restore
target, which is the opposite of the intended behavior.
> - This pull request derives one authoritative revision view from both
sources, sorts revisions newest-first, and keeps the freshest revision
marked current.
> - The benefit is that revision history stays stable and trustworthy
immediately after edits instead of briefly presenting the newest content
as an older revision.
## What Changed
- Added a `document-revisions` helper that merges the current document
snapshot with fetched revision history into one normalized revision
state.
- Updated `IssueDocumentsSection` to render from that normalized state
instead of trusting either query in isolation.
- Added focused tests covering the current-revision selection and
ordering behavior.
## Verification
- `pnpm -r typecheck`
- `pnpm build`
- Targeted revision tests passed locally.
- Manual reviewer check:
- Open an issue document with revision history.
- Edit and save the document.
- Immediately open the revision selector.
- Confirm the newest revision remains marked current and older revisions
remain the restore targets.
## Risks
- Low risk. The change is isolated to issue document revision
presentation in the UI.
- Main risk is merging the current snapshot with fetched history
incorrectly for edge cases, which is why the helper has focused unit
coverage.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [ ] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
Agent configuration edits already had an API path for replacing the full adapterConfig, but the edit form was still sending merge-style patches. That meant clearing the last environment variable serialized as undefined, the key disappeared from JSON, and the server merged the old env bindings back into the saved config.
Build adapter config save payloads as full replacement patches, strip undefined keys before send, and reuse the existing replaceAdapterConfig contract so explicit clears persist correctly. Add regression coverage for the cleared-env case and for adapter-type changes that still need to preserve adapter-agnostic fields.
Fixes#3179
Shows a beautiful categorized cheatsheet of all keyboard shortcuts
(inbox, issue detail, global) when the user presses ? with keyboard
shortcuts enabled. Respects text input focus detection — won't trigger
in text fields. Uses the existing Dialog component and Radix UI.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Use createHistoricalTranscriptMessage for failed/cancelled/timed_out
runs even before transcript data loads. This prevents the flash where
a plain "run X failed" status line transforms into a foldable "failed
after X minutes" header when transcripts arrive asynchronously.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Move child indentation from wrapper paddingLeft into desktopMetaLeading
so the unread dot stays in the leftmost column regardless of nesting
depth. When nesting is enabled, all issue rows get a fixed-width folding
column (chevron or empty spacer) for consistent alignment. Children
indent after the folding column. When nesting is disabled, no folding
column is rendered.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Builds a flat navigation list that includes expanded child issues alongside
top-level items, so j/k moves through every visible row including children.
Also adds the NavEntry type and updates archive/read/enter actions to work
with both top-level work items and nested child issues.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Adds a ListTree icon button in the inbox top bar to toggle nesting
on/off. Preference is persisted in localStorage. When disabled, all
issues display as a flat list without grouping.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Groups child issues under their parent in the inbox, matching the
nesting pattern used on the issues list page. Parent groups sort by
the most recent activity across all family members. Archived parents
don't hide their children — orphans show independently.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Eliminates two visual glitches when a new agent run starts:
1. The initial "Working..." was rendered as plain text without the shimmer
animation or agent icon — now matches the proper working state styling.
2. A brief blank flash occurred when transcript chunks arrived but hadn't
produced parseable parts yet — fixed by deriving waitingText from parts
availability instead of the hasOutput flag.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Replace useEffect with synchronous state derivation during render so
the browser never paints the unfolded intermediate state. This prevents
the visible "jump" when loading an issue page with already-completed
work sections like "worked for 4 minutes".
Co-Authored-By: Paperclip <noreply@paperclip.ing>
The sweep now completes at 60% of the cycle and holds for the
remaining 40%, giving a ~1s pause before repeating. Total cycle
duration increased from 1.5s to 2.5s.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
The shimmer keyframes animated from -100% to 200%, pushing the gradient
fully off-screen at both extremes. On loop restart the text flashed
invisible for a frame. Changed to 100%→0% so the gradient always
overlaps the visible area — both endpoints show solid base-color text
and the highlight sweeps smoothly through without discontinuity.
Co-Authored-By: Paperclip <noreply@paperclip.ing>