Commit Graph

83 Commits

Author SHA1 Message Date
80bb08eb51 fix(ci): use GitHub secrets instead of hardcoded test values
BREAKING: Previous commit used hardcoded test values (security risk)

Changes:
- Replace all hardcoded env vars with ${{ secrets.* }}
- Use existing GitHub secrets configured via 'gh secret set'
- Maintain proper secret isolation in CI/CD

Secrets used:
- SECRET_KEY, DEBUG, HTTPS_ONLY, SERVER_ENV
- Database: SQL_DIALECT, LOCAL_DB_*, CLOUD_DB_*
- API Keys: OPENAI_*, TAVILY_API_KEY
- Reddit: REDDIT_*

Benefits:
-  No secrets exposed in YAML file
-  Uses existing secret management infrastructure
-  Proper separation of concerns
-  Secrets can be rotated via 'gh secret set'

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-04 16:20:22 -05:00
b976fac645 fix(ci): add test environment variables to prevent import errors
Issue: CI failing with "UndefinedValueError: SECRET_KEY not found"
Root Cause: Settings modules load env vars at import time, before
conftest.py can set TESTING=true

Fix: Add minimal test environment variables to CI workflow
- SECRET_KEY for security module
- Database credentials (not used, but required for imports)
- API keys (fake values for testing, not used)
- Reddit credentials (not used in tests)

All values are fake/test-only and do not expose real credentials.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-04 16:13:25 -05:00
a11e0c65fe refactor: migrate to uv + pyproject.toml dependency management
BREAKING CHANGE: Removed pip-style requirements files

Migration Details:
- Removed core_requirements.{in,txt} and dev_requirements.in
- Consolidated all dependencies into pyproject.toml
- Added platform markers for Windows-specific packages:
  - pywin32>=311 (sys_platform == 'win32')
  - win32-setctime>=1.2.0 (sys_platform == 'win32')
  - hypercorn (Windows ASGI server)
  - gunicorn (Unix WSGI server)

CI/CD Changes:
- Updated .github/workflows/test.yml to use 'uv sync --group test'
- Simplified installation: no more manual pip install steps
- Uses 'uv run pytest' for test execution with PYTHONPATH

Benefits:
-  Fixes pywin32 installation failure on Ubuntu CI runners
-  Single source of truth for dependencies (pyproject.toml)
-  Faster resolution with uv lockfile
-  Modern Python packaging (PEP 621)
-  Proper dependency groups (dev, test)
-  Platform-aware installation

New Workflow:
- Production: uv sync
- With tests: uv sync --group test
- With dev tools: uv sync --group dev
- All groups: uv sync --all-groups

Added MIGRATION_UV.md with full migration guide for developers.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-04 16:07:14 -05:00
6745b3c870 test: add comprehensive regression tests for PDF download error handling
Tests mock the exact production bug scenario:
- Session exists but result is None → 404 with proper Response

Regression Tests:
- test_download_pdf_without_session_returns_404
  → No session ID error path
- test_download_pdf_without_result_returns_404
  → Session but no result (EXACT production bug)
- test_download_pdf_error_paths_use_response_not_streaming
  → Verifies Response used, not StreamingResponse

Additional Tests:
- test_download_pdf_returns_pdf_file
  → Successful PDF generation and download
- test_download_pdf_uses_cache
  → PDF caching behavior validation
- test_download_pdf_filename_format
  → Filename format verification

Mocking Strategy:
- Use unittest.mock.MagicMock for request.session
- Mock session.get() to return test session_id
- Call download_pdf() handler directly with mocked request
- Avoids TestClient session middleware complications

Coverage Improvements:
- pdf_service.py: 31% → 99%
- pdf_cache.py: 37% → 76%
- router.py: 35% → 53%
- Overall: 49% → 63%

All tests pass and prevent regression of AttributeError bug.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-04 15:59:36 -05:00
8aaddd7ec9 fix: use Response instead of StreamingResponse for PDF error paths
Bug: AttributeError: 'int' object has no attribute 'encode'
Root Cause: StreamingResponse error paths (no session, no result) were
using raw bytes instead of iterators

