Skip to content

ci(frontend): add npm run build + re-enable frontend lint (#646)#681

Merged
frankbria merged 2 commits into
mainfrom
fix/646-frontend-ci-build-lint
Jun 15, 2026
Merged

ci(frontend): add npm run build + re-enable frontend lint (#646)#681
frankbria merged 2 commits into
mainfrom
fix/646-frontend-ci-build-lint

Conversation

@frankbria

@frankbria frankbria commented Jun 15, 2026

Copy link
Copy Markdown
Owner

Summary

Closes #646. Two CI safety-net gaps for the actively-developed web UI:

  1. frontend-tests never ran npm run build — a TypeScript/build break passed PR CI and only failed at deploy time.
  2. Frontend lint was disabled in code-quality with a "legacy" comment contradicting CLAUDE.md (web UI is actively developed).

Changes

  • frontend-tests: add a npm run build step (fails the PR on a build break).
  • code-quality: re-enable frontend lint — adds Node setup + npm ci + npm run lint so lint fast-fails alongside ruff/mypy (placement confirmed per issue text).
  • Cleared 6 eslint errors (issue named 5; found one extra unused var):
    • CaptureGlitchModal.tsx — removed dead Input import (shipped code)
    • ProofDetailPage.test.tsx — removed unused ProofEvidenceSortCol, SortDir
    • GitHubIssueImportModal.test.tsx — removed unused within, integrationsApi
    • page.test.tsx — removed unused mockEvidenceResponse (extra error not named in issue)

Verification

  • npm run lint → 0 errors (exit 0)
  • npm run build → exit 0
  • npx jest on the 3 edited test files → 51 passed
  • test-summary needs still includes frontend-tests (unchanged)
  • Workflow YAML parses

Acceptance criteria

  • frontend-tests runs npm run build and fails the PR on a build break
  • Frontend lint runs in CI and is green
  • frontend-tests remains wired into test-summary

Known limitations

7 react-hooks/exhaustive-deps warnings remain in pre-existing components. ESLint exits 0 on warnings (no --max-warnings), so lint is green. Resolving them is behavioral and out of scope for this CI-hardening issue.

Summary by CodeRabbit

  • Tests
    • Enabled frontend linting checks in the continuous integration workflow
    • Added a production build verification step before running the Jest/coverage test suite
  • Chores
    • Cleaned up test files by removing unused fixtures/constants and unused type/imports
    • Updated test imports to reduce unnecessary dependencies while keeping existing mock behavior

frontend-tests CI ran coverage only — a TypeScript/build break passed
PR CI and surfaced only at deploy. Frontend lint was disabled in
code-quality with a "legacy" comment that contradicts CLAUDE.md.

- frontend-tests: add `npm run build` step (fails PR on build break)
- code-quality: re-enable frontend lint (Node setup + npm ci + npm run lint)
- clear 6 eslint errors (5 named in issue + an extra unused var in
  page.test.tsx): dead `Input` import in CaptureGlitchModal (shipped),
  unused imports/vars in three test files

frontend-tests remains wired into test-summary. 7 react-hooks/
exhaustive-deps warnings remain (non-failing) — out of scope.

Closes #646
@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 718dfde3-d118-4421-abc2-ab7c9b94b18b

📥 Commits

Reviewing files that changed from the base of the PR and between f853298 and 34b1493.

📒 Files selected for processing (1)
  • .github/workflows/test.yml
🚧 Files skipped from review as they are similar to previous changes (1)
  • .github/workflows/test.yml

Walkthrough

The CI workflow gains frontend lint in the code-quality job (Node.js setup, npm ci, npm run lint) and a npm run build step in the frontend-tests job. Five corresponding unused imports and one unused constant are removed from source and test files so the newly-enabled lint passes.

Changes

Frontend CI Lint Enablement and Fixes

Layer / File(s) Summary
CI workflow: enable frontend lint and add build step
.github/workflows/test.yml
Re-enables frontend lint in the code-quality job by adding Node.js setup, npm ci, and npm run lint; adds npm run build to frontend-tests before Jest runs.
Remove unused imports to satisfy lint
web-ui/src/components/proof/CaptureGlitchModal.tsx, web-ui/src/__tests__/components/proof/ProofDetailPage.test.tsx, web-ui/src/__tests__/components/tasks/GitHubIssueImportModal.test.tsx, web-ui/__tests__/app/proof/req_id/page.test.tsx
Removes the unused Input import from CaptureGlitchModal.tsx, unused ProofEvidenceSortCol/SortDir types from ProofDetailPage.test.tsx, unused within and direct integrationsApi imports from GitHubIssueImportModal.test.tsx, and the unused mockEvidenceResponse constant from the page test.

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~5 minutes

Possibly related PRs

  • frankbria/codeframe#576: Introduced or modified CaptureGlitchModal.tsx, directly overlapping with this PR's removal of the unused Input import from that same file.

Poem

🐇 Lint was sleeping, CI turned a blind eye,
Unused imports piled up, oh my oh my!
I hopped through the workflow, switched the config on,
Removed the dead Input before the break of dawn.
Now build runs first, lint checks every line —
✨ The frontend CI safety nets align!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: adding npm run build and re-enabling frontend lint, which directly addresses the PR's primary objectives.
Linked Issues check ✅ Passed All objectives from #646 are met: npm run build added to frontend-tests, frontend linting re-enabled in code-quality, and all 5+ ESLint errors cleared across test and shipped files.
Out of Scope Changes check ✅ Passed All changes are directly scoped to #646: CI workflow updates, ESLint error fixes, and unused import cleanup in test files. No extraneous modifications present.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/646-frontend-ci-build-lint

Comment @coderabbitai help to get the list of available commands and usage tips.

@claude

claude Bot commented Jun 15, 2026

Copy link
Copy Markdown

Multi-provider LLM support — PR #552 acceptance criteria

2026-04-05T21:34:07Z

PR #552 adds multi-provider LLM support so CodeFrame can use any OpenAI-compatible provider (Ollama, vLLM, GPT-4o) instead of being locked to Anthropic. This demo walks through each acceptance criterion.

AC 1 & 2: --llm-provider and --llm-model flags appear in cf work start --help

uv run codeframe work start --help 2>&1 | grep -A2 "\-\-llm"
 --cloud-timeout 45     codeframe work start abc123 --execute --llm-provider    
 openai --llm-model gpt-4o                                                      
                                                                                
╭─ Arguments ──────────────────────────────────────────────────────────────────╮
--
│ --llm-provider           TEXT                   LLM provider: anthropic,     │
│                                                 openai (default: anthropic   │
│                                                 or $CODEFRAME_LLM_PROVIDER)  │
│ --llm-model              TEXT                   Model name for the chosen    │
│                                                 provider (e.g. gpt-4o,       │
│                                                 qwen2.5-coder:7b,            │

Both --llm-provider and --llm-model flags are present with clear help text. Now checking the same on batch run:

uv run codeframe work batch run --help 2>&1 | grep -A2 "\-\-llm"
│ --llm-provider           TEXT                   LLM provider: anthropic,     │
│                                                 openai (default: anthropic   │
│                                                 or $CODEFRAME_LLM_PROVIDER)  │
│ --llm-model              TEXT                   Model name for the chosen    │
│                                                 provider (e.g. gpt-4o,       │
│                                                 qwen2.5-coder:7b)            │

AC 3: Default provider is still Anthropic when no flags are passed

uv run python3 -c "
from codeframe.adapters.llm import get_provider, AnthropicProvider, OpenAIProvider
# Verify the factory routes correctly without constructing real providers
import inspect
src = inspect.getsource(get_provider)
# Check default branch
assert \"anthropic\" in src
# Verify OpenAI-compatible set covers expected providers
from codeframe.adapters.llm import _OPENAI_COMPATIBLE
print(\"OpenAI-compatible providers:\", sorted(_OPENAI_COMPATIBLE))
print(\"Default: anthropic -> AnthropicProvider\")
print(\"openai/ollama/vllm/compatible -> OpenAIProvider\")
print(\"Factory routes correctly.\")
"
OpenAI-compatible providers: ['compatible', 'ollama', 'openai', 'vllm']
Default: anthropic -> AnthropicProvider
openai/ollama/vllm/compatible -> OpenAIProvider
Factory routes correctly.

AC 4: LLMConfig loads from .codeframe/config.yaml llm: block

uv run python3 -c "
import tempfile, pathlib, yaml
from codeframe.core.config import load_environment_config

# Simulate a workspace with an llm: block in config.yaml
with tempfile.TemporaryDirectory() as tmp:
    cfg_dir = pathlib.Path(tmp) / \".codeframe\"
    cfg_dir.mkdir()
    (cfg_dir / \"config.yaml\").write_text(
        \"llm:\n  provider: openai\n  model: qwen2.5-coder:7b\n  base_url: http://localhost:11434/v1\n\"
    )
    config = load_environment_config(pathlib.Path(tmp))
    print(\"provider:\", config.llm.provider)
    print(\"model:   \", config.llm.model)
    print(\"base_url:\", config.llm.base_url)
    print(\"LLMConfig loaded successfully.\")
"
provider: openai
model:    qwen2.5-coder:7b
base_url: http://localhost:11434/v1
LLMConfig loaded successfully.

AC 5: WorkerAgent uses LLMProvider abstraction — no AsyncAnthropic import

grep -n "import anthropic\|from anthropic\|AsyncAnthropic" \
  codeframe/agents/worker_agent.py \
  codeframe/agents/frontend_worker_agent.py \
  codeframe/agents/test_worker_agent.py 2>&1 || echo "No anthropic imports found — all clear."
No anthropic imports found — all clear.
uv run python3 -c "
from codeframe.adapters.llm import MockProvider
from codeframe.agents.worker_agent import WorkerAgent

# WorkerAgent accepts llm_provider param
provider = MockProvider(default_response=\"Task completed successfully\")
agent = WorkerAgent(
    agent_id=\"demo-001\",
    agent_type=\"backend\",
    provider=\"mock\",
    llm_provider=provider,
)
print(\"llm_provider type:\", type(agent.llm_provider).__name__)
print(\"No API key required when provider injected:\", agent._llm_provider is provider)
"
llm_provider type: MockProvider
No API key required when provider injected: True

AC 6: All tests pass

uv run pytest tests/adapters/test_llm.py tests/adapters/test_llm_async.py tests/adapters/test_llm_openai.py tests/agents/test_worker_agent.py tests/agents/test_worker_agent_provider.py tests/agents/test_frontend_worker_agent.py tests/agents/test_test_worker_agent.py tests/core/test_cli_llm_flags.py tests/core/test_config_llm.py -q --tb=line 2>&1 | tail -8
0.51s call     tests/core/test_cli_llm_flags.py::TestWorkStartLLMFlags::test_work_start_has_llm_provider_flag
0.33s call     tests/agents/test_frontend_worker_agent.py::TestTaskExecution::test_execute_task_success
0.30s call     tests/agents/test_test_worker_agent.py::TestTaskExecution::test_execute_task_basic
0.30s call     tests/agents/test_test_worker_agent.py::TestTestExecution::test_execute_passing_tests
0.26s call     tests/agents/test_test_worker_agent.py::TestTestExecution::test_execute_failing_tests
0.25s call     tests/agents/test_frontend_worker_agent.py::TestTaskExecution::test_execute_task_json_spec
0.24s call     tests/agents/test_frontend_worker_agent.py::TestTaskExecution::test_execute_task_with_websocket_broadcasts
======================== 153 passed in 73.18s (0:01:13) ========================

153 tests pass across all affected modules. All 6 acceptance criteria verified.

@claude

claude Bot commented Jun 15, 2026

Copy link
Copy Markdown

Review: CI Hardening and Lint Cleanup - test

@claude

claude Bot commented Jun 15, 2026

Copy link
Copy Markdown

WorktreeRegistry — Orphan Cleanup & get_base_branch (Issue #533)

2026-04-04T00:39:21Z

Issue #533 adds three things to the worktree isolation system:

  1. get_base_branch() — reads the actual current branch from git instead of hardcoding "main"
  2. WorktreeRegistry — atomically tracks live worktrees (PID + task_id) so orphaned ones can be detected
  3. Auto-cleanup — _execute_parallel() cleans stale worktrees at batch start; cf env doctor reports them

These complete the worktree isolation loop: create → register → execute → cleanup → unregister.

Acceptance Criterion 1: get_base_branch()

Returns the current branch name from git. Falls back to "main" on failure or in detached HEAD state.

uv run python -c "
from codeframe.core.worktrees import get_base_branch
from pathlib import Path

# Returns real branch name in a git repo
branch = get_base_branch(Path(\".\"))
print(f\"Current branch: {branch!r}\")

# Returns main for non-git directory
import tempfile
with tempfile.TemporaryDirectory() as tmp:
    fallback = get_base_branch(Path(tmp))
    print(f\"Non-git dir fallback: {fallback!r}\")

# Returns main for detached HEAD (simulated)
from unittest.mock import patch, MagicMock
mock = MagicMock(returncode=0, stdout=\"HEAD\n\")
with patch(\"subprocess.run\", return_value=mock):
    detached = get_base_branch(Path(\".\"))
    print(f\"Detached HEAD fallback: {detached!r}\")
"
Current branch: 'feat/worktree-registry-533'
Non-git dir fallback: 'main'
Detached HEAD fallback: 'main'

Acceptance Criterion 2: WorktreeRegistry — register / unregister / list_worktrees

Atomically writes PID + task_id + batch_id to .codeframe/worktrees.json using a threading.Lock and os.replace for crash safety.

uv run python -c "
import tempfile, os, json
from pathlib import Path
from codeframe.core.worktrees import WorktreeRegistry, list_worktrees

with tempfile.TemporaryDirectory() as tmp:
    p = Path(tmp)
    reg = WorktreeRegistry()

    # Register two tasks
    reg.register(p, \"task-001\", \"batch-abc\")
    reg.register(p, \"task-002\", \"batch-abc\")

    entries = list_worktrees(p)
    print(f\"Registered {len(entries)} entries:\")
    for e in entries:
        print(f\"  task={e[chr(39)+task_id+chr(39)]!r}  batch={e[chr(39)+batch_id+chr(39)]!r}  pid={e[chr(39)+pid+chr(39)]}\")

    # Unregister one
    reg.unregister(p, \"task-001\")
    after = list_worktrees(p)
    print(f\"After unregister: {[e[chr(39)+task_id+chr(39)] for e in after]}\")

    # Idempotent — re-registering same task does not duplicate
    reg.register(p, \"task-002\", \"batch-abc\")
    dup = list_worktrees(p)
    print(f\"After re-register (idempotent): {len(dup)} entries (expected 1)\")
"
Traceback (most recent call last):
  File "<string>", line 17, in <module>
    print(f"  task={e[chr(39)+task_id+chr(39)]!r}  batch={e[chr(39)+batch_id+chr(39)]!r}  pid={e[chr(39)+pid+chr(39)]}")
                              ^^^^^^^
NameError: name 'task_id' is not defined
Registered 2 entries:
uv run python /tmp/demo_registry.py
Registered 2 entries:
  task='task-001'  batch='batch-abc'  pid=64664
  task='task-002'  batch='batch-abc'  pid=64664
After unregister: ['task-002']
After re-register (idempotent): 1 entries (expected 1)

Acceptance Criterion 3: Stale detection — list_stale() and cleanup_stale()

A "stale" entry is one whose PID is no longer alive. Checked via os.kill(pid, 0). PermissionError (process alive, different owner) is correctly excluded.

uv run python /tmp/demo_stale.py
Stale entries: ['dead-task']
  (live-task excluded — its pid 64794 is our own process)
After cleanup_stale: ['live-task']

Acceptance Criterion 4: register() is wired into create_execution_context()

When IsolationLevel.WORKTREE is used, the ExecutionContext now registers the PID on creation and unregisters it in the cleanup callback — making the orphan detection actually functional.

grep -A 12 "if isolation == IsolationLevel.WORKTREE:" codeframe/core/sandbox/context.py
    if isolation == IsolationLevel.WORKTREE:
        from codeframe.core.worktrees import TaskWorktree, WorktreeRegistry, get_base_branch

        worktree = TaskWorktree()
        registry = WorktreeRegistry()
        base_branch = get_base_branch(repo_path)
        worktree_path = worktree.create(repo_path, task_id, base_branch=base_branch)
        registry.register(repo_path, task_id, batch_id="unknown")

        def cleanup() -> None:
            worktree.cleanup(repo_path, task_id)
            registry.unregister(repo_path, task_id)

Acceptance Criterion 5: Auto-cleanup in _execute_parallel()

cleanup_stale() is called at the start of every parallel batch run when isolation=worktree — clearing any orphaned worktrees from previously crashed workers before starting new ones.

grep -A 4 "Clean up orphaned worktrees" codeframe/core/conductor.py
    # Clean up orphaned worktrees from crashed workers on previous runs
    from codeframe.core.sandbox.context import IsolationLevel as _IL
    if batch.isolation == _IL.WORKTREE.value:
        from codeframe.core.worktrees import WorktreeRegistry
        WorktreeRegistry().cleanup_stale(workspace.repo_path)

Acceptance Criterion 6: cf env doctor reports stale worktrees

grep -A 14 "Stale worktree check" codeframe/cli/env_commands.py
    # Stale worktree check
    try:
        from codeframe.core.worktrees import WorktreeRegistry
        stale = WorktreeRegistry().list_stale(project_path)
        if stale:
            console.print()
            console.print("[bold yellow]Stale Worktrees:[/bold yellow]")
            for entry in stale:
                console.print(
                    f"  [yellow]⚠[/yellow] task [cyan]{entry['task_id']}[/cyan] "
                    f"(pid {entry.get('pid', '?')} no longer running)"
                )
            console.print()
            console.print(
                "[dim]To clean up, run:[/dim] codeframe work batch run --all-ready "

Acceptance Criterion 7: All 27 tests pass

uv run pytest tests/core/test_worktrees.py -q --tb=short 2>&1 | tail -5
0.02s call     tests/core/test_worktrees.py::TestTaskWorktreeCreate::test_returns_correct_path
0.01s call     tests/core/test_worktrees.py::TestTaskWorktreeCleanup::test_cleanup_nonexistent_does_not_raise
0.01s call     tests/core/test_worktrees.py::TestGetBaseBranch::test_returns_current_branch
0.01s call     tests/core/test_worktrees.py::TestBatchRunIsolate::test_defaults_to_true
============================== 27 passed in 1.02s ==============================

All 27 tests pass. The worktree isolation loop is now complete: create → register (PID) → execute → cleanup → unregister. Stale orphans from crashed workers are detected by PID liveness check and cleaned up at next batch run or reported by cf env doctor.

@claude

claude Bot commented Jun 15, 2026

Copy link
Copy Markdown

Review: CI Hardening + Lint Cleanup

This is a clean, well-scoped fix for two real CI gaps.

What is right:

  • Pinned action hashes for setup-node are consistent with existing patterns good supply-chain hygiene.
  • Build before tests in frontend-tests is the right order: build breaks fail fast without spending time running Jest.
  • Dead import cleanup across all 4 files is accurate no functional impact, just noise removal.
  • Reinstating the disabled lint correctly aligns CI with CLAUDE.md (web UI is actively developed, not legacy).

Minor concerns:

  1. npm run build has no NEXT_TELEMETRY_DISABLED env var. Next.js collects telemetry during builds unless opted out. In CI this is harmless but can introduce a small unpredictable delay. Adding 'NEXT_TELEMETRY_DISABLED: 1' to the build step env block would be clean. Not blocking.

  2. The 7 remaining react-hooks/exhaustive-deps warnings. Correctly acknowledged in the PR description (ESLint exits 0 on warnings). Worth tracking as a follow-up issue so they do not silently accumulate. Not required for this PR.

  3. npm run build adds cold-path time. A Next.js build on a cold runner typically adds 1-3 minutes to frontend-tests. Acceptable for the safety net it provides. If the job starts timing out in future, splitting build into its own parallel job is the natural next step.

Overall: Both CI gaps are real - build verification was missing and the lint disable comment was factually wrong per CLAUDE.md. The fix is minimal and correct. The dead-import cleanup is a nice bonus. Ready to merge.

@claude

claude Bot commented Jun 15, 2026

Copy link
Copy Markdown

Follow-up Review

My prior review raised three items. Checking the current diff against each:

1. NEXT_TELEMETRY_DISABLED missing from build step — resolved. The current diff includes it:

- name: Build (fail PR on TypeScript/build break)
  working-directory: web-ui
  env:
    NEXT_TELEMETRY_DISABLED: 1
  run: npm run build

Good catch and clean fix.

2. 7 remaining react-hooks/exhaustive-deps warnings — correctly deferred. These are pre-existing behavioral hooks and out of scope for this CI-hardening PR. Worth a follow-up issue to prevent silent accumulation.

3. npm run build adds CI time — accepted tradeoff. No change needed here.

All three items from the prior review are now either resolved or correctly acknowledged. The PR is correct and ready to merge.

@frankbria frankbria merged commit b2ced8f into main Jun 15, 2026
10 checks passed
@frankbria frankbria deleted the fix/646-frontend-ci-build-lint branch June 15, 2026 19:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[P6.5.1] frontend-tests CI: add npm run build + re-enable frontend lint

1 participant