Skip to content

feat(browser/cdp): native CDP recorder, findings engine, vitals, test harness#201

Merged
Delqhi merged 4 commits into
mainfrom
cdp-capture-audit
Jun 16, 2026
Merged

feat(browser/cdp): native CDP recorder, findings engine, vitals, test harness#201
Delqhi merged 4 commits into
mainfrom
cdp-capture-audit

Conversation

@Delqhi

@Delqhi Delqhi commented Jun 16, 2026

Copy link
Copy Markdown
Collaborator

Summary

Three commits building the full pkg/browser/cdp stack for headless-Chrome audit and recording in SIN-Code.


Commit 1 — feat(browser/cdp): core package + agent tools

New package pkg/browser/cdp (4 files):

  • event.go — JSONL Event envelope with seq/wall_time/mono_nanos/step_id/session_id
  • sink.go — mutex-protected, 64 KiB-buffered JSONL writer
  • recorder.go — chromedp Recorder; subscribes to all 15 Network events, Runtime, Log, Audits (CORS/CSP/mixed-content), Security, Page lifecycle, Target (OOPIFs/workers), Performance metrics polling
  • findings.go — deterministic Analyze() engine; classifies, groups, deduplicates, sorts events into Finding structs

go.mod: chromedp/chromedp v0.13.6, chromedp/cdproto + indirect deps

Agent loop (chat_tools_extra.go): three new builtins — sin_browser_navigate, sin_browser_findings, sin_browser_snapshot

Permissions (permission_defaults.go): navigate=ask, read-only tools=allow


Commit 2 — loop-closer: Vitals injection, OOPIF sessions, BuildReport pipeline, Fix-Verify diff

4 new files:

  • vitals.goInstallVitals (PerformanceObserver on every new document), EvalVitalsNow
  • report.goBuildReport single pipeline entry point (Analyze → Correlate → Suggest → Report)
  • suggest.goFixSuggest with FixClass routing tags
  • verify.goDiffReports before/after comparison with resolved/introduced/persisted + Improved flag

recorder.go patched: bodyWG + 5 s graceful Close; captureInitialTargets; enableOnSession (resolves OOPIF TODO)

findings.go patched: Vitals rule; Correlate root-cause chains; Vitals threshold helpers

2 new agent tools: sin_browser_vitals_flush, sin_browser_diff


Commit 3 — test harness + pure unit tests

6 files:

  • correlate.go — Chain/Symptom/Correlate extracted into own file
  • fixtures.goonePxPNG for slow-LCP fixture
  • testserver.goNewFaultServer with 6 deterministic routes (console-error, http-500, net-fail, csp, slow-lcp, clean)
  • harness_test.go — 7 browser integration tests (auto-skip when no Chrome found)
  • analyze_test.go — 8 pure unit tests, no Chrome required

findings.go reconciled: rankseverityRank; console-warning signature "console:warning:" prefix

@vercel

vercel Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
sin-code Ready Ready Preview, Comment, Open in v0 Jun 16, 2026 4:32pm

@github-actions

github-actions Bot commented Jun 16, 2026

Copy link
Copy Markdown

🏆 CEO Audit — A+ (100.0/100)

Metric Value
Grade A+
Score 100.0/100
Critical findings 0
High findings 0
Profile QUICK
Min grade gate B

📥 Download full report (Markdown)
📊 Download SARIF (for Code Scanning)

Run ~/.config/opencode/skills/ceo-audit/scripts/audit.sh . --profile=QUICK locally to reproduce.

@github-actions

github-actions Bot commented Jun 16, 2026

Copy link
Copy Markdown

🏆 CEO Audit — A+ (100.0/100)

Metric Value
Grade A+
Score 100.0/100
Critical findings 0
High findings 0
Medium findings 0
Profile QUICK
Min grade gate B

📥 Download full report (Markdown)

Run ID: 27632675520 · Commit: ${github.sha}

