Conversation
- route frontend tab, stage, reconnect, and persisted tab data through stateManager - tolerate stale optional sessions and return handled missing-session claim responses - prevent invalid analysis persistence, allow review pane retries, and expand regression coverage
Centralize frontend state access and harden stale-session review flows
- align preview generation with canonical spell-audit state and restore ATS data from backend session state - rebuild stale frontend bundles automatically at startup and surface bundle status metadata in logs and the startup banner - add session cleanup tooling plus focused Python and JS regressions for bundle freshness, staged generation, and cleanup flows
fix: tighten staged generation and startup hygiene
- clean style/import warnings in bibtex, concurrent session, and CV orchestrator tests - preserve direct run_tests.py execution for test_cv_orchestrator - keep keyword-compatible fake conversation manager methods - retain BibTeX parser regression coverage and wrapped fixture content
Tighten test diagnostics and preserve direct test execution
- add a web-scoped ESM package boundary for Node-based tests and helpers - remove the app.js CommonJS relay so tests import canonical modules directly - align the integration runner and state-manager test setup with ESM loading
…points Refactor web test entrypoints to ESM modules
Devel -> Main
- add minimal pointers to the standalone duckflow toolkit - keep repo-local annotation rules while avoiding duplicated in-repo tooling
- reference the standalone duckflow GitHub repository as the source of truth - replace machine-local script path examples with installable CLI examples
docs: restore duckflow toolkit guidance
Devel -> Prod
…r OpenAI providers
Adds json_mode: bool = False to the LLMClient.chat() abstract method and all
concrete implementations, then enables native response_format enforcement on
providers that support it.
Provider behaviour:
- OpenAIClient (and subclasses GroqClient, GitHubModelsClient, CopilotClient):
pass response_format={"type": "json_object"} to the API when json_mode=True,
constraining output at the API level rather than relying on prompt text alone
- CopilotOAuthClient: same — adds response_format to the raw JSON payload
- AnthropicClient, GeminiClient, CopilotSdkClient, LocalLLMClient, StubLLMClient:
accept json_mode param but ignore it; enforcement remains prompt-only for
providers that do not support a native JSON mode parameter
- call_llm() convenience wrapper gains the same param and passes it through
Internal call sites updated to pass json_mode=True:
- analyze_job_description() — returns structured job-analysis dict
- recommend_customizations() — returns structured recommendations dict
- rank_publications_for_job() — returns structured publication ranking list
- _propose_rewrites_via_chat() — returns structured rewrite proposals list
Call sites intentionally NOT updated (non-JSON responses):
- generate_professional_summary() — plain text
- rewrite_achievement() — plain text
- semantic_match() — numeric score
- convert_to_bibtex() — raw BibTeX text
Part of: feat/structured-json-output (Phase 2 of 4)
Add runtime structured output validation for the three heavy LLM calls:
- New `scripts/utils/llm_response_models.py`:
- `JobAnalysisResponse` (10 required fields + optional `reasoning`)
- `CustomizationResult` with nested `ExperienceRecommendation`,
`SkillRecommendation`, `AchievementRecommendation`, `SuggestedAchievement`
- `PublicationRankingItem` (cite_key, relevance_score, confidence, etc.)
- New `LLMClient._validate_with_repair()` helper:
- Validates a parsed dict against a Pydantic v2 BaseModel
- On `ValidationError`, extracts missing/invalid field paths and issues a
targeted one-shot repair prompt to the LLM (with json_mode=True)
- Re-validates the repaired response; re-raises if still invalid
- Wired validation into three callers in `llm_client.py`:
- `analyze_job_description` → validates with `JobAnalysisResponse`
- `recommend_customizations` → validates with `CustomizationResult`
- `rank_publications_for_job` → validates each array item with
`PublicationRankingItem`; invalid items after repair are skipped with a
warning rather than crashing the full ranking call
All 278 tests pass (pytest tests/test_llm_client.py
tests/test_layout_instructions.py tests/test_cv_orchestrator.py
tests/test_conversation_manager.py).
…ions Wire json_mode=True into the structured clarifying-questions chat call so API providers (OpenAI, GitHub Models, CopilotOAuth) enforce JSON output at the protocol level rather than relying only on prompt instructions. - `conversation_manager.py`: add `json_mode=True` to `self.llm.chat()` call that generates post-analysis clarifying questions (the call that feeds `_parse_json_questions_response`) - Remove dead `_extract_structured_questions()` method — it parsed numbered free-form text but was never called; the flow has used `_parse_json_questions_response` (JSON-first) since the structured-questions prompt was introduced - `tests/test_conversation_manager.py`: remove two tests that directly exercised the deleted `_extract_structured_questions` helper; retain the remaining test asserting phase transitions (61 tests pass) All non-browser tests pass: 1248/1250 (2 pre-existing Playwright timeouts).
Four findings from systematic code review; three resolved with code changes, one accepted as intentional design. Finding 1 — CopilotSdkClient ignores json_mode (FIXED): scripts/utils/llm_client.py: When json_mode=True, prepend a hard system instruction 'Respond with valid JSON only. No prose, no markdown fences.' before forwarding messages to any-llm. The copilotsdk provider converts messages to a flat prompt and has no response_format API param; the system message provides equivalent enforcement. Finding 2 — JobAnalysisResponse / CustomizationResult all-optional fields (DESIGN ACCEPTED): scripts/utils/llm_response_models.py: Added docstring comments explaining that all fields intentionally have defaults — type safety without breaking partial responses. Empty fields degrade gracefully in the downstream workflow. Finding 3 — No tests for _validate_with_repair() repair path (FIXED): tests/test_llm_client.py: Added TestValidateWithRepair class with 3 tests covering all code paths — immediate success, repair-on-failure, and ValidationError re-raise on persistent failure. Finding 4 — confidence field not validated against enum (ACCEPTED): scripts/utils/llm_response_models.py: Added inline comments '# expected: high | medium | low' to all four confidence fields. Kept as plain str for flexibility since confidence is display-only. CodeQL: 15 pre-existing findings in routes/ (path-injection, SSRF, stack-trace-exposure) — none in the modified files. All tests: 279 passed.
Security fixes (CodeQL): - auth_routes: remove stack trace exposure from HTTP 500 responses in set-model endpoint; add logger.warning for auth poll thread failures - job_routes: add DNS resolution check to block DNS-rebinding SSRF attacks on bare hostnames - session_routes: replace user-supplied path concatenation with a server-side enumeration helper (_resolve_session_path) to prevent path-injection vulnerabilities across load, delete, rename, restore, and trash endpoints Test fixes (Playwright): - test_web_ui_workflow: increase page-ready timeout (5 s to 15 s) to avoid intermittent startup failures on slow CI runners - mock_responses: add missing structured_output field to mock analysis fixture so phase-advance assertions pass Logging improvements: - auth_routes: log Copilot auth poll failures at WARNING level before storing error string for UI (background thread was completely silent on server logs) - job_routes: existing _requests.RequestException and bare Exception handlers already use logger.exception; Timeout/ConnectionError are user-expected conditions returning 400/500 with instructions - session_routes: silent except-pass clauses in listing loops are intentional (skip corrupt session.json); outer handlers already use logger.exception
…utput feat(conversation): structured JSON output with Pydantic validation and self-repair
Backend: - auth_routes: add _persist_provider_model_to_config() -- atomic YAML write (tmp+backup) so model/provider changes in the UI survive restart - llm_client: forward llm_request_timeout config value to OpenAI, Anthropic, and Gemini API calls (guards against hanging requests) - run_codeql.sh: add --custom-only mode to run .github/codeql/ queries against the VS Code extension's cached DB (~1-5 min vs ~15 min) Frontend: - job-analysis.js: skip extractStructuredQuestionsFromAssistantText when structured questions already present from API response - job-input.js: add FORBID_ATTR:['style'] to DOMPurify to strip inline CSS from Word/JSON-LD job descriptions - message-queue.js: use display='block' (not '') in show-more toggle - session-manager.js: trigger background testCurrentModel() health check after session restore and loadSessionFile - utils.js: guard cleanJsonResponse against non-string input Tests: - test_llm_client: patch utils.config.get_config in Anthropic and Gemini chat tests to isolate them from real config.yaml timeout value CodeQL queries: add 13 custom queries under .github/codeql/ covering unlogged exceptions, swallowed exceptions, exception detail in responses, SSRF path traversal, hardcoded secrets, LLM calls without timeout, master-data writes, Flask route inventory, and more
Replace silent `except: pass` blocks with structured log messages, add a slow-state badge to the LLM busy overlay, update the default provider/model, tighten CodeQL queries, and remove a dead workspace entry. Logging improvements - auth_routes: log non-critical provider/model persistence failure at DEBUG - generation_routes: log materialization failure and git-commit exceptions at WARNING - master_data_routes: log failed backup restoration at WARNING - session_routes: log unreadable session files at DEBUG; scan errors at WARNING - status_routes: log skipped unreadable sessions during clarification search at DEBUG - conversation_manager: log skipped session files and layout digest failures - copilot_auth: log unreadable token cache at DEBUG - session_registry: log timestamp parse failures at DEBUG UI — LLM busy overlay - Add "Taking longer than usual" pill badge that appears in slow-state overlay - Badge is hidden by default; shown only when the `.slow` class is applied Config - Switch default_provider to copilot-sdk and default_model to gpt-5-mini CodeQL - llm-call-without-timeout: exclude provider chat() impls that manage timeout internally via config, eliminating misleading false positives - master-data-write-outside-window: remove cv_data from master-var predicate (always a local rendering copy, never the master dict) - path-traversal: add isBarrier for _resolve_session_path, _resolve_backup_path, and safe_join helpers; narrow sink to os.path.join only Chore - Remove dead clem-diagrams entry from cv-builder.code-workspace
Add 15 new tests covering: - _getStepTooltip: all tooltip states (upcoming, active+viewing, browsing-away, completed+viewing/not-viewing, stale-critical, stale) - _updateViewingIndicator: viewing class applied, other pills cleared, browsing-away on active pill, no browsing-away when viewing active pill, tab alias mappings (questions→analysis, exp-review→customizations), unknown tab clears all rings
…firmed - Remove previewAvailable guard from the fresh-render path so generation_state.preview_html is always populated before confirm-layout, covering the legacy generate_cv action that writes files to disk without updating backend generation state - Collapse the now-redundant recovery path into the fresh-render path; passive restore is used only for confirmed/final_complete sessions or when generate-preview is unavailable (no job_analysis yet) - Regenerate web/bundle.js
…et leakage .specstory/history/ files can contain API keys from chat sessions. Also untrack .specstory/statistics.json which was previously committed.
…rvive tab navigation Agent-Logs-Url: https://github.com/Warnes-Innovations/cv-builder/sessions/ad3a0147-1320-484e-abdc-a603588e6fae Co-authored-by: warnes <6144863+warnes@users.noreply.github.com> # Conflicts: # web/bundle.js
Extend the state-persistence pattern introduced in PR #92 (questions panel) to all remaining panels that fully re-render on tab switch. cover-letter.js - Add _coverLetterFormState module-level object tracking tone, hiringManager, companyAddress, highlight, letterText, and letterVisible - Add _restoreCoverLetterFormState() called at end of populateCoverLetterTab() to restore field values and wire live-update listeners - generateCoverLetter() now saves letterText and letterVisible on success screening-questions.js - Add _screeningInputText module-level variable to persist sc-input textarea - Restore value and wire input event listener at end of populateScreeningTab() - Add _resetScreeningInputText() helper for test isolation rewrite-review.js - Re-apply stored rewriteDecisions (accept/reject/edit) after renderRewritePanel() re-renders the panel; calls applyRewriteAction, restores edited textarea text via saveRewriteEdit, and refreshes the tally display Tests - tests/js/cover-letter.test.js: 4 new tests for _coverLetterFormState persistence - tests/js/screening-questions.test.js: 3 new tests for _screeningInputText persistence - tests/js/rewrite-review.test.js: 4 new tests for decision state restoration on re-render All 1074 JS tests pass. # Conflicts: # web/bundle.js
…job and recommend_customizations
# Conflicts: # web/bundle.js
… sub-sections (closes #95)
…ents, ats-modal, trash, publications)
Add a new user-story file and related updates covering the initial
creation of Master_CV_Data.json from external source materials.
This closes the documented onboarding gap (previously noted in the
specstory review as "no story for creating master CV from scratch").
New file:
- tasks/user-story-master-cv-onboarding.md
Twelve stories (US-O1–US-O12) across three persona layers:
- Applicant / first-time user: guided path detection (US-O1),
LinkedIn data export import (US-O2), resume/CV document import
(US-O3), BibTeX / Google Scholar publications import (US-O4),
GitHub profile import (US-O5), multi-source merge (US-O6),
manual guided-form fallback (US-O7), review-and-confirm gate
(US-O8).
- UI/UX expert: source selection and import flow clarity (US-O9),
error and gap handling (US-O10).
- Master CV curator: post-import completeness check (US-O11),
conflict resolution when merging sources (US-O12).
Updated files:
- tasks/user-story-first-time-user.md: add US-F4 covering the
precondition check and onboarding entry point for first-time users
who have no existing master CV file.
- tasks/gaps.md: add GAP-25 documenting the onboarding gap, its
severity (HIGH), affected stories, and recommended resolution.
- tasks/workflow-review-expanded-personas.md: register the new story
file in the story-file index.
…nput Add five additional input paths to the resume/CV document import story: - Plain text file upload (.txt) - Markdown file upload (.md) — heading structure used as section hints - HTML file upload (.html/.htm) — tags stripped; heading elements used as section-boundary hints - Pasted text — any format accepted (plain, Markdown, or lightly formatted HTML copy-paste); auto-detected and tag-stripped where needed - The paste path is presented as a first-class equal-weight option alongside file upload (not a secondary fallback) Expand acceptance criteria and failure modes accordingly: - HTML tag-stripping required before LLM prompt (prevents script/style tag pollution and prompt injection via malicious HTML) - Pasted HTML auto-detected and stripped rather than passed raw to LLM - Markdown structural markers preserv- Markdown structural markers preserv- Markdown structural markers preserv- Markdown structural markers preserv- Markdown structural markers preserv- Markdown structural markers preserv- ripti- Markdown structural markers preserv- Markdset.
- Fix integration test: read textarea value via .value not .textContent, resolving the "buildSummaryFocusSection did not use session summary" failure - Upgrade CodeQL Action v3 -> v4 across both workflow files - Upgrade actions/setup-python v4 -> v5 across both workflow files - Add FORCE_JAVASCRIPT_ACTIONS_TO_NODE24=true workflow env to silence Node.js 20 deprecation warnings on cache/checkout/setup-node actions
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds user stories and a gap entry for the documented but unspecified workflow of creating
Master_CV_Data.jsonfrom scratch using external source materials. This addresses the onboarding gap previously noted in specstory history reviews: "There is no story for creating master CV from scratch for a first-time user."New file
tasks/user-story-master-cv-onboarding.mdTwelve stories (US-O1–US-O12) across three persona layers:
Applicant / first-time user:
UI/UX expert:
Master CV curator:
Updated files
tasks/user-story-first-time-user.mdtasks/gaps.mdtasks/workflow-review-expanded-personas.mdRelationship to existing stories / gaps
Testing / review checklist
workflow-review-expanded-personas.mdstory file list is complete