* feat(skills): add inline layout option for skill items
* fix: restore default skills layout (regressed by inline feature)
- Restore metrics rowGap style for default layout
- Only render LevelDisplay inside the row for inline layout, not default
* refactor(pdf): Extract inline skills style logic from JSX to reusable function
* test(pdf): add test coverage for inline skills item layout
- Add test suite SkillsSectionInlineFormat to verify isInlineSkillsItem and getSkillsItemStyle behavior
* test(pdf): add comprehensive test coverage for inline skills item style logic
- Test combinations of proficiency, level, and keywords fields (0, 1, 3 fields)
* test(schema): add test coverage for column equals 1 when layout is inline
* test(web): add component-level tests for inline and columns layouts
* test(import): add v4 parser-level test for missing skills layout
* docs: regenerate skills layout references
* test(docx): include skills layout in section fixtures
---------
Co-authored-by: Amruth Pillai <im.amruth@gmail.com>
* fix(pdf): keep list markers with their first text fragment
* fix(pdf): preserve page breaks while rewinding list companions
* fix(pdf): consume oversized list marker presence hints
* test(pdf): allow cold startup for pagination process guard
* fix(pdf): key list presence spacer
* fix(ai): make provider test timeout configurable via AI_TEST_TIMEOUT_MS
- Problem: the 30s hardcoded timeout is too short for self-hosted
deployments with cold-start models (e.g. Ollama). Makes it impossible
to pass the provider test (issue #3374).
- Fix: read AI_TEST_TIMEOUT_MS from the environment, defaulting to 30_000.
Zero behaviour change when the env var is absent.
- Verification: existing test asserts "30 seconds" in the timeout
message; default is unchanged so the test continues to pass.
(CI needs Node 22+ — not available on this host.)
* fix(ai): add AI_TEST_TIMEOUT_MS to turbo globalEnv so it reaches the API process
- Problem: Turborepo filters env vars not listed in globalEnv, so
AI_TEST_TIMEOUT_MS would always be undefined at runtime under
turbo dev/start, making the override dead code.
- Fix: add AI_TEST_TIMEOUT_MS to the globalEnv array.
- Verification: turbo.json validates as valid JSON.
* fix(ai): validate AI_TEST_TIMEOUT_MS as a finite non-negative integer
* docs(ai): add JSDoc to timeout parser and test helper
* test(ai): restore AI_TEST_TIMEOUT_MS after timeout tests
- Problem: loadWithTimeout() mutates process.env.AI_TEST_TIMEOUT_MS but nothing restores it, so the last value tested ("999999999999") leaked to every test that runs after this describe block in the same file.
- Fix: save the pre-test value and restore it in an afterEach hook.
- Verification: pnpm exec vitest run src/features/ai/service.test.ts in packages/api — 18/18 passed.
* test(api): isolate AI timeout environment cases
---------
Co-authored-by: Amruth Pillai <im.amruth@gmail.com>
* fix(pdf): add left padding to section heading text to prevent first-character clipping
Closes#3380
* fix(pdf): apply heading padding default after style composition
Apply paddingLeft: 1 only when no composed style fragment already defines it, so an explicit paddingLeft from a template or style rule is preserved. Keep the fallback for an empty style list.
* fix(pdf): keep heading safety padding on text only
---------
Co-authored-by: Amruth Pillai <im.amruth@gmail.com>
* fix(components/form): resolve FormControl label target regressions (#3369)
- Expose FormControlContext and wrap FormControl children in Base UI's
LabelableProvider so the generated control id reaches the actual
labelable element.
- Update InputGroup/InputGroupInput to consume the context and place
the id on the real input instead of the fieldset.
- Update Slider to discard the wrapper id and use the context via
LabelableProvider so the thumb input receives the id and
aria-labelledby.
- Update ChipInput to consume the context, set id and aria-labelledby
on the inner input, and only fall back to aria-label when not inside
a FormItem.
- Restructure the sidebar layout so a single FormControl labels the
numeric input and the visible FormLabel is referenced by id for the
sibling Slider, removing the duplicate-id defect.
- Add a dev-time warning when the generated id lands on a non-labelable
or missing element.
- Extend form.test.tsx with regression coverage.
* test(form): add regression coverage for chip-input and dual-control layout
* fix(ui): surface FormControl error state as aria-invalid on the Slider control
- Problem: FormControl injects aria-invalid={hasError} onto its rendered
element, but Slider stripped it without re-applying it anywhere, so the
error state never reached the DOM (flagged by Codacy/Greptile/CodeRabbit).
- Fix: bridge aria-invalid onto Base UI's native range input via the Thumb's
public inputRef prop; Base UI v1.7 has no prop path for it (its validation
props only apply through Base UI Field context). id stays stripped since
LabelableProvider already delivers it to the input.
- Verification: new regression test in form.test.tsx fails on the pre-fix
head (aria-invalid null) and passes post-fix; packages/ui 363/363 tests
green; tsc --noEmit on packages/ui clean.
* fix(ui): let a caller-supplied data-slot override the Slider default
- Problem: the FormControl label-target fix moved data-slot="slider" after
{...props} on SliderPrimitive.Root, so a caller's data-slot was silently
overwritten with the default — a prop-ordering regression against both the
prior file and the repo-wide convention (FormItem, FormLabel, InputGroup all
place data-slot before the spread).
- Fix: restore data-slot="slider" before {...props} so caller values win.
- Verification: packages/ui — vitest src/components/slider.test.tsx
src/components/form.test.tsx = 30/30 passing; new regression test
("lets a caller-supplied data-slot override the default") fails on the
pre-fix head (data-slot="slider" wins) and passes with the fix; tsc
--noEmit clean.
* fix(ui): preserve standalone Slider and InputGroup identity props
- Problem: the FormControl prop strip dropped a standalone caller's id on
Slider and id/aria-describedby/aria-invalid on InputGroup, so standalone
compositions rendered no element carrying those attributes (regression
vs main, flagged by maintainer review on this PR).
- Fix: strip the FormControl-generated props only when a FormControl
ancestor is present (useFormControl context); preserve explicit caller
props for standalone usage in both components.
- Verification: new standalone + FormControl-wrapped tests fail on the
prior head and pass after the fix; packages/ui 367/367, apps/web
595/595, tsgo --noEmit clean.
* fix(ui): remove internal label provider dependency
---------
Co-authored-by: Amruth Pillai <im.amruth@gmail.com>
* fix(api): translate copilot AI provider failures to BAD_GATEWAY
- Problem: AI provider errors (bad key, unknown model, quota, 5xx) from the
AI SDK bubble out as opaque 500 INTERNAL_SERVER_ERROR from copilot
endpoints (autofill, match-score, draft-message, tailor-resume).
- Fix: catch AISDKError in generatePlainText and a local generateJson
wrapper that delegates to the shared generate-json module, translating
both to BAD_GATEWAY (502) with the original error preserved as cause.
Mirrors the existing pattern in features/ai/router.ts.
- Verification: vitest (CI — requires Node 22+). Test file unchanged in
assertion logic from the original PR; the local generateJson now
wraps the shared module instead of duplicating it.
Rebased onto main after v5.2.9 AI-layer refactor (generateJson extracted
into features/ai/generate-json.ts).
* fix(api): align generateJson prompt shape with callers and shared module
- Problem: local generateJson wrapper accepted (model, prompt: string,
schema) but all callers pass (model, { prompt: string }, schema).
Caught by CodeRabbit review.
- Fix: match the shared generate-json module signature — accept
{ system?, prompt } as the second argument and pass it through.
Updated test calls to match.
* fix(test): remove stray leading dots from mock object property names
- Problem: rebase onto v5.2.9 introduced `.use`, `.output`, `.errors`
as property names in the chain mock object, which is invalid JS
syntax and would cause a parse error when tests run.
- Fix: remove the leading dots to restore valid property names.
- Verification: cat -A confirms tabs-only indentation, no leading dots.
* fix(docs): correct 'a actionable' to 'an actionable' in comment
- Problem: Grammar typo in inline comment.
- Fix: 'a actionable' → 'an actionable'.
- Verification: grep confirms no remaining instances.
* fix(api): narrow copilot AI BAD_GATEWAY predicate to APICallError and exhausted RetryError
* feat(applications): export applications as CSV
* fix(applications): strip export CSV formula guard on import and resort catalogs
Re-importing an exported CSV kept the apostrophe that csvCell prepends to
formula-triggering cells, so a note starting with "- " came back as "'- ".
mapCsvToApplications now drops a leading apostrophe when the remainder would
have been guarded, sharing the predicate with csvCell so both sides stay in
sync.
Also runs pnpm lingui:extract: the new msgids were hand-appended to en-US.po
and missing from the other 54 catalogs.
* fix(applications): preserve CSV import values
* fix(stylesheet): keep color picker state aligned with source
* fix(stylesheet): serialize picker edits as hex with alpha
* fix: preserve contextual colors in stylesheet editor
* docs: track open issue audit and resolution plan
* docs: update issue audit with verified fixes
* docs: record cover-letter library verification
* docs: record OAuth and cover-letter CI verification
* docs: track compact views and remaining accessibility fixes
* docs: track CSV export and picture rendering fixes
* docs: record PDF localization, cache fix and consent review
* docs: track consent and rendering fixes in repository-only audit
* docs: refresh issue audit progress and review evidence
* docs: record latest issue reproductions and published fixes
Rewrites the landing page, in-app microcopy, and public docs, then fixes what the rewrite exposed: stale template counts, a broken quickstart anchor, out-of-sync FAQ structured data, dead error-hint branches in the MCP tools, and wrong-sense translations across all 53 locales. Adds GLOSSARY.md so translators get the right sense of the ambiguous UI terms.