Skip to content

Commit 2ccec37

Browse files
committed
refactor: reorganize task commands and add auto-fix to pre-commit
- Delete founder/ commands (moved to ops/) - Add backend:db:* commands with clear dev/test cluster separation - Add qa:fix command for auto-fixing linting issues - Add qa:rules:check command for founder rules validation - Update pre-commit hook to auto-fix code quality on commit - Fix 170+ linting issues via biome check --write - Fix non-null assertions and empty type definitions - Update help text to reference ops:* instead of founder:* Commands now organized by clear boundaries: - backend/ → Database management (dev/test clusters) - frontend/ → Frontend development - ops/ → Service lifecycle management - qa/ → All testing and quality checks - shared/ → Cross-cutting utilities
1 parent 9d082fa commit 2ccec37

File tree

165 files changed

+1890
-1990
lines changed

Some content is hidden

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

165 files changed

+1890
-1990
lines changed

.cursor/Taskfile.yml

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,6 @@ includes:
1111
taskfile: ./commands/shared/Taskfile.yml
1212
dir: ./commands/shared
1313

14-
founder:
15-
taskfile: ./commands/founder/Taskfile.yml
16-
dir: ./commands/founder
17-
1814
backend:
1915
taskfile: ./commands/backend/Taskfile.yml
2016
dir: ./commands/backend
@@ -52,14 +48,14 @@ tasks:
5248
- echo "ScreenGraph Unified Automation System"
5349
- echo ""
5450
- echo "Common commands:"
55-
- echo " task founder:servers:start # Start backend + frontend"
56-
- echo " task founder:servers:stop # Stop all services"
57-
- echo " task founder:servers:status # Check service status"
58-
- echo " task qa:smoke:backend # Run backend smoke tests"
59-
- echo " task qa:smoke:frontend # Run frontend smoke tests"
60-
- echo " task founder:rules:check # Validate founder rules"
61-
- echo " node automation/scripts/env.mjs status # Show standard ports"
51+
- echo " task ops:servers:start - Start backend + frontend"
52+
- echo " task ops:servers:stop - Stop all services"
53+
- echo " task ops:servers:status - Check service status"
54+
- echo " task qa:smoke:backend - Run backend smoke tests"
55+
- echo " task qa:smoke:frontend - Run frontend smoke tests"
56+
- echo " task qa:rules:check - Validate founder rules"
57+
- echo " task backend:db:status - Show database cluster info"
6258
- echo ""
63-
- echo "Run 'task --list-all' for all available tasks"
59+
- echo "Run task --list-all for all available tasks"
6460
silent: true
6561

.cursor/commands/founder/Taskfile.yml

Lines changed: 0 additions & 69 deletions
This file was deleted.

.cursor/commands/qa/Taskfile.yml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,3 +138,21 @@ tasks:
138138
- lsof -ti:${APPIUM_PORT:-4723} 2>/dev/null | xargs kill -9 2>/dev/null || true
139139
- echo "✅ Appium server stopped"
140140
silent: false
141+
142+
# Rules validation
143+
rules:check:
144+
desc: "Validate all founder rules"
145+
cmds:
146+
- node ../../../automation/scripts/check-founder-rules.mjs
147+
silent: false
148+
149+
# Auto-fix code quality issues
150+
fix:
151+
desc: "Auto-fix linting and formatting issues"
152+
cmds:
153+
- echo "🔧 Auto-fixing backend..."
154+
- cd ../../../backend && bunx biome check --write .
155+
- echo "🔧 Auto-fixing frontend..."
156+
- cd ../../../frontend && bunx biome check --write .
157+
- echo "✅ Auto-fix complete"
158+
silent: false

.husky/pre-commit

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
#!/bin/sh
22
# Husky pre-commit hook
3-
# Validates founder rules before allowing commit
3+
# Auto-fixes and validates code before allowing commit
44

5-
cd .cursor && task founder:rules:check
5+
# Auto-fix linting and formatting
6+
echo "🔧 Auto-fixing code quality issues..."
7+
cd .cursor && task qa:fix
8+
9+
# Validate founder rules
10+
echo "📋 Validating founder rules..."
11+
cd .cursor && task qa:rules:check
612

713
# Cursor docs validation (informational, won't block commit)
814
cd .. && node automation/scripts/validate-cursor-docs.mjs || echo "⚠️ Cursor docs validation failed (non-blocking)"

backend/agent/adapters/appium/error-kinds.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,4 @@
1-
import {
2-
AppCrashedError,
3-
AppNotInstalledError,
4-
DeviceOfflineError,
5-
TimeoutError,
6-
} from "./errors";
1+
import { AppCrashedError, AppNotInstalledError, DeviceOfflineError, TimeoutError } from "./errors";
72

