Skip to content
This repository was archived by the owner on Jun 8, 2026. It is now read-only.

Commit d623034

Browse files
committed
FEA-1497: Merge main + fix build (Phase 0)
Phase 0 of the in-process agent monitor replacement (PR #264): make the integration branch current and compilable before any feature work. - Merge origin/main (175 commits) into feat/in-process-agent-database, bringing in FEA-1431 genai-prices pricing, FEA-1434 billing_mode, and the FEA-1435/1436 cost-reconciliation subsystem. - Resolve conflicts: - app.ts: keep both 264's in-process SQLite wiring (openAgentDatabase, desktop:db:* IPC handlers) and main's reconciliation subsystem imports/wiring; drop the broken HarnessImportService import, field, constructor, start(), and stop() — harness-parsers/ never existed on the branch and was the tsc build break. - preload.ts: union both sides — 264's db.* bridge plus main's flags / ingest-progress / managed-key-hint methods. - package.json: bump version to 0.15.105 (above main's 0.15.104); keep both @playwright/test (main) and @tailwindcss/* (264). - index.html: keep 264's clean Vite entry (drop the monolithic shell). - pnpm-lock.yaml: regenerated via pnpm install against the merged package.json (re-links vendored @closedloop-ai/design-system). - model_pricing is now absent from the entire desktop src (main's FEA-1431 removed it); the new-DB read path (src/main/database) never referenced it. The Phase 1 relay repoint stays out of scope. Testing: - just desktop-typecheck passes (tsc --noEmit, exit 0, zero errors) - git merge-base --is-ancestor origin/main HEAD holds after this commit - grep confirms zero model_pricing references in apps/desktop/src Risks: - Sidecar + iframe runtime still present (cutover is Phase 1); two SQLite DBs coexist by design until then. No external contract changed. - Scope limited to Phase 0; no write path, relay repoint, or feature work in this commit.
2 parents 9272a7e + 6f2da6b commit d623034

231 files changed

Lines changed: 40168 additions & 952 deletions

File tree

Some content is hidden

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

.gitignore

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,15 @@ dist-dmg/
1515

1616
# Generated agent-monitor runtime tree
1717
apps/desktop/.generated/
18+
.gstack/
19+
20+
# Playwright test artifacts (apps/desktop/test-e2e)
21+
apps/desktop/test-results/
22+
apps/desktop/playwright-report/
23+
24+
# UI Numbers Audit generated reports (FEA-1415 / PLN-738)
25+
# These embed a timestamp; regenerate locally via `pnpm --filter desktop audit:report`
26+
# or `node apps/desktop/test-e2e/agent-monitor/inventory/scan-tiles.mjs`.
27+
apps/desktop/test-e2e/agent-monitor/inventory/REPORT-*.md
28+
apps/desktop/test-e2e/agent-monitor/inventory/INVENTORY-DRAFT.md
29+
apps/desktop/test-e2e/agent-monitor/inventory/manifest.scanned.json

AGENTS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ Gateway route handlers live under `apps/desktop/src/server/operations/`.
5656
- When classifying failed spawned commands or gateway operations, inspect every captured output stream that can feed the user-facing excerpt or diagnostic payload, not only `stderr`. Add focused coverage for stdout-only and stderr-only failure markers when the classification depends on process output.
5757
- When adapting cloud relay command bodies before forwarding them to local gateway routes, preserve each route handler's request contract. Add focused coverage for every route whose body is transformed, especially when one route swaps credentials and another route must keep its original payload fields.
5858
- When a gateway request includes `localRepoPath`, never serialize or forward the raw request path into runtime context, environment, process args, or persisted metadata after sandbox policy rejects it. Only materialize the policy-approved resolved path returned by `tryAssertRepoAllowed`/`assertPathAllowed`, such as `expandedRepoPath`; optional-repo commands that ignore a rejected path must also omit it from `.closedloop-ai/context/*` files.
59+
- When parsing Git CLI path output, account for Git C-quoted paths and format-specific separators instead of using JSON parsing or unqualified string splits. Add focused coverage for non-ASCII filenames, quoted filenames containing separator text, and binary add/delete metadata when the route returns file status or diff shape.
60+
- Git diff and numstat output does not cover every status class, especially untracked files. When a gateway response exposes file stats for mixed tracked and untracked local changes, either supplement the missing untracked metadata or explicitly document zero-value semantics, and add route-level coverage for the chosen behavior.
5961

6062
## Testing Guidelines
6163
Tests run with `tsx --test` (Node test runner) via `just desktop-test`.

CLAUDE.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,32 @@ This rule does NOT apply to internal contracts that ship as a single unit with t
3434

3535
### Code Organization
3636
- **[mistake]**: When adding new operation files, do not copy-paste helper functions from existing files. Check for shared modules first. The `json()` response helper was duplicated across 33 files before being extracted into `response-utils.ts`.
37+
- **[mistake]**: For agent-monitor harness, parser, and build code, extract shared helpers into `agent-monitor-shared`, `parser-utils`, or nearby shared modules instead of duplicating per harness. (context: agent-monitor|duplication|helpers)
3738

3839
### Testing
3940
- **[mistake]**: When writing tests, check for existing shared test helpers (conftest, test_helpers, shared fixtures) before defining local helpers. Duplicated test setup functions drift silently when the shared contract changes. (context: tests|duplication|helpers)
41+
- **[mistake]**: Regression tests must assert the exact reviewed invariant, not only comments or a local surrogate path. (context: tests|coverage|regression)
4042

4143
### Code Quality
4244
- **[mistake]**: When adding code comments, verify they describe the current behavior — not a prior design or planned feature. Comments referencing non-existent files, removed fields, or superseded workflows mislead future readers. (context: comments|accuracy|stale)
4345
- **[mistake]**: Never fabricate history in changelogs, commit messages, or comments. Do not claim code "replaces" or "fixes" a prior implementation unless that implementation verifiably exists in the codebase or git history. (context: changelog|hallucination|fabrication)
4446
- **[mistake]**: Before adding a fallback or recovery path, verify the triggering condition can actually occur. Dead fallbacks that read from files never written or variables never set create false confidence in error handling. (context: dead-code|fallback|unreachable)
4547

48+
### Agent Monitor & Sidecar Security
49+
- **[mistake]**: Treat localhost sidecar routes and iframe messages as privileged surfaces. Mutating routes need origin/trusted-action guards, explicit target origins, and regression coverage. (context: agent-monitor|sidecar|security)
50+
51+
### Process Spawning & Secrets
52+
- **[mistake]**: Keep large or sensitive data out of spawned argv/env. Use stdin or files for prompts, quote shell args, set approved cwd, and pass minimal child environments. (context: spawn|argv|env|secrets)
53+
54+
### Boundary Validation
55+
- **[mistake]**: Runtime-validate gateway, IPC, and persisted payloads before path or file use. TypeScript casts and preload promise types do not protect missing or null fields. (context: validation|ipc|gateway)
56+
57+
### Generated Agent Monitor Runtime
58+
- **[pattern]**: When generated sidecar overlays, snippets, or patch inputs change, update stamp/materialization inputs and verify generated output so stale assets or bypassed patches cannot ship. (context: agent-monitor|generated|build)
59+
60+
### State & Lifecycle
61+
- **[mistake]**: Setting toggles must update persisted state and in-memory side effects together. Avoid one-way restart guards, stale tray state, or stale cloud presence. (context: settings|lifecycle|state)
62+
4663
## Commit Messages
4764

4865
Follow the format in `.gitmessage`. The subject line must be `<TICKET>: <description>` where TICKET is extracted from the branch name (e.g. `FEAT-68: add no-auth dev mode`). Include bullet-point body, Testing, and Risks sections.

apps/desktop/ci/README.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Desktop CI workflow staging
2+
3+
## `audit-gate.yml` — UI Numbers Audit Gate (FEA-1437 Phase 6)
4+
5+
This is the Phase 6 CI gate for the UI Numbers Audit. It lives here, **not** in
6+
`.github/workflows/`, only because the automation token that opened this PR
7+
lacks the GitHub `workflow` OAuth scope (pushing a file under
8+
`.github/workflows/` is rejected without it).
9+
10+
**To activate** (one step, needs a token/UI with `workflow` scope):
11+
12+
```bash
13+
git mv apps/desktop/ci/audit-gate.yml .github/workflows/audit-gate.yml
14+
git commit -m "ci: activate UI Numbers Audit gate"
15+
```
16+
17+
Until moved, the gate does not run automatically, but every check it performs is
18+
runnable locally and is identical to the existing `test:audit` suite:
19+
20+
```bash
21+
pnpm -C apps/desktop audit:coverage # static coverage gate (fast, no build)
22+
pnpm -C apps/desktop test:audit # full node-side audit
23+
pnpm -C apps/desktop test:audit:ui # headless Playwright tile audit
24+
```
25+
26+
The workflow has two jobs:
27+
28+
- **coverage** — runs `audit:coverage`: regenerates the tile scan + coverage
29+
classification, asserts every scanner detection is classified (no
30+
`needs_review`), asserts every harness parser has a `*-parser.contract.test.mjs`,
31+
and posts the `by_status` breakdown to the job summary. This is the merge gate
32+
that fires when a new route / tile / parser lands without manifest coverage.
33+
- **ui-audit** — builds the agent-monitor sidecar and runs the headless
34+
Playwright tile audits (rendered DOM == oracle).
35+
36+
Real-Electron and visual-regression suites are intentionally excluded (they run
37+
elsewhere as non-blocking, flaky-by-nature per PLN-760).
38+
39+
### Branch-protection guidance (read before marking required)
40+
41+
Mark **only the `coverage` job** as a required status check. Leave `ui-audit`
42+
optional (it's heavier — builds the sidecar + Playwright — and shouldn't block on
43+
infra flake). The workflow has **no `paths:` filter** by design: a required check
44+
combined with a paths filter deadlocks any PR that doesn't touch `apps/desktop/**`
45+
(the job never triggers, so the required status sits pending forever). Triggering
46+
on every PR keeps the required `coverage` check from hanging; the job is fast.

apps/desktop/ci/audit-gate.yml

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
name: UI Numbers Audit Gate
2+
3+
# FEA-1437 Phase 6 — gate PRs on the UI Numbers Audit. Two jobs:
4+
# coverage — fast, static: every scanner detection is classified (no
5+
# needs_review), every harness parser has a contract test. This
6+
# is the merge gate that fires when a new route / tile / parser
7+
# lands without manifest coverage.
8+
# ui-audit — heavier: builds the agent-monitor sidecar and runs the headless
9+
# Playwright tile audits (rendered DOM == oracle).
10+
# Real-Electron + visual-regression suites are intentionally NOT here; they run
11+
# elsewhere as non-blocking (flaky-by-nature) per PLN-760.
12+
#
13+
# NO `paths:` filter on purpose. The `coverage` job is intended to be a REQUIRED
14+
# status check, and "required check + paths filter" is a classic deadlock: a PR
15+
# that doesn't touch apps/desktop/** never triggers the job, so the required
16+
# status sits pending forever and the PR can't merge. Triggering on every PR
17+
# keeps the required check from hanging. The `coverage` job is fast; only mark
18+
# `coverage` as required (leave `ui-audit` optional — see apps/desktop/ci/README.md).
19+
20+
on:
21+
pull_request:
22+
types: [opened, synchronize, reopened]
23+
push:
24+
branches: [main]
25+
26+
concurrency:
27+
group: audit-gate-${{ github.event.pull_request.number || github.ref }}
28+
cancel-in-progress: true
29+
30+
jobs:
31+
coverage:
32+
name: Coverage gate (static)
33+
runs-on: ubuntu-latest
34+
timeout-minutes: 10
35+
permissions:
36+
contents: read
37+
packages: read
38+
steps:
39+
- uses: actions/checkout@v4
40+
- uses: pnpm/action-setup@v4
41+
- uses: actions/setup-node@v4
42+
with:
43+
node-version: 22
44+
cache: pnpm
45+
- name: Install dependencies
46+
run: pnpm install --frozen-lockfile
47+
env:
48+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
49+
- name: Generate build info
50+
run: pnpm -r prebuild
51+
- name: Audit coverage gate
52+
run: pnpm -C apps/desktop audit:coverage
53+
- name: Coverage summary -> job summary
54+
if: always()
55+
# Reporting only — never let a summary glitch fail the gate job.
56+
continue-on-error: true
57+
run: |
58+
COV=apps/desktop/test-e2e/agent-monitor/inventory/coverage.json
59+
if [ -f "$COV" ]; then
60+
{
61+
echo "### UI Numbers Audit — coverage"
62+
echo ""
63+
echo "| status | count |"
64+
echo "|---|---|"
65+
# Resolve to an absolute path: require() treats a bare relative
66+
# string as a node_modules specifier and would throw.
67+
node -e '
68+
const c = require(require("path").resolve(process.env.COV));
69+
for (const [k, v] of Object.entries(c.by_status)) console.log(`| ${k} | ${v} |`);
70+
console.log(`| **total** | **${c.total_detections}** |`);
71+
'
72+
} >> "$GITHUB_STEP_SUMMARY"
73+
fi
74+
env:
75+
COV: apps/desktop/test-e2e/agent-monitor/inventory/coverage.json
76+
77+
ui-audit:
78+
name: UI tile audit (headless Playwright)
79+
runs-on: ubuntu-latest
80+
timeout-minutes: 25
81+
permissions:
82+
contents: read
83+
packages: read
84+
steps:
85+
- uses: actions/checkout@v4
86+
- uses: pnpm/action-setup@v4
87+
- uses: actions/setup-node@v4
88+
with:
89+
node-version: 22
90+
cache: pnpm
91+
- name: Install dependencies
92+
run: pnpm install --frozen-lockfile
93+
env:
94+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
95+
- name: Generate build info
96+
run: pnpm -r prebuild
97+
- name: Install Playwright Chromium
98+
run: pnpm -C apps/desktop exec playwright install --with-deps chromium
99+
- name: Run headless tile audit
100+
run: pnpm -C apps/desktop test:audit:ui
101+
- name: Upload Playwright report on failure
102+
if: failure()
103+
uses: actions/upload-artifact@v4
104+
with:
105+
name: playwright-audit-report
106+
path: apps/desktop/test-results/
107+
retention-days: 7
108+
if-no-files-found: ignore

apps/desktop/package.json

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "desktop",
3-
"version": "0.15.57",
3+
"version": "0.15.105",
44
"description": "ClosedLoop Desktop",
55
"author": "ClosedLoop AI <support@closedloop.ai>",
66
"private": true,
@@ -20,13 +20,27 @@
2020
"stage:package": "node scripts/stage-packaging-app.mjs",
2121
"typecheck": "tsc -p tsconfig.json --noEmit",
2222
"lint": "eslint src/",
23-
"test": "tsx --test test/*.test.ts && node --test \"scripts/agent-monitor-packs/__tests__/*.test.js\"",
23+
"test": "tsx --test test/*.test.ts && node --test \"scripts/agent-monitor-packs/__tests__/*.test.js\" \"scripts/agent-monitor-pull-requests/__tests__/*.test.js\"",
24+
"pretest:contract": "pnpm build:agent-monitor",
25+
"test:contract": "node --test \"test-e2e/agent-monitor/specs/api-contract/*.test.mjs\"",
26+
"pretest:e2e": "pnpm build:agent-monitor",
27+
"test:e2e": "playwright test --config test-e2e/agent-monitor/playwright.config.ts",
28+
"pretest:audit": "pnpm build:agent-monitor && pnpm audit:scan && pnpm audit:classify",
29+
"audit:scan": "node test-e2e/agent-monitor/inventory/scan-tiles.mjs",
30+
"audit:classify": "node test-e2e/agent-monitor/inventory/coverage-classifier.mjs",
31+
"audit:coverage": "bash scripts/check-audit-coverage.sh",
32+
"test:audit": "node --test \"test-e2e/agent-monitor/specs/audit/*.test.mjs\"",
33+
"pretest:audit:ui": "pnpm build:agent-monitor",
34+
"test:audit:ui": "playwright test --config test-e2e/agent-monitor/playwright.audit.config.ts",
35+
"preaudit:report": "pnpm build:agent-monitor",
36+
"audit:report": "node test-e2e/agent-monitor/inventory/run-report.mjs",
2437
"package": "pnpm clean:package && pnpm build && pnpm stage:package && node scripts/run-electron-builder.mjs",
2538
"release": "pnpm clean:package && pnpm build && pnpm stage:package && node scripts/run-electron-builder.mjs --publish always"
2639
},
2740
"dependencies": {
2841
"@closedloop-ai/design-system": "file:vendor/design-system",
2942
"@closedloop-ai/loops-api": "0.2.11",
43+
"@pydantic/genai-prices": "0.0.62",
3044
"agent-dashboard": "github:hoangsonww/Claude-Code-Agent-Monitor#840c518d7fa69231de049e41b893938228b67e40",
3145
"busboy": "^1.6.0",
3246
"electron-log": "^5.4.3",
@@ -44,6 +58,7 @@
4458
"zod": "^4.0.0"
4559
},
4660
"devDependencies": {
61+
"@playwright/test": "^1.60.0",
4762
"@tailwindcss/postcss": "^4.3.0",
4863
"@tailwindcss/typography": "^0.5.19",
4964
"@tailwindcss/vite": "^4.3.0",

0 commit comments

Comments
 (0)