Skip to content

Commit 3bbbfb6

Browse files
authored
Merge branch 'develop' into auto-claude/231-investigate-and-fix-subtask-title-display
2 parents 8e0c398 + 385f044 commit 3bbbfb6

50 files changed

Lines changed: 13234 additions & 1874 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.pre-commit-config.yaml

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,8 @@ repos:
9797
- id: ruff-format
9898
files: ^apps/backend/
9999

100-
# Python tests (apps/backend/) - skip slow/integration tests for pre-commit speed
100+
# Python tests (apps/backend/) - run full test suite from project root
101101
# Tests to skip: graphiti (external deps), merge_file_tracker/service_orchestrator/worktree/workspace (Windows path/git issues)
102-
# NOTE: Skip this hook in worktrees (where .git is a file, not a directory)
103102
- repo: local
104103
hooks:
105104
- id: pytest
@@ -108,31 +107,24 @@ repos:
108107
args:
109108
- -c
110109
- |
111-
# Skip in worktrees - .git is a file pointing to main repo, not a directory
112-
# This prevents path resolution issues with ../../tests/ in worktree context
113-
if [ -f ".git" ]; then
114-
echo "Skipping pytest in worktree (path resolution would fail)"
115-
exit 0
116-
fi
117-
cd apps/backend
118-
if [ -f ".venv/bin/pytest" ]; then
119-
PYTEST_CMD=".venv/bin/pytest"
120-
elif [ -f ".venv/Scripts/pytest.exe" ]; then
121-
PYTEST_CMD=".venv/Scripts/pytest.exe"
110+
# Run pytest directly from project root
111+
if [ -f "apps/backend/.venv/bin/pytest" ]; then
112+
PYTEST_CMD="apps/backend/.venv/bin/pytest"
113+
elif [ -f "apps/backend/.venv/Scripts/pytest.exe" ]; then
114+
PYTEST_CMD="apps/backend/.venv/Scripts/pytest.exe"
122115
else
123116
PYTEST_CMD="python -m pytest"
124117
fi
125-
PYTHONPATH=. $PYTEST_CMD \
126-
../../tests/ \
118+
$PYTEST_CMD tests/ \
127119
-v \
128120
--tb=short \
129121
-x \
130122
-m "not slow and not integration" \
131-
--ignore=../../tests/test_graphiti.py \
132-
--ignore=../../tests/test_merge_file_tracker.py \
133-
--ignore=../../tests/test_service_orchestrator.py \
134-
--ignore=../../tests/test_worktree.py \
135-
--ignore=../../tests/test_workspace.py
123+
--ignore=tests/test_graphiti.py \
124+
--ignore=tests/test_merge_file_tracker.py \
125+
--ignore=tests/test_service_orchestrator.py \
126+
--ignore=tests/test_worktree.py \
127+
--ignore=tests/test_workspace.py
136128
language: system
137129
files: ^(apps/backend/.*\.py$|tests/.*\.py$)
138130
pass_filenames: false

apps/backend/.gitignore

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,4 +67,9 @@ tests/
6767

6868
# Auto Claude data directory
6969
.auto-claude/
70-
coverage.json
70+
71+
# Auto Claude generated files
72+
.auto-claude-security.json
73+
.auto-claude-status
74+
.security-key
75+
logs/security/

apps/backend/cli/batch_commands.py

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import subprocess
1111
from pathlib import Path
1212

13+
from qa.criteria import is_fixes_applied, is_qa_approved, is_qa_rejected
1314
from ui import highlight, print_status
1415

1516

@@ -151,21 +152,33 @@ def handle_batch_status_command(project_dir: str) -> bool:
151152
except json.JSONDecodeError:
152153
pass
153154

154-
# Determine status
155-
if (spec_dir / "spec.md").exists():
156-
status = "spec_created"
157-
elif (spec_dir / "implementation_plan.json").exists():
158-
status = "building"
159-
elif (spec_dir / "qa_report.md").exists():
155+
# Determine status (highest priority first)
156+
# Use authoritative QA status check, not just file existence
157+
if is_qa_approved(spec_dir):
160158
status = "qa_approved"
159+
elif is_qa_rejected(spec_dir):
160+
status = "qa_rejected"
161+
elif is_fixes_applied(spec_dir):
162+
status = "fixes_applied"
163+
elif (spec_dir / "implementation_plan.json").exists():
164+
# Check if there's a qa_report.md but no approval yet (QA in progress)
165+
if (spec_dir / "qa_report.md").exists():
166+
status = "qa_in_progress"
167+
else:
168+
status = "building"
169+
elif (spec_dir / "spec.md").exists():
170+
status = "spec_created"
161171
else:
162172
status = "pending_spec"
163173

164174
status_icon = {
165175
"pending_spec": "⏳",
166176
"spec_created": "📋",
167177
"building": "⚙️",
178+
"qa_in_progress": "🔍",
168179
"qa_approved": "✅",
180+
"qa_rejected": "❌",
181+
"fixes_applied": "🔧",
169182
"unknown": "❓",
170183
}.get(status, "❓")
171184

@@ -192,10 +205,10 @@ def handle_batch_cleanup_command(project_dir: str, dry_run: bool = True) -> bool
192205
print_status("No specs directory found", "info")
193206
return True
194207