83
/**
94
* DriverErrorKind enumerates normalized error categories surfaced by Appium adapters.
@@ -41,4 +36,3 @@ export function mapAdapterErrorToDriverErrorKind(error: unknown): DriverErrorKin
4136

4237
return DriverErrorKind.INTERNAL_DRIVER_ERROR;
4338
}
44-

backend/agent/adapters/appium/webdriverio/app-lifecycle.adapter.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import type { ApplicationForegroundContext } from "../../../domain/entities";
2-
import type { AppLifecyclePort, AppLifecycleBudget } from "../../../ports/appium/app-lifecycle.port";
3-
import { AppNotInstalledError, AppCrashedError, TimeoutError } from "../errors";
4-
import { DriverErrorKind, mapAdapterErrorToDriverErrorKind } from "../error-kinds";
2+
import type {
3+
AppLifecycleBudget,
4+
AppLifecyclePort,
5+
} from "../../../ports/appium/app-lifecycle.port";
6+
import { type DriverErrorKind, mapAdapterErrorToDriverErrorKind } from "../error-kinds";
7+
import { AppCrashedError, AppNotInstalledError, TimeoutError } from "../errors";
58
import type { SessionContext } from "./session-context";
69

710
/**

backend/agent/adapters/appium/webdriverio/package-manager.adapter.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import type { PackageManagerPort, PackageInfo } from "../../../ports/appium/package-manager.port";
2-
import { AppNotInstalledError, TimeoutError, InvalidArgumentError } from "../errors";
1+
import type { PackageInfo, PackageManagerPort } from "../../../ports/appium/package-manager.port";
2+
import { AppNotInstalledError, InvalidArgumentError, TimeoutError } from "../errors";
33
import type { SessionContext } from "./session-context";
44

55
/**
@@ -73,7 +73,10 @@ export class WebDriverIOPackageManagerAdapter implements PackageManagerPort {
7373
* TimeoutError: If installation timed out
7474
* InvalidArgumentError: If APK is invalid or corrupted
7575
*/
76-
async installFromObjectStorage(ref: string, _expectedSha256?: string): Promise<{ packageId: string }> {
76+
async installFromObjectStorage(
77+
ref: string,
78+
_expectedSha256?: string,
79+
): Promise<{ packageId: string }> {
7780
try {
7881
// WebDriverIO will upload and install the APK located at ref (path accessible to Appium server)
7982
await this.context.driver.installApp(ref);
@@ -113,13 +116,13 @@ export class WebDriverIOPackageManagerAdapter implements PackageManagerPort {
113116
const output = typeof result === "string" ? result : JSON.stringify(result);
114117
// Look for a line like: "SHA-256 digest: ABCDEF..."
115118
const shaMatch = output.match(/SHA-256 digest:\s*([A-Fa-f0-9:]+)/);
116-
if (shaMatch && shaMatch[1]) {
119+
if (shaMatch?.[1]) {
117120
// Normalize to lowercase without colons
118121
return shaMatch[1].replace(/:/g, "").toLowerCase();
119122
}
120123
// Fallback: attempt to parse legacy signature lines
121124
const legacyMatch = output.match(/signatures=\[([A-Fa-f0-9:]+)\]/);
122-
if (legacyMatch && legacyMatch[1]) {
125+
if (legacyMatch?.[1]) {
123126
return legacyMatch[1].replace(/:/g, "").toLowerCase();
124127
}
125128
throw new InvalidArgumentError("Unable to parse SHA-256 signature from dumpsys output");
@@ -147,4 +150,3 @@ export class WebDriverIOPackageManagerAdapter implements PackageManagerPort {
147150
}
148151
}
149152
}
150-

backend/agent/adapters/appium/webdriverio/session.adapter.ts

Lines changed: 29 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1+
import log from "encore.dev/log";
12
import { remote } from "webdriverio";
3+
import { AGENT_ACTORS, MODULES } from "../../../../logging/logger";
24
import type { DeviceRuntimeContext } from "../../../domain/entities";
3-
import type { SessionPort, DeviceConfiguration } from "../../../ports/appium/session.port";
5+
import type { DeviceConfiguration, SessionPort } from "../../../ports/appium/session.port";
46
import { DeviceOfflineError, TimeoutError } from "../errors";
57
import type { SessionContext } from "./session-context";
6-
import log from "encore.dev/log";
7-
import { MODULES, AGENT_ACTORS } from "../../../../logging/logger";
88

99
interface RemoteOptions {
1010
hostname: string;
@@ -63,18 +63,18 @@ export class WebDriverIOSessionAdapter implements SessionPort {
6363

6464
/**
6565
* Lazily initialize the Appium session if not already created.
66-
*
66+
*
6767
* PURPOSE:
6868
* --------
6969
* Create the actual WebDriverIO session on first use (not during EnsureDevice).
7070
* Defers session creation until app context is available in ProvisionApp.
71-
*
71+
*
7272
* Args:
7373
* config: Device configuration (needed for session creation)
74-
*
74+
*
7575
* Returns:
7676
* SessionContext with active WebDriverIO driver
77-
*
77+
*
7878
* Raises:
7979
* DeviceOfflineError: If session creation fails
8080
* TimeoutError: If session creation times out
@@ -86,9 +86,9 @@ export class WebDriverIOSessionAdapter implements SessionPort {
8686
});
8787

8888
// If we already have a real driver, return existing context
89-
if (this.context?.driver && this.context.driver.sessionId) {
90-
logger.info("Session already initialized, reusing", {
91-
sessionId: this.context.driver.sessionId
89+
if (this.context?.driver?.sessionId) {
90+
logger.info("Session already initialized, reusing", {
91+
sessionId: this.context.driver.sessionId,
9292
});
9393
return this.context;
9494
}
@@ -154,14 +154,16 @@ export class WebDriverIOSessionAdapter implements SessionPort {
154154
logger.error("Failed to create Appium session", {
155155
error: error instanceof Error ? error.message : String(error),
156156
});
157-
157+
158158
if (error instanceof Error) {
159159
const errorMsg = error.message.toLowerCase();
160160
if (errorMsg.includes("timeout")) {
161161
throw new TimeoutError(`Session creation timeout: ${error.message}`);
162162
}
163163
}
164-
throw new DeviceOfflineError(`Failed to create session: ${error instanceof Error ? error.message : String(error)}`);
164+
throw new DeviceOfflineError(
165+
`Failed to create session: ${error instanceof Error ? error.message : String(error)}`,
166+
);
165167
}
166168
}
167169

@@ -204,7 +206,7 @@ export class WebDriverIOSessionAdapter implements SessionPort {
204206
if (this.context?.driver === null || (this.context && !this.context.driver.sessionId)) {
205207
logger.info("Lazy initialization: creating Appium session");
206208
await this.ensureSessionInitialized(config);
207-
209+
208210
if (!this.context) {
209211
throw new DeviceOfflineError("Session initialization failed");
210212
}
@@ -222,7 +224,7 @@ export class WebDriverIOSessionAdapter implements SessionPort {
222224
logger.info("Deferring session creation until app context is available");
223225

224226
const deviceRuntimeContextId = `pending-${Date.now()}`;
225-
227+
226228
// Store minimal context for later
227229
const capabilities = {
228230
platformName: config.platformName,
@@ -242,16 +244,17 @@ export class WebDriverIOSessionAdapter implements SessionPort {
242244
healthProbeStatus: "HEALTHY" as const,
243245
};
244246
} catch (error) {
245-
const errorDetails = error instanceof Error
246-
? {
247-
name: error.name,
248-
message: error.message,
249-
stack: error.stack,
250-
}
251-
: String(error);
252-
247+
const errorDetails =
248+
error instanceof Error
249+
? {
250+
name: error.name,
251+
message: error.message,
252+
stack: error.stack,
253+
}
254+
: String(error);
255+
253256
logger.error("ensureDevice failed", { error: errorDetails, config });
254-
257+
255258
if (error instanceof Error) {
256259
const errorMsg = error.message.toLowerCase();
257260
if (errorMsg.includes("econnrefused") || errorMsg.includes("econnreset")) {
@@ -261,7 +264,9 @@ export class WebDriverIOSessionAdapter implements SessionPort {
261264
throw new TimeoutError(`Connection timeout: ${error.message}`);
262265
}
263266
}
264-
throw new DeviceOfflineError(`Failed to ensure device: ${error instanceof Error ? error.message : String(error)}`);
267+
throw new DeviceOfflineError(
268+
`Failed to ensure device: ${error instanceof Error ? error.message : String(error)}`,
269+
);
265270
}
266271
}
267272

backend/agent/adapters/fakes/fake-llm.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import type { LLMPort } from "../../ports/llm";
21
import type { ActionCandidate, ActionDecision } from "../../domain/actions";
2+
import type { LLMPort } from "../../ports/llm";
33

44
/**
55
* FakeLLM offers deterministic LLM behavior for tests and demos.

backend/agent/adapters/fakes/fake-package-manager.adapter.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { PackageManagerPort, PackageInfo } from "../../ports/appium/package-manager.port";
1+
import type { PackageInfo, PackageManagerPort } from "../../ports/appium/package-manager.port";
22

33
/**
44
* Fake implementation of PackageManagerPort for testing.
@@ -20,7 +20,10 @@ export class FakePackageManagerAdapter implements PackageManagerPort {
2020
return this.installedPackages[packageId] || { installed: false };
2121
}
2222

23-
async installFromObjectStorage(ref: string, expectedSha256?: string): Promise<{ packageId: string }> {
23+
async installFromObjectStorage(
24+
ref: string,
25+
expectedSha256?: string,
26+
): Promise<{ packageId: string }> {
2427
// Extract package ID from ref (mock implementation)
2528
const packageId = ref.split("/").pop()?.replace(".apk", "") || "com.example.app";
2629
this.installedPackages[packageId] = {
@@ -38,4 +41,3 @@ export class FakePackageManagerAdapter implements PackageManagerPort {
3841
return "fake-signature-hash";
3942
}
4043
}
41-

0 commit comments

Comments
 (0)