Fix:
- Import Response from starlette.responses
- Replace StreamingResponse with Response for error paths:
  - No session ID (line 189)
  - No result available (line 201)
- PDF success path already uses iterfile() generator (correct)

This prevents the encode error when PDF download fails validation checks.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-04 15:48:39 -05:00
380ea7ef17 docs: add comprehensive test coverage gap analysis
Created TEST_COVERAGE_GAPS.md documenting areas needing test coverage.
Organized by priority with specific test suggestions for each gap.

High Priority Gaps:
- E2E test for complete analysis flow (form → celery → status → result → PDF)
- AI agent testing (SWOT generation, tool use, error handling)
- Input validation/sanitization (SQL injection, XSS, SSRF)
- PDF cache memory limits (no eviction policy currently)
- Dependency security scanning (4 high-severity vulnerabilities)

Medium Priority Gaps:
- Database operations (persistence, migrations, transactions)
- HTMX OOB swap DOM validation (verify correct structure)
- Load testing (concurrency, memory leaks, queue saturation)
- PDF security (size limits, timeouts, content sanitization)
- Deployment validation (Docker build, env vars, health checks)

Low Priority Gaps:
- Frontend Jinjax component testing
- Cross-browser compatibility
- Accessibility (WCAG 2.1 AA compliance)
- Logging and error tracking
- Test data fixtures

For each gap, document includes:
- Description of the gap
- Current coverage status
- Specific test suggestions
- Priority and effort estimates

Ready to convert to GitHub issues using provided template.

Current Coverage: ~15%
Target Coverage: 80%
Critical Path Target: 95%

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-04 15:19:41 -05:00
1be156ed7c feat: add pytest infrastructure and regression tests
Implements comprehensive testing framework to prevent regressions like:
- PDF style name conflicts (BodyText)
- StreamingResponse type errors
- Template not found after refactors

Test Infrastructure:
- pytest.ini: Configuration with coverage, markers, asyncio support
- conftest.py: Shared fixtures (test_client, test_db, sample_data)
- GitHub Actions CI: Automated testing on push/PR
- Directory structure: tests/{unit,integration,fixtures}

Integration Tests (test_analyze_flow.py):
- Regression: analyze endpoint returns empty response (not status.html)
- Status polling with OOB swaps
- Session creation and management
- First poll returns container + items
- Subsequent polls return only new items
- Result endpoint with/without data

Integration Tests (test_pdf_export.py):
- Regression: PDF generation returns BytesIO (not int)
- Regression: No ReportLab style name conflicts
- PDF download endpoint with streaming response
- PDF caching behavior
- Valid PDF format verification
- Filename format validation

Unit Tests (test_pdf_service.py):
- Content hash generation and consistency
- PDF generator initialization
- Custom style creation without conflicts
- SwotAnalysis model validation

CI/CD:
- GitHub Actions workflow for automated testing
- Python 3.13 support
- Coverage reporting with codecov integration

Test Markers:
- @pytest.mark.unit: Fast, isolated tests
- @pytest.mark.integration: Multi-component tests
- @pytest.mark.pdf: PDF-related tests
- @pytest.mark.api: API endpoint tests

Fixtures:
- test_client: FastAPI TestClient
- test_db_session: SQLite in-memory database
- sample_swot_analysis: Mock SWOT data
- clear_caches: Auto-cleanup between tests

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-04 15:18:07 -05:00
b120638055 fix: resolve analyze_url regression after Jinjax refactor
Bug: TemplateNotFound: 'status.html' not found
Cause: status.html was deleted during Jinjax refactor but analyze_url
       endpoint still referenced it

Fix: Return empty HTMLResponse instead of template
     - HTMX polling with hx-swap="none" doesn't need initial content
     - All status rendering happens via OOB swaps from /status endpoint
     - Empty response simply acknowledges form submission

This regression occurred because the Jinjax refactor removed status.html
but didn't update the analyze_url endpoint that used it.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-04 15:02:49 -05:00
e0cbbab312 fix: resolve critical PDF generation bugs
Fixed two critical errors preventing PDF downloads:

