ci(frontend): add npm run build + re-enable frontend lint (#646)#681
Conversation
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
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
WalkthroughThe CI workflow gains frontend lint in the ChangesFrontend CI Lint Enablement and Fixes
Estimated code review effort🎯 1 (Trivial) | ⏱️ ~5 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
Multi-provider LLM support — PR #552 acceptance criteria2026-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 --helpuv run codeframe work start --help 2>&1 | grep -A2 "\-\-llm"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"AC 3: Default provider is still Anthropic when no flags are passeduv 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.\")
"AC 4: LLMConfig loads from .codeframe/config.yaml llm: blockuv 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.\")
"AC 5: WorkerAgent uses LLMProvider abstraction — no AsyncAnthropic importgrep -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."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)
"AC 6: All tests passuv 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 -8153 tests pass across all affected modules. All 6 acceptance criteria verified. |
|
Review: CI Hardening and Lint Cleanup - test |
WorktreeRegistry — Orphan Cleanup & get_base_branch (Issue #533)2026-04-04T00:39:21Z Issue #533 adds three things to the worktree isolation system:
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}\")
"Acceptance Criterion 2: WorktreeRegistry — register / unregister / list_worktreesAtomically 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)\")
"uv run python /tmp/demo_registry.pyAcceptance 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.pyAcceptance 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.pyAcceptance 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.pyAcceptance Criterion 6: cf env doctor reports stale worktreesgrep -A 14 "Stale worktree check" codeframe/cli/env_commands.pyAcceptance Criterion 7: All 27 tests passuv run pytest tests/core/test_worktrees.py -q --tb=short 2>&1 | tail -5All 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. |
|
Review: CI Hardening + Lint Cleanup This is a clean, well-scoped fix for two real CI gaps. What is right:
Minor concerns:
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. |
Follow-up ReviewMy prior review raised three items. Checking the current diff against each: 1. - name: Build (fail PR on TypeScript/build break)
working-directory: web-ui
env:
NEXT_TELEMETRY_DISABLED: 1
run: npm run buildGood catch and clean fix. 2. 7 remaining 3. All three items from the prior review are now either resolved or correctly acknowledged. The PR is correct and ready to merge. |
Summary
Closes #646. Two CI safety-net gaps for the actively-developed web UI:
frontend-testsnever rannpm run build— a TypeScript/build break passed PR CI and only failed at deploy time.code-qualitywith a "legacy" comment contradicting CLAUDE.md (web UI is actively developed).Changes
frontend-tests: add anpm run buildstep (fails the PR on a build break).code-quality: re-enable frontend lint — adds Node setup +npm ci+npm run lintso lint fast-fails alongside ruff/mypy (placement confirmed per issue text).CaptureGlitchModal.tsx— removed deadInputimport (shipped code)ProofDetailPage.test.tsx— removed unusedProofEvidenceSortCol,SortDirGitHubIssueImportModal.test.tsx— removed unusedwithin,integrationsApipage.test.tsx— removed unusedmockEvidenceResponse(extra error not named in issue)Verification
npm run lint→ 0 errors (exit 0)npm run build→ exit 0npx jeston the 3 edited test files → 51 passedtest-summaryneedsstill includesfrontend-tests(unchanged)Acceptance criteria
frontend-testsrunsnpm run buildand fails the PR on a build breakfrontend-testsremains wired intotest-summaryKnown limitations
7
react-hooks/exhaustive-depswarnings 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