* docs(adr): propose agent AI SDK v7 adoption plan
* fix(ai): bind analyzeResume through aiService in service test
The test destructured analyzeResume as a named export that does not exist; main was red.
* test(agent): keep pure ai helpers real via spread-actual mock factory
* feat(agent): add run guards, patch version guard, run wall-clock timeout
* feat(agent): validate UI messages at the send boundary
* feat(agent): crash-safe draft-row persistence and server-side cancellation
* feat(agent): reap stale run claims at boot, on send, and on thread open
* feat(agent): fresh-document patch output and tiered context pruning
* feat(ai): shared agent tool contracts and message metadata schema
* feat(agent): add per-thread review-patches setting with update endpoint
* feat(agent): gate resume patches behind hmac-signed tool approval
* feat(agent): merge question answers and approval decisions before run claim
* feat(agent): approval ui with composed auto-send and fixture-driven tests
* feat(agent): usage metadata, tool activity cards, smoother streaming
* feat(agent): tool-call repair, input examples, structured step logging
* chore(i18n): translate new agent workspace strings across all locales
* fix(agent): gate stale-run draft cancellation on winning the claim clear
Snapshot streaming drafts before the conditional clear and skip the flip entirely when another reaper or a replacement run already cleared the claim. Also address review nits in eleven locale catalogs.
* fix(agent): flip reaped drafts only when their snapshotted state is unchanged
* fix(agent): address review findings across run lifecycle, context budget, and approval flow
- bind patches to the revision the model read via signed baseUpdatedAt
- claim the run before consuming a continuation; recorded-but-unexecuted approvals retry as pending continuations
- keep run ownership on stop() until cancellation persists; preserve the claim for the reaper when final persistence fails
- estimate tokens without serializing binary attachments (tokenx) and enforce the budget by dropping oldest whole turns
- mark crash-recovered patch results as snapshot boundaries; strip /data prefixes at execution time
- retry failed continuations without regenerate; mount a single AgentChat; disable response controls on read-only threads; freeze review toggle during runs (client+server)
- accumulate usage across continuations and match the SDK's nested usage shape; label-form token strings; reorderable source label; accessible note field; state-neutral web-search label
* chore(i18n): translate revised agent strings across all locales
* fix(agent): harden baseUpdatedAt validation and address review follow-ups
- bundle tokenx in the server runtime dependencies (e2e boot failure)
- strict ISO schema for baseUpdatedAt plus loud executor rejection of unparseable values
- it-IT source label consistency (Fonte)
- prove penultimate-turn retention in the context pruning test
* chore(deps): exempt tokenx from knip for the externalized server bundle
* docs: remove .superpowers
* feat(dsh-plugin): bring the DeepSeek Harness plugin into the monorepo
Moves dsh-plugin-reactive-resume out of its own repository and into
packages/dsh-plugin. It stays a published, public npm package — the only
one here — but now builds, typechecks, tests, and lints under the same
turbo tasks as everything else.
The move pays for itself in the drift guard. Standalone, the plugin kept a
generated snapshot of the tool names scraped from the live server card at
https://rxresu.me, plus a weekly CI job to notice when that snapshot went
stale. Sitting next to packages/mcp, it reads MCP_TOOL_NAME directly, so a
tool rename breaks the prompt guide on the same pull request instead of
days later. The snapshot, the fetch script, and the scheduled job are gone.
packages/mcp gains a ./tool-names export so that import goes through the
public export map rather than another workspace's src.
Also flips autoInstallPeers off. The DeepSeek Harness rc packages declare
peers that are host-supplied and, in one case
(@deepseek-ai/dsh-type-meta), not published at all, so auto-install 404s
the whole workspace. Turning it off drops only optional peers elsewhere;
@neodrag/core was the single hard peer that had been arriving implicitly,
and it is now declared where it is used. Full typecheck and test suites
pass, and pnpm peers check reports nothing new beyond the pre-existing
drizzle-orm range mismatch.
Tests move from test/ to colocated src/*.test.ts and the build output from
lib/ to dist/ to match repository conventions.
* fix(dsh-plugin): ship a bundle manifest and target the current Harness
`dsh plugin add` warned that the package "declares no dsh.bundle — installed
as a plain dependency, not a profile layer", and it was right. Every other
Harness plugin, in-box and third-party, ships a cordis.patch.yml and points
dsh.bundle.patch at it; that declaration is what joins a package to a
profile's bundle stack. Without it the package installed and then sat inert,
and the README's hand-written insert row was a workaround for the gap rather
than the intended way in.
The peer ranges were also a generation behind. They asked for
@deepseek-ai/dsh-mcp-client and dsh-system-prompt at ^0.0.1-rc.1, which
cannot match the 0.1.0-rc.6 a current harness ships, so the plugin could
never have resolved against the thing it targets. Both APIs are unchanged
across the bump — StreamableHttpConfig still takes the same six fields and
PromptSection still takes name/order/text — so this is a range correction,
not a migration.
That bump pays for itself elsewhere. The old generation peer-depended on
@deepseek-ai/dsh-type-meta, which was never published, and working around
that 404 is why merging this package turned autoInstallPeers off for the
whole repository and pulled @neodrag/core in by hand. The new generation
dropped that peer and publishes every other one, so both changes are
reverted and pnpm-workspace.yaml is back to what it was.
Because a bundle patch mounts the plugin the moment it is installed, a
required apiKey would fail config validation and take the profile down
before the user ever had a chance to mint a key. It now defaults to empty
and apply() warns and mounts nothing, matching how dsh-honcho-memory
handles the same problem.
Verified by packing the tarball and installing it into a clean project with
default pnpm settings: it resolves, imports, and reports its exports.
The sonner wrapper was the only consumer of next-themes; the Base UI toast
that replaced it does not use the hook. knip flagged it as unused, and CI's
`knip --fix` step removed it and then failed `pnpm check` against a lockfile
that still listed it.
The dependency-update commit bumped @react-pdf/renderer 4.5.1->4.6.0 (textkit
6.3.0->6.4.0) and silently dropped the pnpm patch that overrides font vertical
metrics to prefer OS/2 sTypo* over inflated hhea values, matching browser line
boxes. 6.4.0 did not upstream it, so the semantic-CSS template visual snapshots
(baselined with the patch, maxDiffPixelRatio 0) no longer matched and the E2E
job failed. Re-create the patch for textkit@6.4.0 and re-register it in
patchedDependencies; remove the orphaned 6.3.0 patch.
Claude-Session: https://claude.ai/code/session_01ULhhLQ24DvnYwzP4afDuye
Add comprehensive Application Tracker REST and MCP coverage, document the MCP workflow, add Markdown/ActionLint checks, bump the release version, and fill all extracted translations.
* feat(applications): job application tracker with AI copilot
Add an Applications module at /dashboard/applications: pipeline board
(dnd-kit), table view with bulk actions, Insights (fit tiles, funnel,
sources, shareable funnel-flow SVG), campaigns, tags, CSV import, and
Add/Edit/Detail slide-overs. Each application links a live Reactive
Resume.
AI "Application Copilot" (applications.ai.*): job-posting autofill,
resume↔job match score (fit ring), resume tailoring, and cover-letter /
follow-up drafting — via the user's configured provider.
Board cards + table rows get context menus (edit / move / archive /
delete). Charts are CSS/SVG (no new chart dep); adds a UI Checkbox.
Also includes local TanStack devtools setup and toolchain bumps.
Claude-Session: https://claude.ai/code/session_01TEeRHnEayw2MFCShFRyL5f
* feat(applications): close follow-up gaps + squash migrations
Finish the deferred/open items on the applications tracker:
- Cover-letter upload re-enabled. Fix the storage blocker by deriving the
key extension from content type (buildFileKey/EXTENSION_BY_CONTENT_TYPE)
instead of hardcoding .jpeg, so PDFs serve correctly and non-JPEG image
avatars keep working under FLAG_DISABLE_IMAGE_PROCESSING. Add
coverLetterUrl/coverLetterName columns + Documents-section upload/remove.
- Contacts editor in the detail sheet (add/edit/remove, keyed per app).
- Board caps rendered cards per column (COLUMN_PAGE_SIZE=50 + "Show more").
- Extract new Lingui messages across locales.
- Guard coverLetterUrl to http(s)/relative at the API boundary.
Squash the five branch-only application-table migrations (create -> +tags
-> +cover-letter -> drop -> re-add) into a single clean CREATE TABLE via
drizzle-kit generate.
Claude-Session: https://claude.ai/code/session_01TEeRHnEayw2MFCShFRyL5f
* chore: update dependencies
* fix(web): address React Doctor findings — compiler, purity, query, component structure
prefer-module-scope-pure-function: hoist buildSubtitle, getDecimalPlaces,
handleLocaleChange, onLocaleChange, stop, listContent/groupedListContent to
module scope so they aren't rebuilt on every render.
react-compiler-todo (??=): rewrite draft.metadata.styleRules ??= [] to the
non-assignment form to unblock auto-memoization.
set-state-in-effect: derive updatedAtLabel at render time instead of syncing
it through useState + useEffect.
query-destructure-result: destructure useQuery results at call site in
resume-analysis and resume-thumbnail to follow TanStack Query v5 convention.
only-export-components: extract non-component exports to sibling .ts files so
Fast Refresh can preserve component state:
- getNextWeights → typography/get-next-weights.ts
- detectJsonImportType + ImportType → dialogs/resume/import.utils.ts
- getLocaleOptions → features/locale/locale-options.tsx
- preview helpers + DEFAULT_PDF_PAGE_SIZE → preview.shared.utils.ts
- resolveHighlightToolbarState + defaultHighlightColor → rich-input.utils.ts
- computeDelta + getSparklinePoints → statistics.utils.ts
no-multi-comp: split multi-component files into focused companions:
- ResumePane + ToolbarButton → routes/agent/-components/resume-pane.tsx
- DesktopBuilderShell → builder/$resumeId/-components/desktop-builder-shell.tsx
- MobileBuilderShell + helpers → builder/$resumeId/-components/mobile-builder-shell.tsx
- setBuilderLayout/getBuilderLayout moved to -store/sidebar.ts
fix(tests): add Resume type import to section-builder mocks and cast partial
mock data as unknown as Resume to satisfy stricter type checking; fix
noExplicitAny Biome errors in the same mocks.
* feat(applications): improve performance
* chore: fix knip issues
* perf(builder): halve per-keystroke render cost
Section-form fields called `form.handleSubmit()` on every keystroke, which
re-validated the whole form and toggled submit state — firing the render
cascade twice per character (~6809 renders/keystroke, FPS dropping to 9).
Persist via a form-level `listeners.onChange` instead and drop the per-field
`handleSubmit()` (basics, custom-fields, design). Narrow header/dock resume
subscriptions to metadata slices so they no longer re-render on content edits.
Cuts renders 6809 -> 3403 per keystroke (50%), 0 frame drops. Save, preview,
and design controls verified working; 449/449 web tests pass.
* perf(home): eliminate hero CLS from unreserved video box
The hero <section> is `flex items-center` (shrink-to-fit), so the video
wrapper's width depended on the video's intrinsic size, which only resolves
after the media loads. aspect-ratio couldn't reserve height without a definite
width, so the video grew from ~190px to ~563px after first paint and shoved the
centered hero text down ~373px (CLS ~0.095).
Give the wrapper a definite width (w-full + mx-auto on the CometCard) and set an
explicit aspect ratio + width/height on the video so its box is reserved before
load. CLS 0.095 -> 0; hero stays visually centered at max-w-4xl.
* docs: add application tracker guides
* chore(db): squash application migrations
* fix(email): import React in auth template for server-side rendering compatibility
* chore(release): v5.2.1
* Refactor resume rendering and builder workflows
* fix: address application tracker review findings
* feat(export): separate resume/cover-letter downloads, redesign dialog, add Markdown
Let people export the resume and cover letter as distinct documents, and add a
Markdown format alongside PDF / DOCX / JSON (handy for AI agents).
- Server/API: scope PDF generation and download URLs to a resume/cover-letter target.
- Export domain: getResumeExportData + resumeHasCoverLetter in @reactive-resume/resume.
- Redesign the download dialog: one global "What to export" scope toggle (Tabs) plus
flattened per-format rows, reusing existing UI components and design language.
- Add Markdown export (@reactive-resume/resume/markdown) with a small tiptap-HTML converter.
- Fix blank section headings in DOCX and Markdown by injecting the locale-aware
section-title resolver (titles are stored empty and resolved at render time).
- Locale catalogs updated for the new strings.
* test(e2e): open the download dialog before exporting JSON
The JSON export moved into the redesigned download dialog, so the spec now opens
the dialog from the Export sidebar section before clicking "Download JSON".
The utils color fallback restore re-added the @uiw/color-convert import
to packages/utils but not to apps/server, whose bundle keeps the package
external. Production server startup failed with ERR_MODULE_NOT_FOUND,
breaking the E2E workflow on main. Re-add the dependency.
Claude-Session: https://claude.ai/code/session_012Bnvt1MghwHj4qQRxuQUGa
Code-review findings:
- donation-toast: restore path/secure/sameSite cookie attributes the
inlined useCookie dropped (security/scope regression).
- utils/color: restore @uiw fallback so percentage-notation rgb() still
converts (custom style-rule colors are arbitrary strings); add tests
pinning the one real difference vs the black fallback.
- knip: drop stale npm-check-updates ignoreDependencies entry.
Claude-Session: https://claude.ai/code/session_012Bnvt1MghwHj4qQRxuQUGa
The 107-line useCookie hook had exactly one consumer (donation-toast) that
only read + set-with-expiry. Inline the two Cookies.* calls directly and
delete the hook + its 128-line test. Drop js-cookie and @types/js-cookie
from packages/ui/package.json (apps/web retains its own js-cookie dep).
Claude-Session: https://claude.ai/code/session_012Bnvt1MghwHj4qQRxuQUGa
Finding 1: Replace hand-rolled MONTH_NAMES[12] array with Intl.DateTimeFormat("en-US", {month:"long"}).
Finding 2: Drop @uiw/color-convert fallback — parseColorString already rejects the same inputs, return "#000000" instead. Remove dep from utils and server package.json.
Finding 3: Replace 56 z.literal calls in z.union with z.enum([...]); identical parse behavior and inferred type.
Claude-Session: https://claude.ai/code/session_012Bnvt1MghwHj4qQRxuQUGa
Two match chains in sections.tsx are replaced with plain lookup maps typed
via `satisfies Record<CustomSectionType, ...>` which preserves compile-time
exhaustiveness without the ts-pattern dependency. Remove ts-pattern from
packages/pdf/package.json.
Claude-Session: https://claude.ai/code/session_012Bnvt1MghwHj4qQRxuQUGa
AuthProvider was the only zod usage in the package — a z.enum solely to
infer a type. Replace with a plain union type and remove zod from
packages/auth/package.json dependencies.
Claude-Session: https://claude.ai/code/session_012Bnvt1MghwHj4qQRxuQUGa
* feat: add section heading icons to PDF templates
Add customizable Phosphor icons before section titles in PDF output.
Users can toggle visibility globally via a new "Hide section heading icons"
switch (independent of item-level icons) and customize individual section
icons through the builder sidebar icon picker.
- Add `icon` field to `baseSectionSchema` and `summarySchema`
- Add `hideSectionIcons` to `pageSchema` (defaults to true for backward compat)
- Implement `SectionHeadingIcon` component with heading font-size scaling
- Support "none" sentinel for per-section icon hiding
- Fallback to sensible defaults (briefcase, graduation-cap, etc.) for legacy data
- Add icon picker to builder sidebar sections and custom section dialogs
Closes#2632
* test: add unit tests for section heading icons
- Add tests for getResumeSectionIcon() covering built-in sections,
summary, custom sections, "none" sentinel, and default fallbacks
- Add schema tests for baseSectionSchema icon field, summarySchema icon,
and pageSchema hideSectionIcons default behavior
* refactor: minor updates to icon display
* Update apps/web/locales/es-ES.po
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
---------
Co-authored-by: Amruth Pillai <im.amruth@gmail.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>