1. KeyError: "Style 'BodyText' already defined in stylesheet"
   - Renamed custom style from "BodyText" to "ReportBodyText"
   - BodyText is a reserved ReportLab built-in style name
   - Updated all references to use new name

2. AttributeError: 'int' object has no attribute 'encode'
   - StreamingResponse expected an iterator, not BytesIO directly
   - Added iterfile() generator function to yield BytesIO chunks
   - Properly resets buffer position with seek(0) before iteration

Additional:
- Removed unused hex_to_rgb_tuple() function (dead code cleanup)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-04 14:43:04 -05:00
ce76f958f7 refactor: convert status updates to Jinjax component architecture
Replaced raw HTML string concatenation with proper Jinjax components
following the project's established component patterns.

Component Architecture:
- StatusItem.jinja: Individual status message component
  * Accepts: message, is_last, result, use_oob
  * Determines status type (error/loading/complete/info) automatically
  * Renders with appropriate icons and styling
  * Optional HTMX OOB swap for dynamic appending

- StatusTimeline.jinja: Container component for status items
  * Provides semantic HTML structure with ARIA labels
  * Optional HTMX OOB swap for initial insertion
  * Children appended via HTMX to #status-timeline

Backend Changes:
- Removed raw HTML string templates (anti-pattern)
- Use catalog.render() for all component rendering
- First poll: Render StatusTimeline + StatusItem components with OOB
- Subsequent polls: Render only new StatusItem components with OOB
- Clean separation of concerns (backend = logic, Jinjax = presentation)

Frontend Changes:
- Changed #status div to hx-swap="none" (all updates via OOB)
- Removed old status.html and status_item.html templates
- StatusItem and StatusTimeline now in components/snippets/

Benefits:
- No raw HTML in Python code
- Reusable Jinjax components
- Consistent with project patterns
- Easier to maintain and test
- Clean separation of presentation and logic

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-04 14:36:47 -05:00
664cbca4b8 fix: replace Alpine.js button with anchor tag for PDF download
Issue: PDF download button wasn't working because Alpine.js doesn't
automatically initialize on dynamically loaded HTMX content. The
result.html template is loaded via HTMX polling, and the x-data
directive wasn't being processed on the new DOM elements.

Solution: Replaced the Alpine.js-powered button with a simple anchor
tag styled as a button. This works immediately without requiring
Alpine initialization and properly triggers browser download behavior.

Changes:
- Removed x-data, @click, :disabled, :class Alpine directives
- Changed <button> to <a> with href="{{ url_for('download_pdf') }}"
- Added download attribute for proper download hint
- Kept all styling and accessibility features
- Simplified implementation without loading state (unnecessary for downloads)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-04 14:15:50 -05:00
694273b065 fix: add missing id attribute for HTMX OOB swap target
The status-timeline container was missing its id attribute, causing
HTMX out-of-band swaps to fail silently. Status items were being
generated but not appended to the DOM because the OOB swap directive
hx-swap-oob="beforeend:#status-timeline" couldn't find the target.

Fixed by adding id="status-timeline" to the status timeline div.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-04 14:08:32 -05:00
d3e82bc757 feat: add PDF export functionality for SWOT analysis reports
- Add ReportLab dependency (v4.4.9) for PDF generation
- Create professional PDF generator with StrategIQ branding
  * Brand colors matching _variables.scss
  * Structured layout with SWOT categories
  * Executive summary section
  * Page numbering and footer branding
- Implement composite caching system (5-minute TTL)
  * Cache key: session_id + content_hash
  * Auto-cleanup of expired entries
  * Access tracking and statistics
- Add async PDF download endpoint (/download-pdf)
  * Cache-first strategy for performance
  * Safe dictionary access with .get()
  * Proper error handling for missing sessions
- Add download button to results page
  * Alpine.js loading state
  * Accessible button with ARIA labels
  * Professional styling with gradient

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-04 13:38:20 -05:00
7a903b7872 fix: ensure white background for executive summary box
Explicitly set background-color to white for executive summary to fix
dark text on dark background readability issue.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-04 13:18:36 -05:00
b9aaefd840 feat: implement HTMX OOB swaps for incremental status updates
Replace full container refresh with incremental status item appends
using HTMX Out-of-Band swaps for smoother UX without flashing.