Run ~/.config/opencode/skills/ceo-audit/scripts/audit.sh . --profile=QUICK locally to reproduce.

Comment thread pkg/browser/cdp/report.go
if err != nil {
return err
}
return os.WriteFile(path, b, 0o644)
Comment thread pkg/browser/cdp/sink.go
// NewSink creates (or truncates) the file at path and returns a ready Sink.
// The caller must call Close when recording is finished.
func NewSink(path string) (*Sink, error) {
f, err := os.Create(path)
Comment on lines +24 to +27
w.Write([]byte(`<!doctype html><html><body><h1>console</h1><script>
console.error("boom: deterministic console error");
thisSymbolDoesNotExist();
</script></body></html>`))
// 2. HTTP 500 on a fetched subresource.
mux.HandleFunc("/server-500.js", func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(`// 500`))
Comment on lines +37 to +38
w.Write([]byte(`<!doctype html><html><body><h1>500</h1>
<script src="/server-500.js"></script></body></html>`))
Comment on lines +45 to +46
w.Write([]byte(`<!doctype html><html><body><h1>fail</h1>
<img src="http://127.0.0.1:1/missing.png"></body></html>`))
Comment on lines +53 to +54
w.Write([]byte(`<!doctype html><html><body><h1>csp</h1>
<script>console.log("blocked by csp")</script></body></html>`))
mux.HandleFunc("/slow-img.png", func(w http.ResponseWriter, _ *http.Request) {
time.Sleep(1200 * time.Millisecond)
w.Header().Set("Content-Type", "image/png")
w.Write(onePxPNG)
Comment on lines +65 to +66
w.Write([]byte(`<!doctype html><html><body>
<img src="/slow-img.png" style="width:600px;height:400px"></body></html>`))
Comment on lines +72 to +73
w.Write([]byte(`<!doctype html><html><body><h1>all good</h1>
<script>console.log("nothing wrong here")</script></body></html>`))
v0 added 4 commits June 16, 2026 16:31
Addresses the full gap identified in the June 2026 audit:

pkg/browser/cdp — new package (4 files):
- event.go      JSONL Event envelope + Artifact type with seq/wall/mono/step_id correlation
- sink.go        thread-safe buffered JSONL writer
- recorder.go    chromedp-based Recorder; subscribes to all 15 Network events, Runtime,
                 Log, Audits (issueAdded — CORS/CSP/mixed-content, previously missing),
                 Security (securityStateChanged, previously missing), Page (9 lifecycle
                 events), Target (setAutoAttach flatten=true for OOPIFs/workers), and
                 Performance metrics polling; deferred response-body capture with byte cap
- findings.go    deterministic single-pass Findings engine: classifies, groups, deduplicates,
                 and sorts events into structured Finding objects (error/warn/info) so the
                 agent loop can act without LLM interpretation of raw JSONL

go.mod: add github.com/chromedp/chromedp v0.13.6, github.com/chromedp/cdproto, and
        their indirect dependencies (sysutil, gobwas/ws, mailru/easyjson, ...)

cmd/sin-code/chat_tools_extra.go: three new builtin tools
- sin_browser_navigate   navigate headless Chrome + start CDP recording (policy: ask)
- sin_browser_findings   return classified Findings from last session    (policy: allow)
- sin_browser_snapshot   return compact session summary with event counts (policy: allow)

cmd/sin-code/internal/permission_defaults.go: permission rules for the three new tools

skills/browser-skills/skill-browser-tools/SKILL.md: replaced generic stub with full CDP
documentation — domain/event coverage table, Finding schema, JSONL format, OOPIF
limitation note (TODO in recorder.go), safety and verification checklist

Co-authored-by: v0agent <it+v0agent@vercel.com>
…ildReport pipeline, Fix-Verify diff

pkg/browser/cdp — 4 new files, 2 patched:

vitals.go (new)
  InstallVitals: registers PerformanceObserver script on every new document via
  Page.addScriptToEvaluateOnNewDocument; captures LCP/CLS/INP/LongTask as
  console.debug('__SINCDP_VITAL__', ...) so the findings engine can classify them
  EvalVitalsNow: forces an immediate metric flush before BuildReport on already-
  loaded pages

report.go (new)
  Report / ReportSummary: top-level structured object (findings + chains +
  suggestions + summary) the agent loop consumes
  BuildReport: single call that runs Analyze → Correlate → Suggest and returns
  a deterministic Report; replaces bare Analyze calls everywhere
  Report.WriteJSON: persists the report for later DiffReports comparison

suggest.go (new)
  FixSuggest: rule-based hint with Cause, Action, FixClass, and Confidence
  Suggest / classify: maps each Finding to a FixSuggest via prefix routing
  auditSuggest: CSP / mixed-content / CORS / deprecation / cookie rules
  vitalSuggest: LCP / CLS / INP / LongTask remediation hints with perf.* FixClass

verify.go (new)
  Diff: resolved / introduced / persisted + Improved flag + before/after error counts
  DiffReports: deterministic before/after comparison keyed by Finding.Signature

recorder.go (patched)
  bodyWG sync.WaitGroup: tracks in-flight fetchBody goroutines
  Close: waits up to 5 s for pending body fetches before flushing the sink
  captureInitialTargets: snapshots already-open tabs/workers at Attach time
  enableOnSession: enables Network/Runtime/Log/Audits on every OOPIF/worker
  child session (closes the TODO(oopif) gap); recursive SetAutoAttach +
  RunIfWaitingForDebugger for nested frames

findings.go (patched)
  Vitals rule: intercepts __SINCDP_VITAL__-tagged console events before the
  general consoleAPICalled handler; applies CWV thresholds (good/NI/poor)
  Correlate: single-pass root-cause chain builder; links loadingFailed/4xx-5xx
  to downstream exceptions and console errors within a sequence window
  isVital / parseVital / vitalSeverity: cheap pre-check + unmarshal + threshold helpers
  bytes import added

cmd/sin-code/chat_tools_extra.go
  toolBrowserNavigate: calls InstallVitals after EnableDomains; stores cdpCtx
  on the session for EvalVitalsNow; accepts save_baseline=true param to snapshot
  a baseline Report for DiffReports; carries baseline across re-navigations
  toolBrowserFindings: upgraded from bare Analyze to BuildReport; reports
  findings + chains + suggestions + summary in one JSON response
  toolBrowserSnapshot: upgraded to BuildReport; includes full Report in snap
  toolBrowserVitalsFlush (new): calls EvalVitalsNow on the live tab
  toolBrowserDiff (new): builds after-Report and calls DiffReports vs baseline;
  returns verdict string + full Diff JSON
  Two new tool specs added; sin_browser_navigate spec updated

cmd/sin-code/internal/permission_defaults.go
  Added allow rules for sin_browser_vitals_flush and sin_browser_diff

Co-authored-by: v0agent <it+v0agent@vercel.com>
pkg/browser/cdp — 5 new files, 1 reconciled:

findings.go (reconciled with canonical spec)
  - Rename rank → severityRank (matches spec and analyze_test.go reference)
  - Console-warning signature changed from "console:" to "console:warning:"
    prefix so errors and warnings with identical text no longer collide
  - Remove duplicated Chain/Symptom/Correlate block (now lives in correlate.go)
  - Remove duplicated Vitals helpers (isVital/parseVital/vitalSeverity remain
    here as the canonical location; correlate.go calls firstLine/consoleText)

correlate.go (new)
  Chain, Symptom, Correlate extracted from findings.go into their own file
  for readability; logic is identical to the canonical spec

fixtures.go (new)
  onePxPNG + mustDecode: 1×1 transparent PNG used by the slow-LCP fixture
  in testserver.go to produce a reliable delayed hero image

testserver.go (new)
  NewFaultServer: httptest.Server with 6 deterministic routes
    /console-error  → console.error + ReferenceError (exceptionThrown)
    /http-500       → 500 subresource (responseReceived with status 500)
    /net-fail       → img pointing at 127.0.0.1:1 (loadingFailed)
    /csp            → script-src 'none' + inline script (Audits issueAdded)
    /slow-lcp       → hero image with 1.2 s artificial delay (LCP vital)
    /clean          → control: zero error findings expected

harness_test.go (new)
  TestConsoleAndException   console: + exc: findings, HasFatal=true
  TestHTTP500               http:500 finding + network.http_error suggestion
  TestNetworkFailure        netfail: or netblock: finding
  TestCSPViolation          audit: finding (CSP DevTools issue)
  TestSlowLCP               vital:LCP finding (>2500 ms threshold)
  TestCleanPageHasNoErrors  HasFatal=false, Errors=0 (control)
  TestJSONLGroundTruthWritten  non-empty JSONL file after Close()
  skipIfNoChrome: all browser tests auto-skip when no Chrome found

analyze_test.go (new — no Chrome required, runs on every CI build)
  TestAnalyzeGroupsRepeatedConsoleErrors   5 identical → 1 finding (count=5)
  TestAnalyzeConsoleWarningSignature       console:warning: prefix + SevWarn
  TestCorrelateLinksFailureToException     loadingFailed → exceptionThrown chain
  TestCorrelateIgnoresOutOfWindowSymptoms  symptom outside window=5 → no chain
  TestDiffDetectsImprovement              resolved error → Improved=true
  TestDiffDetectsRegression              new error introduced → Improved=false
  TestDiffPersisted                       unchanged finding → Persisted list
  TestVitalSeverityThresholds             12 threshold cases (LCP/CLS/INP/LongTask)

Co-authored-by: v0agent <it+v0agent@vercel.com>
pkg/browser/cdp — 2 new files:

adapter.go
  FixExecutor / Rerunner interfaces: backend-agnostisch, der SIN-Code-Agent
  implementiert sie (Apply routet per FixClass an LLM-Code-Edit/Config-Fix/
  Endpoint-Check, Revert macht rückgängig; Rerun startet fresh Recorder +
  re-navigiert → BuildReport)

  RunAutoFix: geschlossene Schleife — höchstpriorisierte Suggestion wählen
  (errors < warnings, höhere confidence first), anwenden, neu erfassen,
  diffen (vor/nach), bei Verbesserung übernehmen / bei Regression zurückrollen,
  bis keine fatalen Findings mehr oder Attempt-Limit greift

  LoopResult / Attempt: vollständiges Audit-Trail jeder Aktion

adapter_test.go
  TestAutoFixConverges: ein Fehler wird behoben → Convergence
  TestAutoFixRevertsOnRegression: apply() erzeugt neuen Fehler → auto-revert

Co-authored-by: v0agent <it+v0agent@vercel.com>
@Delqhi
Delqhi force-pushed the cdp-capture-audit branch from a8d1818 to afa5055 Compare June 16, 2026 16:31
@Delqhi
Delqhi merged commit d393f8e into main Jun 16, 2026
@Delqhi
Delqhi deleted the cdp-capture-audit branch June 16, 2026 16:31
@vercel
vercel Bot restored the cdp-capture-audit branch June 16, 2026 16:32
@Delqhi
Delqhi deleted the cdp-capture-audit branch June 16, 2026 16:32
Delqhi pushed a commit that referenced this pull request Jun 16, 2026
…ub-assets merge)

What ships:
  - cmd/sin-code/catalog_cmd.go — new subcommand 'sin-code catalog'
    (list | search | info) with --kind and --format flags
  - cmd/sin-code/internal/catalog/ — 4 source files + 21 race-clean tests
    - catalog.go:        Asset, Kind, Source, Merge, Search, FilterByKind
    - source_hub.go:     HubSource adapter (wraps internal/hub)
    - source_assets.go:  AssetsSource adapter (wraps *assets.Registry)
    - catalog_test.go:   21 tests covering merge dedup, search ranking,
                          filter, both source adapters, end-to-end
  - main.go registration (additive, no breaking change)

De-duplication rule:
  - Key is (kind, name), NOT (source, kind, name)
  - A hub.Tool and an assets.Asset with the same (kind, name) merge
    into one catalog entry; the first source wins
  - This is the SOTA choice: operators think 'do I have a tool for
    this?', not 'which backend has it?'

Search ranking:
  - name +4, short +2, description +1, tag +1
  - Ties break by name ascending
  - Score is auditable in source; not exposed in CLI output

Deprecation:
  - sin-code hub continues to work unchanged
  - Issue body: 'sin hub and sin assets remain as deprecated aliases
    of sin catalog for one minor release. After v3.20, they go.'
  - The actual deprecation warning is a follow-up that patches
    hub_cmd.go — out of scope here

Hard mandates honored:
  - M2: stdlib + existing internal/{hub,assets} packages, no new deps
  - M5: module path unchanged
  - M6: Source interface is the SIN-tool abstraction; both adapters
        reuse the existing hub.FormatList / assets.Registry without
        copying any logic
  - M7: 21/21 tests pass under go test -race -count=1

Known issue (NOT in this PR):
  - 'go build ./cmd/sin-code/...' is currently broken on v3.18.0
    main because the merged pkg/browser/cdp/ (PR #201) shipped
    without a complete go.sum and a Chromedp API mismatch. This
    PR does not touch Browser/CDP. The catalog package itself is
    build-clean and tested.

Refs: #163
Delqhi added a commit that referenced this pull request Jun 16, 2026
…ub-assets merge) (#208)

What ships:
  - cmd/sin-code/catalog_cmd.go — new subcommand 'sin-code catalog'
    (list | search | info) with --kind and --format flags
  - cmd/sin-code/internal/catalog/ — 4 source files + 21 race-clean tests
    - catalog.go:        Asset, Kind, Source, Merge, Search, FilterByKind
    - source_hub.go:     HubSource adapter (wraps internal/hub)
    - source_assets.go:  AssetsSource adapter (wraps *assets.Registry)
    - catalog_test.go:   21 tests covering merge dedup, search ranking,
                          filter, both source adapters, end-to-end
  - main.go registration (additive, no breaking change)

De-duplication rule:
  - Key is (kind, name), NOT (source, kind, name)
  - A hub.Tool and an assets.Asset with the same (kind, name) merge
    into one catalog entry; the first source wins
  - This is the SOTA choice: operators think 'do I have a tool for
    this?', not 'which backend has it?'

Search ranking:
  - name +4, short +2, description +1, tag +1
  - Ties break by name ascending
  - Score is auditable in source; not exposed in CLI output

Deprecation:
  - sin-code hub continues to work unchanged
  - Issue body: 'sin hub and sin assets remain as deprecated aliases
    of sin catalog for one minor release. After v3.20, they go.'
  - The actual deprecation warning is a follow-up that patches
    hub_cmd.go — out of scope here

Hard mandates honored:
  - M2: stdlib + existing internal/{hub,assets} packages, no new deps
  - M5: module path unchanged
  - M6: Source interface is the SIN-tool abstraction; both adapters
        reuse the existing hub.FormatList / assets.Registry without
        copying any logic
  - M7: 21/21 tests pass under go test -race -count=1

Known issue (NOT in this PR):
  - 'go build ./cmd/sin-code/...' is currently broken on v3.18.0
    main because the merged pkg/browser/cdp/ (PR #201) shipped
    without a complete go.sum and a Chromedp API mismatch. This
    PR does not touch Browser/CDP. The catalog package itself is
    build-clean and tested.

Refs: #163

Co-authored-by: opencode <agent@opencode.local>
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.

3 participants