Commit Graph
5851 Commits
Author SHA1 Message Date
Amruth Pillai bcd5cf0ce9 docs: publish first implementation PR 2026-09-05 23:56:34 +02:00
Amruth Pillai 3180672543 docs: record plan 09 review findings 2026-09-05 23:53:49 +02:00
Amruth Pillai e6e11c41b2 docs: track review fixes and re-review 2026-09-05 23:51:57 +02:00
Amruth Pillai 654f8898b6 docs: queue literal whitespace unit 2026-09-05 23:49:33 +02:00
Amruth Pillai 142555302e docs: record plan 07 review finding 2026-09-05 23:47:41 +02:00
Amruth Pillai 0a7b158ee3 docs: queue one-shot sorting unit 2026-09-05 23:46:24 +02:00
Amruth Pillai f01a590389 docs: begin plan 09 review 2026-09-05 23:45:46 +02:00
Amruth Pillai 0a14ca78f7 docs: queue section and pagination units 2026-09-05 23:44:27 +02:00
Amruth Pillai c3d98241a7 docs: begin independent reviews 2026-09-05 23:42:11 +02:00
Amruth Pillai e81de44adf docs: make audit plan discovery executable 2026-09-05 23:39:36 +02:00
Amruth Pillai c87aae562e docs: harden pinned execution briefs 2026-09-05 23:38:40 +02:00
autofix-ci[bot] 18d49376ce [autofix.ci] apply automated fixes 2026-09-05 21:35:56 +00:00
Amruth Pillai c66a15bc68 docs: queue picture and table units 2026-09-05 23:35:00 +02:00
Amruth Pillai 02de0e9fcb docs: record backend audit corrections 2026-09-05 23:32:29 +02:00
Amruth Pillai 39c564cdf1 docs: record rendering and builder audits 2026-09-05 23:30:24 +02:00
Amruth Pillai 6f09cea66d docs: record backend revalidation dispositions 2026-09-05 23:26:03 +02:00
Amruth Pillai 124f9d8a2e docs: define plan 02 recovery brief 2026-09-05 23:24:23 +02:00
Amruth Pillai 22dcb838f0 docs: queue initial documentation units 2026-09-05 23:19:46 +02:00
Amruth Pillai 01f4963762 docs: define plan 09 and 11 briefs 2026-09-05 23:19:22 +02:00
Amruth Pillai 8f7faca67d docs: make execution briefs portable 2026-09-05 23:16:47 +02:00
Amruth Pillai 699229f2c5 docs: record active revalidation wave 2026-09-05 23:14:08 +02:00
Amruth Pillai ddc60756db docs: define plan 07 implementation brief 2026-09-05 23:13:34 +02:00
Amruth Pillai 7c827a42f0 docs: link coordinator ledger PR 2026-09-05 23:11:42 +02:00
Amruth Pillai 04029ec7f5 docs: record live status of existing PRs 2026-09-05 23:11:11 +02:00
Amruth Pillai b852518335 docs: add initial revalidation briefs 2026-09-05 23:09:14 +02:00
Amruth Pillai 9ecf340b9d docs: initialize approved issue execution ledger 2026-09-05 23:07:59 +02:00
Amruth Pillai 7a98f6662f docs: record Unicode fixes and remaining issue actions (#3452)
* docs: record Unicode fixes and remaining issue actions

* docs: record final Unicode PR merges
2026-09-05 11:40:32 -07:00
Amruth Pillai 05e48a7cbc fix(pdf): preserve authored Unicode spaces in rich text (#3451) 2026-09-05 11:31:03 -07:00
Amruth Pillai d10eb4a55d fix(pdf): isolate cached glyph character metadata (#3450)
* fix(pdf): isolate character metadata for cached font glyphs

* test(pdf): verify glyph aliases do not grow cache

* test(pdf): assert glyph aliases are unique
2026-09-05 11:22:05 -07:00
Amruth Pillai 1536dc48d9 docs: reconcile issue audit and record remaining reproductions (#3444)
* docs: reconcile issue audit with current resolutions

* docs: record pagination PR and new Ditgar reproduction

* docs: reconcile merged fixes and preserve pending scope decisions

* docs: reconcile audit scopes and merged PR states

* docs: record Ditgar fix and verified margin closure

* docs: reconcile incremental audit totals

* docs: record RTL preview fix and latest issue resolutions

* docs: record final audit PR merges

* docs: reconcile final review evidence

* docs: record paragraph indentation and RTL canvas PRs

* docs: record marker fix and Unicode-space reproduction

* docs: record final implementation PR merges
2026-09-05 11:04:15 -07:00
Amruth Pillai 8d4cf8a2f8 fix(pdf): keep ordered list markers clear of body text (#3449)
* fix(pdf): keep ordered list markers clear of body text

* perf(pdf): cache ordered list marker sizing
2026-09-05 10:53:34 -07:00
Amruth Pillai f468651c79 feat(editor): support whole-paragraph indentation (#3448)
* feat(editor): support whole-paragraph indentation

* fix(docx): retain indentation in quotes and RTL documents

* fix(exports): bound paragraph insets in narrow PDF columns

* fix(pdf): type bounded paragraph rendering consistently

* test(editor): use explicit list conversion commands

* fix(docx): preserve quote inset on list items
2026-09-05 10:41:30 -07:00
Amruth Pillai 5c8338c175 fix(builder): preserve PDF glyph positions in RTL previews (#3447) 2026-09-05 10:31:39 -07:00
Amruth Pillai 873835a571 fix(pdf): align Ditgar item headers with body text (#3445)
* fix(pdf): align Ditgar item headers with body text

* refactor(pdf): share Ditgar header border width
2026-09-05 10:14:12 -07:00
Amruth Pillai 14c7c06516 feat: add per-resume public download button preference (#3419) 2026-09-05 10:10:00 -07:00
Amruth Pillai 9fdcec2eca fix(builder): center preview in RTL interfaces (#3446) 2026-09-05 10:09:02 -07:00
Amruth Pillai 1d4194a207 feat: manage cover letters in a shared library (#3423)
* feat: add shared cover-letter library with resume styling

* fix: retain required sanitizer dependency in CI

* fix(cover-letters): prevent concurrent AI draft requests

* chore(codacy): exclude generated migrations

* fix(applications): keep Lingui macro out of callback dependencies
2026-09-05 10:01:22 -07:00
Syed Ali Abbas ZaidiandAmruth Pillai cce6d64afa feat(import): parse a PDF resume without an AI provider (#3400)
* feat(import): parse a PDF resume without an AI provider

Importing a PDF required a connected AI provider, so anyone without a
paid API key could only import the three JSON formats. Almost nobody
arrives with one of those files; they arrive with a PDF. The first thing
a new user tries to do was blocked behind bringing their own key.

Adds a deterministic parser that reads the text out of the PDF in the
browser and prefills the builder. It pulls the contact block, segments
the body on conventional headings, and maps entries to real items,
reusing the ATS period parser for dates so a date range is not mistaken
for a phone number.

Nothing is thrown away: header parts that do not map to a field go into
the description, and unrecognized headings become custom sections. The
imported sections are placed on the page so the result renders straight
away. Output is validated against the resume schema before it is
returned.

Text extraction groups items by baseline rather than trusting hasEOL,
and turns wide column gaps into a double space, which is what lets a
row split into company, position and location.

The AI path still runs when a provider is connected. Word import is
unchanged and still requires one.

Closes #3334

* fix(import): keep every section and entry the PDF actually contains

Review found three ways the parser lost or mangled content, all of them
reproducible.

A document whose first heading was not one of the known aliases never
started a section, because unknown-heading detection was gated on a
section already being open. Everything after it was swallowed as contact
header text. The header block is now bounded by where the contact
details stop, so a heading is recognized wherever it appears.

An entry spreading company, position and dates over three lines was
imported as two malformed items. A line that introduces an entry now
merges into the open entry instead of starting a second one.

An uppercase company such as ACME CORPORATION was read as a section
heading and fragmented the entry. A heading candidate followed by a date
line is now treated as an entry header, which is what it is.

Also escape single quotes, and construct the PDF worker inside the try
so the nested worker is terminated even if construction throws.

Title-case headings are deliberately still not treated as headings:
company and school names are title case too, and splitting on them would
fragment real entries. Such a section stays in the preceding one with its
text intact rather than risking loss.

* fix(import): look past a multi-line preamble before calling a line a heading

The previous guard only inspected the next line, so an uppercase company
followed by a separate role line and then the dates was still read as a
section heading. The experience or education entry was moved into a
custom section and lost.

Heading detection now scans a two-line window for the date that marks an
entry, and stops early at a bullet so a genuine heading whose section
opens with bullet points is still recognized.

The window can suppress a real heading whose first entry puts a bare date
two lines below it. That is the deliberate direction to fail in: a missed
heading leaves the text in the preceding section, while a misread entry
fragments structured content.

* fix(import): collect an entry preamble until its dates appear

An entry that spread company, role, location and dates over four lines
was imported as two broken items: the company with no dates, and the
location carrying the period.

The cause was in entry grouping rather than heading detection. Lines
before a date were only folded into the entry header when the date sat
on the very next line; anything earlier fell through to the description.
Preamble lines are now collected into the entry header until the dates
turn up, bounded by the same lookahead and stopping at a bullet, so an
undated section cannot swallow itself.

The heading lookahead widens to four lines to match, which is the
realistic maximum for company, role, location and dates.

* fix(import): harden local PDF resume parsing

* chore(import): document audited HTML construction

---------

Co-authored-by: Amruth Pillai <im.amruth@gmail.com>
2026-09-05 09:53:37 -07:00
Emanuele Tonello ef47baf243 fix(applications): handle cover letter copy failures (#3394)
* fix(applications): handle cover letter copy failures

* style(applications): format clipboard error toast

* refactor(applications): memoize copy draft handler
2026-09-05 09:51:24 -07:00
Emanuele TonelloandAmruth Pillai 1f0844b39c feat(applications): add contact email and phone (#3396)
* feat(applications): add contact email and phone

* fix(applications): validate imported contact emails

* fix(applications): validate all imported contact fields

* fix(applications): preserve data when contact validation fails

* test(applications): complete contact export fixture

---------

Co-authored-by: Amruth Pillai <im.amruth@gmail.com>
2026-09-05 09:51:21 -07:00
Diego Vega CentenoandAmruth Pillai 8df1b25550 feat(skills): add inline layout option for skill items (#3358)
* 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>
2026-09-05 09:51:14 -07:00
Amruth Pillai ea2beb8450 fix(pdf): keep list markers with their first text fragment (#3443)
* 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
2026-09-05 09:51:09 -07:00
Santhi PrakashandAmruth Pillai 861ba8bf60 fix(ai): make provider test timeout configurable via AI_TEST_TIMEOUT_MS (#3384)
* 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>
2026-09-05 09:35:14 -07:00
Santhi PrakashandAmruth Pillai e0c2f6d88a fix(pdf): preserve first character of section headings by adding left padding (#3386)
* 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>
2026-09-05 09:35:06 -07:00
Santhi PrakashandAmruth Pillai ea3980cba0 fix(components/form): resolve FormControl label target regressions (#3369) (#3387)
* 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>
2026-09-05 09:35:00 -07:00
Amruth Pillai cddb01f037 fix(sharing): record public PDF download statistics (#3414)
* fix(sharing): record public PDF download statistics

* docs(api): explain download statistics access cookie
2026-09-05 09:34:52 -07:00
Santhi Prakash c4eb9d860b fix(api): translate copilot AI provider failures to BAD_GATEWAY (#3333)
* 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
2026-09-05 09:33:18 -07:00
Amruth Pillai fe9b59e111 fix: restore MCP OAuth registration and authorization (#3421)
* fix: align MCP OAuth provider schema and authorization flow

* test: isolate OpenAPI generation from OAuth initialization

* fix: accept auth routes without a callback query

* fix: require explicit OAuth consent and preserve signed requests

* test: verify OAuth audiences through real MCP initialization

* test(e2e): isolate OAuth token audience validation
2026-09-05 09:33:15 -07:00
Amruth Pillai bf71253ca4 fix: preserve margins on PDF overflow pages (#3422)
* fix: preserve page margins across PDF overflow pages

* test(pdf): preserve styled and full-width Glalie backgrounds

* test(pdf): assert exact semantic margin colors
2026-09-05 09:33:13 -07:00
github-actions[bot]andCrowdin Bot 0207e5dfcc [skip ci] chore(i18n): sync translations from crowdin (#3441)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-09-05 09:02:30 -07:00