Problem:
- Status container innerHTML swap replaced entire timeline every second
- All items re-rendered causing visual flashing even with CSS transitions
- Inefficient: re-sends all messages every poll

Solution:
- Track last message index per session
- Return only NEW messages since last poll
- Use HTMX hx-swap-oob="beforeend:#status-timeline" to append items
- First poll returns full container, subsequent polls return only deltas

Architecture:
1. Session Tracking (consts.py):
   - Added last_message_index dict to track per-session state
   - Memory-based, resets on server restart (acceptable for dev)

2. New Template (status_item.html):
   - Single status article with OOB swap attribute
   - Reusable component for incremental updates

3. Backend Logic (router.py):
   - First poll: full status.html with all messages
   - Subsequent: only new messages with OOB attributes
   - Empty response if no new messages

4. Frontend (home.html):
   - Removed settle:200ms (no longer needed)
   - Status timeline receives appended items

Benefits:
- Eliminates flashing during updates
- More efficient (only sends deltas)
- Smoother perceived performance
- No JavaScript state management required
- Automatic HTMX OOB handling

Technical Details:
- OOB swap: Items append to #status-timeline automatically
- Session index tracks progress per analysis
- First poll initializes container structure
- Graceful degradation if session tracking resets

Testing:
- Verify first poll shows container + all messages
- Verify subsequent polls append only new items
- Verify no flashing during status updates
- Verify empty responses when no new messages

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-04 13:13:40 -05:00
f7ec32f4fa fix: improve text contrast for WCAG accessibility compliance
Fix poor contrast issues in hero title and executive summary text
to meet WCAG 2.1 AA standards (4.5:1 minimum ratio).

Problems:
1. Hero title: Purple gradient text on purple background (low contrast)
2. Executive summary: Dark gray text potentially hard to read

Root Cause:
- .hero-title used gradient with transparent text fill
- Gradient purple + purple background = poor visibility
- CSS custom properties (--neutral-700) may not resolve correctly