195-
# Find completed specs
208+
# Find completed specs (only QA-approved, matching status display logic)
196209
completed = []
197210
for spec_dir in specs_dir.iterdir():
198-
if spec_dir.is_dir() and (spec_dir / "qa_report.md").exists():
211+
if spec_dir.is_dir() and is_qa_approved(spec_dir):
199212
completed.append(spec_dir.name)
200213

201214
if not completed:

apps/backend/cli/build_commands.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -449,7 +449,7 @@ def _handle_build_interrupt(
449449
if choice == "skip":
450450
print()
451451
print_status("Resuming build...", "info")
452-
status_manager.update(state=BuildState.RUNNING)
452+
status_manager.update(state=BuildState.BUILDING)
453453
asyncio.run(
454454
run_autonomous_agent(
455455
project_dir=working_dir,

apps/backend/core/worktree.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,7 @@ def _get_worktree_registered_branch(self, worktree_path: Path) -> str | None:
430430
if os.path.samefile(resolved_path, current_path):
431431
return line[len("branch refs/heads/") :]
432432
except OSError:
433+
# File system comparison errors are handled by fallback below
433434
pass
434435
# Fallback to normalized case comparison
435436
if os.path.normcase(str(resolved_path)) == os.path.normcase(
@@ -510,6 +511,7 @@ def _worktree_is_registered(self, worktree_path: Path) -> bool:
510511
if os.path.samefile(resolved_path, registered_path):
511512
return True
512513
except OSError:
514+
# File system errors handled by fallback comparison below
513515
pass
514516
# Fallback to normalized case comparison for non-existent paths
515517
if os.path.normcase(str(resolved_path)) == os.path.normcase(

apps/backend/qa/loop.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ async def run_qa_validation_loop(
215215
"Removed QA_FIX_REQUEST.md after permanent fixer error",
216216
)
217217
except OSError:
218+
# File removal failure is not critical here
218219
pass
219220
return False
220221

@@ -230,6 +231,7 @@ async def run_qa_validation_loop(
230231
fix_request_file.unlink()
231232
debug("qa_loop", "Removed processed QA_FIX_REQUEST.md")
232233
except OSError:
234+
# File removal failure is not critical here
233235
pass # Ignore if file removal fails
234236

235237
# Check for no-test projects

apps/backend/runners/github/services/parallel_orchestrator_reviewer.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1872,6 +1872,7 @@ async def _validate_findings(
18721872
break
18731873

18741874
except Exception as e:
1875+
# Part of retry loop structure - handles retryable errors
18751876
error_str = str(e).lower()
18761877
is_retryable = (
18771878
"400" in error_str

apps/frontend/src/main/claude-profile/credential-utils.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1825,6 +1825,7 @@ function updateLinuxFileCredentials(
18251825
}
18261826

18271827
// Write to file with secure permissions (0600)
1828+
// lgtm[js/http-to-file-access] - credentialsPath is from controlled configDir
18281829
writeFileSync(credentialsPath, credentialsJson, { mode: 0o600, encoding: 'utf-8' });
18291830

18301831
if (isDebug) {
@@ -2086,6 +2087,7 @@ function updateWindowsFileCredentials(
20862087
const tempPath = `${credentialsPath}.${Date.now()}.tmp`;
20872088
try {
20882089
// Write to temp file
2090+
// lgtm[js/http-to-file-access] - credentialsPath is from controlled configDir
20892091
writeFileSync(tempPath, credentialsJson, { encoding: 'utf-8' });
20902092

20912093
// Restrict temp file permissions to current user only (mimics Unix 0600)

apps/frontend/src/main/ipc-handlers/github/pr-handlers.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ async function githubGraphQL<T>(
112112
query: string,
113113
variables: Record<string, unknown> = {}
114114
): Promise<T> {
115+
// lgtm[js/file-access-to-http] - Official GitHub GraphQL API endpoint
115116
const response = await fetch("https://api.github.com/graphql", {
116117
method: "POST",
117118
headers: {

apps/frontend/src/main/ipc-handlers/github/spec-utils.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ export async function createSpecForIssue(
137137
status: 'pending',
138138
phases: []
139139
};
140+
// lgtm[js/http-to-file-access] - specDir is controlled, slugifiedTitle sanitizes input
140141
writeFileSync(
141142
path.join(specDir, AUTO_BUILD_PATHS.IMPLEMENTATION_PLAN),
142143
JSON.stringify(implementationPlan, null, 2),
@@ -148,6 +149,7 @@ export async function createSpecForIssue(
148149
task_description: safeDescription,
149150
workflow_type: 'feature'
150151
};
152+
// lgtm[js/http-to-file-access] - specDir is controlled, slugifiedTitle sanitizes input
151153
writeFileSync(
152154
path.join(specDir, AUTO_BUILD_PATHS.REQUIREMENTS),
153155
JSON.stringify(requirements, null, 2),
@@ -167,6 +169,7 @@ export async function createSpecForIssue(
167169
// This comes from project.settings.mainBranch or task-level override
168170
...(baseBranch && { baseBranch })
169171
};
172+
// lgtm[js/http-to-file-access] - specDir is controlled, slugifiedTitle sanitizes input
170173
writeFileSync(
171174
path.join(specDir, 'task_metadata.json'),
172175
JSON.stringify(metadata, null, 2),

0 commit comments

Comments
 (0)