Solution:
1. Hero Title: Replace gradient with solid white (#FFFFFF)
   - Adds subtle text shadow for depth
   - High contrast on purple gradient background (21:1 ratio)

2. Executive Summary: Use explicit hex color
   - Replace var(--neutral-700) with #374151
   - Add font-size: 1rem for consistency
   - Ensures readability on white background (10.6:1 ratio)

Changes:
- _typography.scss: Solid white hero title with text shadow
- result.html: Explicit color for executive summary

WCAG Compliance:
- Hero title: 21:1 ratio (AAA) 
- Executive summary: 10.6:1 ratio (AAA) 
- Both exceed 4.5:1 minimum for AA compliance

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-04 13:04:35 -05:00
beda4fc4df feat: add smooth transitions to reduce status container jarring
Add HTMX settle delay and CSS transitions to make status updates
less jarring when the entire container refreshes.

Problem:
- Status container innerHTML swap replaces entire timeline
- All status items re-render causing visible flash
- Jarring UX during updates

Solution:
- Add HTMX settle:200ms modifier for smoother swaps
- Add CSS transitions for .htmx-swapping and .htmx-settling classes
- HTMX automatically applies these classes during swap lifecycle

Changes:
- home.html: hx-swap="innerHTML settle:200ms"
- _animations.scss: Add .htmx-swapping and .htmx-settling transitions
- Smooth fade during swap (opacity 0.7) and settle (opacity 1)

Technical Details:
- HTMX swap lifecycle: swapping → settling (200ms each)
- CSS transition: 150ms ease for smooth opacity changes
- Still replaces full container but visually smoother

Result:
- Less jarring status updates
- Smooth fade during refresh
- Better perceived performance
- Maintains current architecture (no backend changes)

Future Enhancement:
Consider HTMX OOB swaps to append only new items instead of
replacing entire container for even better UX.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-04 13:00:55 -05:00
e8e4c3ae23 fix: stop container flashing and halt polling when complete
Remove double wrapping and add DOM-based polling stop condition to
prevent container flashing and endless polling.

Problems:
1. Status/results containers flash during updates
2. Polling continues forever even after results load
3. Double wrapping: home.html box + backend template section

Root Cause:
- home.html wrapped backend templates in <div class="box">
- Backend templates return complete <section> elements with styling
- No condition to stop polling after results appear

Solution:
- Remove class="box" wrapper from home.html (backend handles styling)
- Add DOM check to stop polling: [!document.querySelector('#result-container')]
- Polling stops once #result-container appears in DOM

Changes:
- Removed class="box" from status div
- Removed class="box" from results div
- Added polling stop condition to both divs
- HTMX checks DOM every second, stops when results exist

Result:
- No more flashing containers (single wrapper)
- Smooth content updates without re-rendering parent
- Polling stops automatically when analysis complete
- Better performance (no unnecessary requests)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-04 12:46:23 -05:00
d526569933 fix: correct HTMX polling and Alpine state integration
Fix status and results not loading by removing HTMX conditions that
don't react to Alpine state changes and simplifying visibility logic.

Problems:
1. HTMX trigger conditions [analyzing] don't react to Alpine state
2. Status div hidden initially (x-show with hasStatus requirement)
3. Chicken-and-egg: hasStatus needed to show, but only set on response
4. HTMX doesn't poll hidden elements effectively

Solution:
- Remove HTMX conditional triggers [analyzing], [analyzing && !hasResults]
- Simplify status visibility: x-show="analyzing && !hasResults"
- Let HTMX poll unconditionally with "every 1s"
- Alpine x-show controls visibility, HTMX polls when visible
- Cleaner event handlers with explicit response checking

Changes:
- Status: removed hasStatus from x-show condition
- Status: hx-trigger="every 1s" (no conditions)
- Results: hx-trigger="every 1s" (no conditions)
- Better response validation in @htmx:after-request

Result:
- Status updates appear immediately when analyzing starts
- Status polls continuously until results arrive
- Results polling works correctly
- Proper state transitions: analyzing → hasResults

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-04 12:30:15 -05:00
904f19f8f8 fix: prevent spinner flash on page load with x-cloak
Add Alpine.js x-cloak directive and CSS to prevent spinner from
showing during Alpine initialization.

Problem:
- Spinner briefly visible on initial page load before Alpine.js initializes
- x-show directive not applied until Alpine loads
- Creates confusing FOUC (Flash of Unstyled Content)

Solution:
- Add x-cloak attribute to spinner div
- Add [x-cloak] { display: none !important; } CSS
- Alpine removes x-cloak after initialization, allowing x-show to work

Result:
- Spinner hidden on initial page load
- Only shows when analyzing state is true
- Clean page load experience

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-04 12:16:55 -05:00
700bb9565f feat: migrate to Alpine.js for declarative state management
Fix spinner overlay blocking status updates by introducing Alpine.js
for reactive state management. Replaces scattered vanilla JS with
declarative directives.

Problem Fixed:
- Spinner remained as full-screen overlay during status updates
- Status messages were rendered but hidden behind spinner
- Manual DOM manipulation scattered across multiple files

Solution:
- Alpine.js v3 added via CDN (15KB, zero build config)
- Centralized state: { analyzing, hasStatus, hasResults }
- Declarative visibility with x-show and x-transition
- HTMX integration via @htmx:after-request events

Changes:
- Scripts.jinja: Add Alpine.js CDN script
- home.html: Wrap analysis UI in x-data, add state directives
- app.js: Remove manual spinner visibility logic, keep UX features
- Spinner.jinja: Update to Alpine-compatible component

Benefits:
- Spinner hides automatically when status updates appear
- Smooth transitions between states
- Cleaner, more maintainable code (-36 lines)
- Better HTMX + Alpine.js integration pattern
- Maintains WCAG 2.1 AA accessibility

Technical Details:
- State flow: analyzing → hasStatus → hasResults
- Spinner: visible when analyzing && !hasResults
- Status: visible when analyzing && hasStatus && !hasResults
- Results: visible when hasResults

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-04 11:54:51 -05:00
f9762093f8 feat: upgrade Docker image to Python 3.13
Upgrade from Python 3.12.3 to Python 3.13.3 using deadsnakes PPA.

Changes:
- Add build.sh execution in Dockerfile to install Python 3.13
- Install Python 3.13 from deadsnakes PPA with dev packages
- Set Python 3.13 as default via update-alternatives
- Create venv explicitly with Python 3.13
- Update pyproject.toml to require Python >= 3.13
- Fix line endings in shell scripts (CRLF → LF)

Build process:
1. Adds deadsnakes PPA repository
2. Installs python3.13, python3.13-dev, python3.13-venv, python3.13-distutils
3. Sets as system default Python
4. UV creates venv with Python 3.13
5. All packages install successfully

Tested with FastAPI 0.128.0 - all dependencies working correctly.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-04 11:17:53 -05:00
464f6b3ddb fix: correct Traefik loadbalancer configuration for Docker network
Replace external IP-based server.url with internal port-based auto-discovery.
This allows Traefik to correctly communicate with the service via the Docker
proxy network instead of trying to route through external IPs.

Changes:
- Remove: traefik.http.services.pygentic_ai.loadbalancer.server.url
- Add: traefik.http.services.pygentic_ai.loadbalancer.server.port
- Use INTERNAL_PORT (5051) for container communication

This fixes the gateway timeout issues in Komodo deployments.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-04 10:27:49 -05:00
9f9f611f9e [Komodo] fsecada01: Write Stack File: update ./compose.yaml 2026-02-04 14:07:08 +00:00
03eef9afd1 [Komodo] fsecada01: Write Stack File: update ./compose.yaml 2026-02-04 14:06:49 +00:00
cb8e1b54e8 [Komodo] fsecada01: Write Stack File: update ./compose.yaml 2026-02-04 14:05:50 +00:00
708f95089f Merge pull request #11 from FJS-Services-Inc/dev_deploy
Some checks failed
Bandit / bandit (push) Has been cancelled
Docker Image CI / build (3.13) (push) Has been cancelled
refactor: use pathlib.Path for BASE_DIR, BACKEND_DIR, FRONTEND_DIR
2026-02-02 23:12:39 -05:00
49aab16f83 refactor: use pathlib.Path for BASE_DIR, BACKEND_DIR, FRONTEND_DIR
Some checks failed
Bandit / bandit (push) Has been cancelled
Docker Image CI / build (3.13) (push) Has been cancelled
Replaces os.path.dirname/join chains with Path(__file__).resolve() and /
operator. Updated Settings field types from str to Path to satisfy
pydantic v2 strict coercion.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-02 23:12:16 -05:00
408a2960af Merge pull request #10 from FJS-Services-Inc/dev_deploy
fix: resolve remaining pydantic-ai 0.4.x breakages and startup errors
2026-02-02 23:09:30 -05:00
6e978b9de9 fix: resolve remaining pydantic-ai 0.4.x breakages and startup errors
- result_type -> output_type on Agent (core.py)
- result_validator -> output_validator decorator (tools.py)
- BASE_DIR was operating on the string literal "__name__" instead of
  __file__; all paths were relative to CWD and only worked in Docker
  because the entrypoint cd's into src/ first (consts.py)
- url_for('static', filename=...) -> path=... to match Starlette's
  StaticFiles param name; every other template already used path= (home.html)

Verified locally: all routes and static assets return 200.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-02 23:09:05 -05:00
78da74a2ab Merge pull request #9 from FJS-Services-Inc/dev_deploy
fix: migrate OpenAIModel to provider API (pydantic-ai 0.4.x)
2026-02-02 21:39:26 -05:00
db0786f60d fix: migrate OpenAIModel to provider API (pydantic-ai 0.4.x)
pydantic-ai 0.4.x removed the api_key kwarg from model constructors.
Pass credentials via OpenAIProvider instead.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-02 21:38:58 -05:00
e2df9fda79 Merge pull request #8 from FJS-Services-Inc/dev_deploy
fix: add default values for WORKERS, TIMEOUT, PORT in startup scripts
2026-02-02 21:29:40 -05:00
abad0c4d11 fix: add default values for WORKERS, TIMEOUT, PORT in startup scripts
Gunicorn crashes on container start when these env vars are missing from
stack.env — -w receives no argument and exits immediately. Defaults now
match .env.example values (WORKERS=4, TIMEOUT=120, PORT=5051), consistent
with the bash default syntax already used in compose.yaml.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-02 21:29:16 -05:00
2f938de28d [Komodo] fsecada01: Write Stack File: update ./compose.yaml 2026-02-03 02:16:32 +00:00
1ee0712e25 [Komodo] fsecada01: Write Stack File: update ./compose.yaml 2026-02-03 02:15:50 +00:00
3652f96539 Merge pull request #7 from FJS-Services-Inc/dev_deploy
chore: sync dev_deploy to main
2026-02-02 21:09:00 -05:00
43bb7684c1 chore: sync rename commit from main 2026-02-02 20:53:23 -05:00
9e2f79d455 docs: rename project to StrategIQ
Rename GitHub repo from Pygentic-AI to strategiq. Update all
user-facing references: README badges/links/prose, page <title>,
hero alt text, CLAUDE.md, system-prompt, justfile comments,
komodo deploy echo, SCSS header comments, and compiled CSS.

Intentionally left unchanged: Docker registry image name
(s3docker.francissecada.com/pygentic_ai), container paths
(/opt/pygentic_ai), DB schema, CSS filenames, Traefik labels,
and the production domain (pygenticai.francissecada.com) — all
operational names that would require infra changes.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-02 20:52:45 -05:00
0bf7a4ff1e Merge pull request #4 from FJS-Services-Inc/feature/tavily-multi-entity-swot
feat: Tavily web search + comparative multi-entity SWOT
2026-02-02 20:45:02 -05:00
b245cb9c5f Merge pull request #6 from FJS-Services-Inc/dev_deploy
chore: sync dev_deploy into main for branch parity
2026-02-02 20:39:03 -05:00
c55601c19b Merge pull request #5 from FJS-Services-Inc/main
chore: sync main into dev_deploy
2026-02-02 20:38:47 -05:00
3bcc93267f feat: add Tavily web search tool and comparative multi-entity SWOT
- Add `search_web` tool backed by tavily-python for grounded research
- Extend SWOT pipeline to accept a primary entity plus optional comparison
  entities; agent prompt drives comparative analysis when comparators are
  present
- Add `primary_entity` / `comparison_entities` fields to SwotAnalysis model
- Wire new form fields (primary + comparison inputs) through router →
  utils → run_agent
- Render entity badges ("vs" header) in result template; fix pre-existing
  bug where iterating model.dict() leaked the `analysis` string into the
  SWOT card grid
- Add comparison-input and entity-badge SCSS components; recompile CSS
- Pin requires-python >=3.12 in pyproject.toml; lock tavily-python 0.7.21

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-02 20:36:42 -05:00
1ec0884a39 Merge remote-tracking branch 'origin/prod_deploy' 2026-02-02 19:27:12 -05:00
ad1454ff3f Production Deployment Readiness
Some checks failed
Bandit / bandit (push) Has been cancelled
Docker Image CI / build (3.13) (push) Has been cancelled
 Local build verified successfully
 Environment variables complete  
 CI/CD workflows updated
 Build process simplified (source copy vs git clone)
 All production-readiness fixes applied

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-02 19:11:03 -05:00
0e99dd3fcf refactor: build from source instead of git clone
- Remove git clone in Dockerfile, use COPY instead
- Remove SSH agent setup from GitHub Actions workflow
- Remove --ssh and GIT_BRANCH build args
- Fix Unix line endings in shell scripts (dos2unix)
- Simplifies build process and removes SSH dependency
- Enables faster, more reliable CI/CD builds

 Local build tested and successful

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-02 19:08:47 -05:00
e491f79b45 fix: production deployment readiness improvements
- Fix deprecated GitHub Actions ::set-output syntax
- Add missing environment variables to .env.example
  - ANTHROPIC_API_KEY for Claude integration
  - FLOWER credentials for Celery monitoring
  - Server configuration (PORT, WORKERS, TIMEOUT)
  - Docker resource limits and configuration
- Update Dockerfile to use organization repository

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-02 17:25:39 -05:00
f6012feda1 fix: use forward slash in Claude system prompt path for cross-platform compatibility
Changed .claude\system-prompt.md to .claude/system-prompt.md
Forward slashes work on all platforms (Windows, Linux, macOS)
Also streamlined the start-claude output messages

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-02 16:49:31 -05:00
20f1798782 feat: add justfile task automation and multi-agent Claude orchestration
Task Automation (justfile):
- Comprehensive justfile with 50+ recipes for all common tasks
- Dynamic argument passing for Docker commands
- Organized categories: dev, docker, testing, deployment, cleanup
- Environment variable substitution for flexible configuration
- Health checks and monitoring commands
- Database migration helpers
- Quality check automation (lint, format, security, test)

Development Workflow:
- just setup - One-command project initialization
- just dev/celery/scss-watch - Multi-terminal dev environment
- just build/deploy - Docker operations with custom tags
- just test/lint/check - Quality assurance automation
- just clean/prune - Cleanup utilities

Multi-Agent Claude Orchestration:
- .claude/system-prompt.md - Multi-agent orchestration system
  * 5 specialized personas (🏗️ Architect, 🎨 Frontend, ⚙️ Backend, 🔒 Security, 🚀 DevOps)
  * MCP server routing patterns (Sequential, Context7, Magic, Playwright, Morphllm, Serena)
  * Task coordination workflows and decision frameworks
  * Code quality standards and project-specific patterns

- CLAUDE.md - Project initialization guide
  * Technology stack overview
  * Project structure documentation
  * Key workflows and commands
  * Environment configuration guide
  * Multi-persona activation patterns
  * Quick reference for common tasks

Documentation:
- Updated README.md with comprehensive justfile documentation
  * Installation instructions for just
  * Quick start guide using justfile
  * Complete command reference organized by category
  * Claude AI assistance section
  * Improved project overview and features
  * Development and production workflows

Benefits:
- Consistent command interface across all operations
- Reduced cognitive load - no need to remember complex Docker commands
- Self-documenting workflows with `just --list`
- Claude can intelligently coordinate across multiple domains
- Improved onboarding with clear documentation

Usage:
  just                  # List all commands
  just setup            # Initialize project
  just dev              # Start development
  just start-claude     # View Claude context

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-02 16:45:04 -05:00
b129cd4f0c feat: modernize deployment pipeline for Komodo integration
Production Deployment Improvements:
- Added .env.example template for secure credential management
- Modernized Docker build workflow with branch sanitization
- Created Komodo deployment trigger workflow
- Updated compose.yaml with environment variable substitution

GitHub Workflows:
- Updated docker-image.yml:
  - Add branch name sanitization (replace / with -)
  - Generate both date-tagged and -latest Docker tags
  - Upgrade to actions/checkout@v4
  - Add pull: true for layer caching

- New komodo-deploy.yml:
  - Triggers after successful Docker Image CI
  - Sends signed webhook to Komodo service
  - Extracts and sanitizes branch names
  - Requires secrets: KOMODO_HOST, KOMODO_STACK_ID_OR_NAME, KOMODO_WEBHOOK_SECRET

Docker Compose Modernization:
- Dynamic IMAGE_TAG with sensible defaults (main-latest)
- Configurable memory limits and reservations
- Environment variable substitution for all configs
- Added container names and restart policies
- Improved healthcheck with Host header
- Updated Traefik labels with Let's Encrypt cert resolver
- Added loadbalancer server URL configuration
- Explicit command paths for reliability

Security:
- .env.example provides safe template (no credentials)
- Actual .env remains in .gitignore (not tracked)

Pattern based on proven Formana deployment architecture.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-02 16:30:10 -05:00