deepdebug-local-agent 1.0.23 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/context.md +76 -0
- package/.claude/skills/README.md +25 -0
- package/.claude/skills/tdd-debugging/SKILL.md +53 -0
- package/.claude/skills/tdd-debugging/references/anti-patterns.md +43 -0
- package/.claude/skills/tdd-debugging/references/command-reference.md +46 -0
- package/.claude/standards/README.md +51 -0
- package/.claude/standards/clean-code.md +75 -0
- package/.claude/standards/code-review-checklist.md +61 -0
- package/.claude/standards/layered-architecture.md +163 -0
- package/.claude/standards/node.md +45 -0
- package/.claude/standards/security-owasp.md +114 -0
- package/.claude/standards/simplicity.md +42 -0
- package/.claude/standards/solid.md +64 -0
- package/.claude/standards/testing.md +59 -0
- package/.cursor/rules/standards.mdc +17 -0
- package/.github/workflows/build-byoc-image.yml +315 -0
- package/.github/workflows/deploy-gcp.yml +102 -0
- package/.github/workflows/npm-publish.yml +69 -8
- package/AGENTS.md +21 -0
- package/CHANGELOG.md +69 -0
- package/CLAUDE.md +13 -0
- package/CLAUDE_CODE_AGENT_INFINITE.md +5 -3
- package/Dockerfile +4 -0
- package/Dockerfile.full +55 -3
- package/bin/install.js +16 -5
- package/cloudbuild-full.yaml +57 -0
- package/docker-compose.yml +5 -3
- package/docs/OBSERVABILITY.md +164 -0
- package/docs/byoc-agent-image.md +70 -0
- package/docs/graphify-packaging-plan.md +133 -0
- package/docs/graphify-validation-run-2026-10-28.md +243 -0
- package/docs/releasing.md +43 -0
- package/docs/signed-commands.md +163 -0
- package/env.example +30 -0
- package/helm/Chart.yaml +2 -2
- package/helm/templates/deployment.yaml +13 -3
- package/helm/templates/networkpolicy.yaml +22 -0
- package/helm/templates/secret.yaml +1 -0
- package/helm/values.yaml +13 -14
- package/mcp-server.js +4 -1
- package/package.json +9 -4
- package/scripts/upgrade-observability-push.sh +227 -0
- package/src/ai-engine.js +22 -22
- package/src/config/trusted-signing-keys.json +3 -0
- package/src/detectors/build-tool-detector.js +50 -0
- package/src/detectors/multi-project-detector.js +22 -22
- package/src/detectors/service-detector.js +24 -24
- package/src/exec-utils.js +26 -4
- package/src/git/base-git-provider.js +117 -117
- package/src/git/bitbucket-provider.js +2 -2
- package/src/git/git-provider-registry.js +18 -18
- package/src/git/github-provider.js +13 -13
- package/src/git/multi-repo-pr.js +161 -0
- package/src/mcp-http-server.js +32 -16
- package/src/observability/bundle-files.js +418 -0
- package/src/observability/detect.js +151 -0
- package/src/observability/provision.js +212 -0
- package/src/observability/render-cli.js +59 -0
- package/src/observability/routes.js +88 -0
- package/src/patch/patch-engine.js +28 -28
- package/src/patch-manager.js +85 -85
- package/src/patch_bkp.js +14 -14
- package/src/ports.js +4 -4
- package/src/routes/workspace.route.js +47 -16
- package/src/runtimes/base-runtime.js +75 -75
- package/src/runtimes/java/gradle-runtime.js +17 -17
- package/src/runtimes/java/java-integrations.js +25 -25
- package/src/runtimes/java/maven-runtime.js +34 -34
- package/src/runtimes/node/node-integrations.js +1 -1
- package/src/runtimes/node/npm-runtime.js +14 -14
- package/src/runtimes/node/yarn-runtime.js +4 -4
- package/src/runtimes/runtime-registry.js +25 -25
- package/src/server-local.js +4 -4
- package/src/server.js +1366 -333
- package/src/siblings/burst-suppressor.js +89 -0
- package/src/siblings/capabilities.js +273 -0
- package/src/siblings/env-compat.cjs +58 -0
- package/src/siblings/incident-forwarder.js +320 -0
- package/src/siblings/logger.js +15 -0
- package/src/siblings/metrics.js +94 -0
- package/src/utils/agent-auth.js +184 -0
- package/src/utils/approval-token.js +56 -0
- package/src/utils/bind-host.js +29 -0
- package/src/utils/branch-refspec.js +98 -0
- package/src/utils/child-env.js +34 -0
- package/src/utils/environment-diagnostics.js +55 -55
- package/src/utils/exec-policy.js +962 -0
- package/src/utils/exec-utils.js +15 -15
- package/src/utils/file-count.js +46 -0
- package/src/utils/fs-utils.js +10 -10
- package/src/utils/multi-repo-open.js +132 -0
- package/src/utils/open-gate.js +183 -0
- package/src/utils/ranged-read.js +65 -0
- package/src/utils/request-guards.js +77 -0
- package/src/utils/request-signing.js +342 -0
- package/src/utils/root-recovery.js +244 -0
- package/src/utils/safe-exec-args.js +58 -0
- package/src/utils/sandbox.js +182 -0
- package/src/utils/search-outcome.js +56 -0
- package/src/utils/workspace-root.js +80 -0
- package/src/validators/syntax-validator.js +149 -0
- package/src/workspace/detect-port.js +16 -16
- package/src/workspace/file-reader.js +1 -1
- package/src/workspace/process-manager.js +46 -46
- package/src/workspace-manager.js +86 -24
- package/test/adapters/registry.test.js +9 -2
- package/test/agent-auth.test.js +194 -0
- package/test/bind-host.test.js +26 -0
- package/test/branch-refspec.test.js +224 -0
- package/test/burst-suppressor.test.js +60 -0
- package/test/capabilities-access.test.js +47 -0
- package/test/capabilities.test.js +87 -0
- package/test/child-env.test.js +23 -0
- package/test/env-compat.test.js +71 -0
- package/test/exec-policy.test.js +463 -0
- package/test/file-count.test.js +64 -0
- package/test/incident-forwarder.test.js +231 -0
- package/test/multi-repo-open.test.js +180 -0
- package/test/multi-repo-pr.test.js +129 -0
- package/test/observability-detect.test.js +199 -0
- package/test/open-gate.test.js +341 -0
- package/test/ranged-read.test.js +60 -0
- package/test/request-guards.test.js +91 -0
- package/test/request-signing.test.js +284 -0
- package/test/root-recovery-sidecar.test.js +171 -0
- package/test/root-recovery.test.js +274 -0
- package/test/safe-exec-args.test.js +58 -0
- package/test/sandbox.test.js +98 -0
- package/test/search-outcome.test.js +50 -0
- package/test/workspace-root.test.js +64 -0
- package/tools/graphify-validation/README.md +92 -0
- package/tools/graphify-validation/config/questions.json +31 -0
- package/tools/graphify-validation/config/repos.json +9 -0
- package/tools/graphify-validation/config/symbols.json +6 -0
- package/tools/graphify-validation/fixtures/spring/expected.json +34 -0
- package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/AppConfig.java +29 -0
- package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/AuditLogger.java +15 -0
- package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/Cache.java +7 -0
- package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/CheckoutService.java +25 -0
- package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/InMemoryCache.java +18 -0
- package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/OrderController.java +22 -0
- package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/PaymentGateway.java +5 -0
- package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/PaypalGateway.java +11 -0
- package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/RefundService.java +14 -0
- package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/StripeGateway.java +11 -0
- package/tools/graphify-validation/lib/affected_vs_rg.py +81 -0
- package/tools/graphify-validation/lib/audit_hook/sitecustomize.py +22 -0
- package/tools/graphify-validation/lib/graph_metrics.py +96 -0
- package/tools/graphify-validation/lib/gv_common.py +130 -0
- package/tools/graphify-validation/lib/licenses.py +47 -0
- package/tools/graphify-validation/lib/pip_audit_summary.py +18 -0
- package/tools/graphify-validation/lib/recall.py +115 -0
- package/tools/graphify-validation/lib/report.py +226 -0
- package/tools/graphify-validation/lib/run_all.py +237 -0
- package/tools/graphify-validation/lib/run_graphify.py +144 -0
- package/tools/graphify-validation/lib/rusage_helper.py +17 -0
- package/tools/graphify-validation/lib/sample_edges.py +246 -0
- package/tools/graphify-validation/lib/spring_fixture.py +81 -0
- package/tools/graphify-validation/requirements-py311.lock +508 -0
- package/tools/graphify-validation/requirements-tools.in +1 -0
- package/tools/graphify-validation/requirements-tools.lock +371 -0
- package/tools/graphify-validation/requirements.in +1 -0
- package/tools/graphify-validation/requirements.lock +502 -0
- package/tools/graphify-validation/run.sh +43 -0
- package/tools/graphify-validation/tests/test_harness.py +151 -0
- package/cloudbuild-agent-qa.yaml +0 -43
- package/cloudbuild-agent-staging.yaml +0 -40
- package/cloudbuild.yaml.deprecated +0 -42
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# Client Observability — method & runbook
|
|
2
|
+
|
|
3
|
+
How the DeepDebug local agent brings **observability to a client that has none**.
|
|
4
|
+
It is the client-side delivery of the same stack we run for ourselves in
|
|
5
|
+
`vh-observability` (Grafana + Loki + Alloy + Alertmanager) — same building
|
|
6
|
+
blocks, different plumbing.
|
|
7
|
+
|
|
8
|
+
| | `vh-observability` (ours) | This bundle (client) |
|
|
9
|
+
| --- | --- | --- |
|
|
10
|
+
| Runs on | Cloud Run (multi-container) | Client VPS/host (docker-compose) |
|
|
11
|
+
| Log source | Cloud Logging → Pub/Sub (`gcplog`) | **`docker.sock`** (every container) |
|
|
12
|
+
| Loki storage | GCS bucket | **local filesystem volume** |
|
|
13
|
+
| Grafana login | Google Workspace OAuth | local admin (loopback-only) |
|
|
14
|
+
| Alerts → | gateway `/webhooks/alertmanager` (Bearer secret) | **same gateway webhook** |
|
|
15
|
+
|
|
16
|
+
Everything downstream of Loki is identical: the ruler evaluates a LogQL error
|
|
17
|
+
rule, Alertmanager posts firing alerts to the gateway (tenant resolved from the
|
|
18
|
+
trigger secret, sent as a Bearer header), and Grafana's logs dashboard has an **"Open in DeepDebug
|
|
19
|
+
Workbench"** data link into `/api/v1/webhooks/from-log`.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Architecture
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
All containers on the host
|
|
27
|
+
│ (docker.sock, read-only)
|
|
28
|
+
▼
|
|
29
|
+
Alloy ──push──▶ Loki (filesystem) ◀──query── Grafana (127.0.0.1:3000)
|
|
30
|
+
(discovery.docker, (chunks + tsdb in (provisioned datasource +
|
|
31
|
+
service = name, a local volume) dashboard, loopback only)
|
|
32
|
+
level parsed) │ ruler
|
|
33
|
+
▼
|
|
34
|
+
Alertmanager ──webhook──▶ gateway /api/v1/webhooks/alertmanager (Bearer <secret>)
|
|
35
|
+
│
|
|
36
|
+
incident (tenant from the secret) + Slack notify
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Four containers, one compose project (`deepdebug-observability`). Only Grafana
|
|
40
|
+
publishes a port, and only on `127.0.0.1` — reach it over an SSH tunnel, never
|
|
41
|
+
expose it to the internet.
|
|
42
|
+
|
|
43
|
+
## The error → workbench loop
|
|
44
|
+
|
|
45
|
+
- **Automatic** — Loki rule `ClientServiceErrors` fires on error rate → Alertmanager
|
|
46
|
+
→ `POST /api/v1/webhooks/alertmanager` (`Authorization: Bearer <secret>`) → incident → Slack.
|
|
47
|
+
- **Manual** — the Logs panel data link → `GET /api/v1/webhooks/from-log?s=<secret>&…`
|
|
48
|
+
→ incident → 302 redirect to `<frontend>/incidents/<id>` (the PG-persisted id).
|
|
49
|
+
|
|
50
|
+
The tenant is always resolved from the trigger secret, never from the
|
|
51
|
+
caller — the secret IS the auth.
|
|
52
|
+
|
|
53
|
+
## Logs into the platform (Live logs + services)
|
|
54
|
+
|
|
55
|
+
Loki stays on the host for Grafana, but the platform cannot reach it (no inbound
|
|
56
|
+
port). Alloy therefore **also pushes** the same Docker stream to the gateway:
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
loki.process ──▶ otelcol.receiver.loki ──▶ batch ──▶ otelcol.exporter.otlphttp
|
|
60
|
+
POST <gateway>/api/v1/webhooks/logs/otlp (JSON, gzip, Authorization: Bearer <secret>)
|
|
61
|
+
──▶ Redis list vh.logs.push.<tenant> ──▶ monitor-service (provider SELF_HOSTED)
|
|
62
|
+
──▶ log_entries + live tail ──▶ Live logs (one service per container)
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
- Outbound HTTPS only; same `vht_` secret as the Alertmanager.
|
|
66
|
+
- The first push creates the tenant's `SELF_HOSTED` row in `tenant_log_configs`.
|
|
67
|
+
- Pushed logs are **not** promoted to incidents by the monitor (the Loki rule above
|
|
68
|
+
already does that) unless `MONITOR_PUSH_DETECT_ENABLED=true`.
|
|
69
|
+
- The gateway accepts OTLP **JSON** only (protobuf gets 415).
|
|
70
|
+
|
|
71
|
+
**Existing installs** (provisioned before this change): run
|
|
72
|
+
`scripts/upgrade-observability-push.sh` on the server (`--dry-run` first). It finds the
|
|
73
|
+
bundle, reads the gateway URL and secret from `alertmanager.yml`, probes the push endpoint,
|
|
74
|
+
adds the `otelcol.*` blocks, moves the secret to a Bearer header, validates both configs
|
|
75
|
+
with the stack's own images, restarts only `alloy` + `alertmanager` and rolls back on
|
|
76
|
+
failure. Idempotent.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## The zero-observability method
|
|
81
|
+
|
|
82
|
+
The agent only offers to provision when the host has **nothing** already shipping
|
|
83
|
+
logs/metrics. The decision is deliberately conservative: a false "they have it"
|
|
84
|
+
just means we skip; a false "they have nothing" would deploy on top of a real
|
|
85
|
+
stack. So we skip on ANY match.
|
|
86
|
+
|
|
87
|
+
**Signals** (matched case-insensitively against container image/name AND process
|
|
88
|
+
command lines): promtail, fluent-bit, fluentd, vector, filebeat, logstash,
|
|
89
|
+
otel-collector, telegraf, loki, elasticsearch, opensearch, victoriametrics,
|
|
90
|
+
prometheus, thanos, mimir, signoz, grafana, kibana, datadog, newrelic, splunk,
|
|
91
|
+
dynatrace, instana, sumologic, … (see `OBSERVABILITY_SIGNALS` in
|
|
92
|
+
`src/observability/detect.js`).
|
|
93
|
+
|
|
94
|
+
**Our own stack never counts** as the client already having observability: any
|
|
95
|
+
container whose compose project is `deepdebug-observability` is excluded, so
|
|
96
|
+
re-running detection after we provision still reports the client's baseline and
|
|
97
|
+
`up -d` stays idempotent.
|
|
98
|
+
|
|
99
|
+
### Step-by-step the agent performs (`provisionObservability`)
|
|
100
|
+
|
|
101
|
+
1. **Probe** the host — `docker ps` + process list → classify (above).
|
|
102
|
+
2. **Guard** — if the client already has observability and `force` is not set,
|
|
103
|
+
**stop** and report what was found. Do not touch their stack.
|
|
104
|
+
3. **Pre-flight** — verify `docker` and the compose plugin are present
|
|
105
|
+
(else return an actionable error).
|
|
106
|
+
4. **Render** the bundle for this tenant: inject `gatewayUrl`, the alertmanager
|
|
107
|
+
`triggerSecret`, and a static `environment` label into every template.
|
|
108
|
+
5. **Write** files to `~/.deepdebug/observability/deepdebug-observability/`
|
|
109
|
+
plus a `.env` (Grafana admin creds, `DDO_ENVIRONMENT`).
|
|
110
|
+
6. **Start** — `docker compose up -d --remove-orphans`.
|
|
111
|
+
7. **Report** — container status + the loopback Grafana URL and admin password.
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## Agent API
|
|
116
|
+
|
|
117
|
+
Called by the gateway (Setup/Onboarding agent) or by an operator directly.
|
|
118
|
+
|
|
119
|
+
### `GET /observability/status`
|
|
120
|
+
Probe + classify. No side effects.
|
|
121
|
+
```json
|
|
122
|
+
{ "ok": true, "hasObservability": false, "recommendation": "provision",
|
|
123
|
+
"found": [], "ownStackRunning": false,
|
|
124
|
+
"docker": { "docker": true, "compose": true }, "containersScanned": 6 }
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### `POST /observability/provision`
|
|
128
|
+
```jsonc
|
|
129
|
+
{
|
|
130
|
+
"gatewayUrl": "https://api.visionhammer.io", // no trailing slash needed
|
|
131
|
+
"triggerSecret": "vht_….", // tenant alertmanager secret
|
|
132
|
+
"environment": "production", // static label for this client
|
|
133
|
+
"grafanaUser": "admin", // optional
|
|
134
|
+
"grafanaPassword": "…", // optional (else generated + returned)
|
|
135
|
+
"force": false, // deploy even if a stack exists
|
|
136
|
+
"start": true // run `docker compose up -d`
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
Returns `409 { skipped: true, found: [...] }` when the client already has a stack
|
|
140
|
+
and `force` is not set. On success returns `installDir`, container `status`, and
|
|
141
|
+
`grafana: { url, user, password }`.
|
|
142
|
+
|
|
143
|
+
### `POST /observability/teardown`
|
|
144
|
+
`{ "purge": false }` → `docker compose down` (`purge:true` also removes volumes).
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## Access & security
|
|
149
|
+
|
|
150
|
+
- **Grafana** is bound to `127.0.0.1:3000`. Reach it with
|
|
151
|
+
`ssh -L 3000:127.0.0.1:3000 <host>` then open `http://localhost:3000`.
|
|
152
|
+
- The **trigger secret** is the only credential in the alertmanager webhook URL
|
|
153
|
+
and it authorises exactly one tenant. Rotate via the gateway trigger source.
|
|
154
|
+
- Alloy mounts `docker.sock` **read-only** and only tails logs.
|
|
155
|
+
- The generated Grafana admin password is reported once — rotate it, or pass
|
|
156
|
+
your own `grafanaPassword`.
|
|
157
|
+
|
|
158
|
+
## Where the bundle lives
|
|
159
|
+
|
|
160
|
+
The bundle is **embedded** in the agent (`src/observability/bundle-files.js`) so
|
|
161
|
+
it ships inside the `pkg` binary — there are no loose asset files to lose. Editing
|
|
162
|
+
that file changes what the agent deploys. `renderBundle()` is a pure function
|
|
163
|
+
(templated in → final files out) and is covered by
|
|
164
|
+
`test/observability-detect.test.js`.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# BYOC agent image (`local-agent-full`)
|
|
2
|
+
|
|
3
|
+
Customer-cloud agents (BYOC GCP Cloud Run and BYOC Azure Container Instances)
|
|
4
|
+
run the public image built from `Dockerfile.full`:
|
|
5
|
+
|
|
6
|
+
```
|
|
7
|
+
europe-west1-docker.pkg.dev/visionhammer/visionhammer-docker-public-eu/local-agent-full:<semver>
|
|
8
|
+
europe-west1-docker.pkg.dev/visionhammer/visionhammer-docker-public-eu/local-agent-full:<git-sha>
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
The managed per-tenant Cloud Run agent (gateway `AgentProvisioningService`) is
|
|
12
|
+
built separately per tenant and does not use this image.
|
|
13
|
+
|
|
14
|
+
## Publishing a version
|
|
15
|
+
|
|
16
|
+
1. Actions → **Build BYOC Agent Image** → *Run workflow* on `main` (version
|
|
17
|
+
defaults to `package.json`; or push a `v*` tag / publish a release on a
|
|
18
|
+
commit that is on `main`).
|
|
19
|
+
2. The workflow builds `Dockerfile.full`, smoke-tests it (`/health` public,
|
|
20
|
+
API returns 401 without `X-Agent-Token` when `LOCAL_AGENT_TOKEN` is set),
|
|
21
|
+
then pushes `:<semver>` and `:<git-sha>`. It refuses to overwrite an
|
|
22
|
+
existing `:<semver>` built from a different commit, and never moves an
|
|
23
|
+
existing `:<git-sha>` (a commit rebuilt under a new semver keeps its first
|
|
24
|
+
image under `:<git-sha>`; always pin `:<semver>@sha256:`). There is no floating tag.
|
|
25
|
+
3. Copy the pinned reference from the run summary
|
|
26
|
+
(`...local-agent-full:<semver>@sha256:<digest>`).
|
|
27
|
+
|
|
28
|
+
4. Optional supply chain (flag, off by default): tick `supply_chain` on the
|
|
29
|
+
dispatch (or set the repository variable `BYOC_SUPPLY_CHAIN=true` for tag and
|
|
30
|
+
release runs). The `supply-chain` job signs `:<semver>@sha256:<digest>` with
|
|
31
|
+
cosign keyless (GitHub OIDC), attaches an SPDX SBOM (syft) and a SLSA v1
|
|
32
|
+
provenance attestation, and verifies all three. Consumers verify with:
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
cosign verify \
|
|
36
|
+
--certificate-identity-regexp '^https://github.com/VISIONHAMMER/deepdebug-local-agent/' \
|
|
37
|
+
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
|
|
38
|
+
<image>@sha256:<digest>
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Use `cosign verify-attestation --type spdxjson` (or `slsaprovenance1`) for
|
|
42
|
+
the SBOM and the provenance. The provenance is generated inside this
|
|
43
|
+
workflow and signed with the workflow identity; it is not a
|
|
44
|
+
slsa-github-generator (isolated builder) attestation.
|
|
45
|
+
|
|
46
|
+
Fallback without GitHub Actions: `cloudbuild-full.yaml` (see header).
|
|
47
|
+
|
|
48
|
+
## Rolling it out (gateway)
|
|
49
|
+
|
|
50
|
+
Set `deepdebug.byoc.agent-image` in vh-api-gateway (env `BYOC_AGENT_IMAGE`) to
|
|
51
|
+
the pinned reference, or bump its default in `application.yml` via PR. Only
|
|
52
|
+
newly provisioned agents pick it up; existing ones keep their image until
|
|
53
|
+
re-provisioned from the platform (Cloud Engineer → provision again). Azure
|
|
54
|
+
container groups created before the TLS/token spec are recreated on re-provision.
|
|
55
|
+
|
|
56
|
+
## Runtime knobs relevant to BYOC
|
|
57
|
+
|
|
58
|
+
| Env | Meaning |
|
|
59
|
+
| --- | --- |
|
|
60
|
+
| `LOCAL_AGENT_TOKEN` | Enforce `X-Agent-Token` on every non-public route (gateway sets it = the per-workspace shared secret on ACI). |
|
|
61
|
+
| `AGENT_SHARED_SECRET` | Same secret; used for the agent's own in-process calls. |
|
|
62
|
+
| `LOCAL_AGENT_BIND_HOST` | Listen address. Default: `0.0.0.0` when a token (`LOCAL_AGENT_TOKEN`/`AGENT_SHARED_SECRET`) or Cloud Run is present, otherwise `127.0.0.1`. ACI sets `127.0.0.1`: only the Caddy TLS sidecar in the same container group can reach it. |
|
|
63
|
+
|
|
64
|
+
## One-time operator setup
|
|
65
|
+
|
|
66
|
+
- `github-deployer@visionhammer.iam.gserviceaccount.com` needs
|
|
67
|
+
`roles/artifactregistry.writer` on the `visionhammer-docker-public-eu` repository (europe-west1; today granted at project level)
|
|
68
|
+
(and `artifactregistry.reader` for the overwrite guard).
|
|
69
|
+
- The repository must stay public-read (`allUsers` → `artifactregistry.reader`) so
|
|
70
|
+
customer projects / ACI pull without credentials.
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# Graphify no `local-agent-full`: plano de empacotamento e checklist de licenças
|
|
2
|
+
|
|
3
|
+
Estado: **proposta** (tarefa T4-06 do plano GA do Local Agent, decisões D8, D11, D12, D14 e D18 do ADR-0017). Nada neste
|
|
4
|
+
documento está aplicado: nenhum Dockerfile, workflow ou `package.json` foi alterado. A implementação é a tarefa T4-07
|
|
5
|
+
(PR separado). Marcação: **[V]** verificado nesta sessão (comando e saída em `docs/graphify-validation-run-2026-10-28.md`),
|
|
6
|
+
**[N]** não verificado.
|
|
7
|
+
|
|
8
|
+
## 1. Decisões que este plano executa
|
|
9
|
+
|
|
10
|
+
| Decisão | Consequência técnica |
|
|
11
|
+
|---|---|
|
|
12
|
+
| D8: Graphify via PyPI, versão fixada, com hash | dependência `graphifyy==X.Y.Z`, sem fork; instalação só com `--require-hashes` |
|
|
13
|
+
| D11: GA 1.1.0 | a imagem `local-agent-full:1.1.0` é a primeira com o runtime; o `Dockerfile.full` e o Helm deixam de apontar para `1.0.0` e `:latest` (tarefa T4-07) |
|
|
14
|
+
| D12: cosign e SBOM obrigatórios | a imagem e o SBOM saem assinados na mesma release (secção 6) |
|
|
15
|
+
| D14: fallback ripgrep | a imagem inclui `ripgrep`; se o índice do grafo faltar ou estiver velho, o contexto vem de `rg` |
|
|
16
|
+
| D18: runtime pack multi-runtime | o runtime Graphify é um pack como os outros (Java, Swift): instalável e removível sem tocar no agente Node |
|
|
17
|
+
|
|
18
|
+
## 2. O que vai fixado, e como
|
|
19
|
+
|
|
20
|
+
### 2.1 Ficheiros de lock (já no harness)
|
|
21
|
+
|
|
22
|
+
`tools/graphify-validation/requirements.in` tem **uma** linha, `graphifyy[sql,terraform]==0.9.93`. Dois locks com hashes SHA-256
|
|
23
|
+
de todos os artefactos de cada versão:
|
|
24
|
+
|
|
25
|
+
- `requirements.lock`, Python 3.12 (venv do harness) [V];
|
|
26
|
+
- `requirements-py311.lock`, Python 3.11, a versão do Debian bookworm que o `Dockerfile.full` já instala (`python3`, `libpython3.11`) [V no ficheiro].
|
|
27
|
+
|
|
28
|
+
**Os locks não são intermutáveis [V]:** em 3.11 o resolvedor escolhe `networkx 3.6.1` e `numpy 2.4.6`; em 3.12, `networkx 3.7` e
|
|
29
|
+
`numpy 2.5.4`. A imagem usa o lock do Python que tiver. Para a mesma versão de Python, o conjunto de pacotes resolvido é o mesmo em
|
|
30
|
+
Linux x86_64, Linux aarch64 e macOS arm64 (`uv pip compile --python-platform ...`, comparado) [V]. O lock 3.11 foi instalado com
|
|
31
|
+
`--require-hashes --no-deps --only-binary=:all:` num venv Python 3.11 em macOS arm64 e deu o mesmo grafo (52 nós, 84 arestas) na fixture Spring [V];
|
|
32
|
+
a instalação em Linux (glibc, musl) **não foi corrida** [N] (sem Docker local).
|
|
33
|
+
|
|
34
|
+
Extras: só `sql` (`tree-sitter-sql`) e `terraform` (`tree-sitter-hcl`). **Não** entram `mcp`, `leiden` (puxa `graspologic`), nem extras de LLM
|
|
35
|
+
(`anthropic`, `openai`, `gemini`, `bedrock`, `ollama`...). Sem `leiden`, a deteção de comunidades usa o fallback do próprio Graphify;
|
|
36
|
+
as comunidades não são identificadores estáveis de qualquer modo (nota do piloto). O pacote instala o script `graphify-mcp`
|
|
37
|
+
mesmo sem o extra `mcp` [V, `entry_points.txt`]: **apagar `bin/graphify-mcp`** na imagem.
|
|
38
|
+
|
|
39
|
+
### 2.2 Instalação
|
|
40
|
+
|
|
41
|
+
```dockerfile
|
|
42
|
+
# proposta para Dockerfile.full (T4-07); não aplicada
|
|
43
|
+
ARG GRAPHIFY_LOCK=requirements-py311.lock
|
|
44
|
+
COPY tools/graphify-validation/${GRAPHIFY_LOCK} /tmp/graphify.lock
|
|
45
|
+
RUN python3 -m venv /opt/vh-graphify \
|
|
46
|
+
&& /opt/vh-graphify/bin/pip install --no-cache-dir --disable-pip-version-check \
|
|
47
|
+
--require-hashes --no-deps --only-binary=:all: -r /tmp/graphify.lock \
|
|
48
|
+
&& /opt/vh-graphify/bin/python -m compileall -q /opt/vh-graphify/lib \
|
|
49
|
+
&& rm -f /opt/vh-graphify/bin/graphify-mcp /tmp/graphify.lock
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Porquê cada flag: `--require-hashes` recusa qualquer ficheiro cujo hash não esteja no lock; `--no-deps` impede que o pip resolva
|
|
53
|
+
dependências por fora do lock; `--only-binary=:all:` proíbe sdists, logo **nenhum `setup.py` de terceiros corre no build** (a nota do piloto já
|
|
54
|
+
tinha visto que o `pyproject` do Graphify é setuptools puro [C]); `compileall` deixa o bytecode feito para o sistema de ficheiros só de leitura
|
|
55
|
+
(o Python não escreve `.pyc` com `PYTHONDONTWRITEBYTECODE=1`).
|
|
56
|
+
|
|
57
|
+
Tamanho medido do `site-packages`: 154 MB (Python 3.12, macOS arm64, com `pip`) e 124 MB (3.11, sem `pip`) [V]. Em Linux pode diferir [N]; o
|
|
58
|
+
limite proposto de delta de imagem é 400 MB (G9).
|
|
59
|
+
|
|
60
|
+
### 2.3 Política de subida
|
|
61
|
+
|
|
62
|
+
1. Só versões com mais de 7 dias no PyPI (cooldown contra pacote comprometido). A fixada, 0.9.93, tinha 8 dias a 2026-10-28 [V, data de upload no PyPI]; o
|
|
63
|
+
upstream já ia na 0.9.97 (publicada a 2026-10-27, **não escolhida de propósito**).
|
|
64
|
+
2. PR de subida: editar `requirements.in`, regenerar os dois locks (comandos no README do harness), **rever o diff do código Python do Graphify entre versões**
|
|
65
|
+
(o PyPI mostra 13 versões em 17 dias, de 0.9.84 a 0.9.97 [V]), repetir o harness (G1, G2, G7, G8) e anexar o `REPORT.md`.
|
|
66
|
+
3. Nunca `pip install graphifyy` sem lock, nem `--upgrade`, nem `latest`, em Dockerfile, instalador, Helm ou CI.
|
|
67
|
+
4. Mesma regra para o `pip-audit` (`requirements-tools.in/.lock`).
|
|
68
|
+
|
|
69
|
+
## 3. `pip-audit` e cadeia de fornecimento
|
|
70
|
+
|
|
71
|
+
- Local: `run.sh --pip-audit` corre `pip-audit --require-hashes --disable-pip --no-deps -r requirements.lock`. Resultado de 2026-10-28: **0 vulnerabilidades conhecidas em
|
|
72
|
+
32 pacotes** (base PyPI/OSV nessa data) [V]. O mesmo audit tem de correr para `requirements-py311.lock` [N: só o lock 3.12 foi auditado].
|
|
73
|
+
- CI (T4-07): job no PR que toca nos locks e job semanal agendado; falha com qualquer vulnerabilidade com versão corrigida disponível. Exceções só com ID, motivo e data de expiração
|
|
74
|
+
num ficheiro `pip-audit-ignore.txt` revisto no PR. Nota: o audit só vê vulnerabilidades **publicadas**; não substitui a revisão de diff da secção 2.3.
|
|
75
|
+
- Proveniência: confirmar se o `graphifyy` publica atestações PyPI (Trusted Publishing) [N]. Se publicar, verificar no job de subida; se não, registar o risco no ADR.
|
|
76
|
+
- A imagem corre **sem rede para o Graphify**: o `entrypoint` do pack não define proxies nem chaves, e o contentor de teste usa `--network none` (G9) [N].
|
|
77
|
+
|
|
78
|
+
## 4. Superfície permitida em runtime
|
|
79
|
+
|
|
80
|
+
O Local Agent chama o Graphify por **um wrapper** (`vh-graphify`, a escrever em T4-07), nunca diretamente:
|
|
81
|
+
|
|
82
|
+
| Permitido | Proibido (o wrapper recusa com exit 64) |
|
|
83
|
+
|---|---|
|
|
84
|
+
| `update <path>`, `query`, `affected`, `explain`, `path`, `god-nodes` | `label`, `cluster-only` sem `--no-label`, `add`, `clone`, `prs`, `watch`, `install`, `global`, `merge-graphs`, `graphify-mcp` |
|
|
85
|
+
|
|
86
|
+
- Ambiente do processo filho: `HOME` num diretório do volume de dados, `PATH`, `LANG`, `PYTHONHASHSEED=0`, `PYTHONDONTWRITEBYTECODE=1`. **Sem** chaves LLM, tokens nem proxies
|
|
87
|
+
(a CLI auto-deteta chaves e envia nomes de comunidade ao LLM, nota do piloto). O harness corre assim e o relatório regista as chaves passadas [V].
|
|
88
|
+
- Rede: o harness provou 0 eventos `socket.*` com a rede bloqueada nos três repos VH, a rede estava de facto bloqueada (sonda TCP falhou) e os únicos `subprocess.Popen` foram `git rev-parse HEAD` [V].
|
|
89
|
+
O wrapper deve manter o `git` no `PATH`.
|
|
90
|
+
- Um índice de cada vez (fila), limite de memória 1,5 GB e timeout por job. Medido: o `update` incremental chega a 1,13-1,17 GB no maior processo filho nos repos de ~1 700 ficheiros [V]; num VPS de 4 GB cabe, num de 2 GB não.
|
|
91
|
+
Em 2 vCPU 4 GB **não medido** [N].
|
|
92
|
+
- `graphify-out/` fica no volume de dados do cliente, fora do git, e entra na lista de limpeza/erasure. `git ls-files graphify-out` tem de dar 0; hoje o `visionhammer-agents@release/qa` tem 57 ficheiros versionados [V] (remoção = PR próprio no agents, `git rm -r --cached graphify-out`).
|
|
93
|
+
- Utilizador não-root, FS só de leitura exceto o volume de dados e `/tmp`.
|
|
94
|
+
|
|
95
|
+
## 5. Checklist de licenças
|
|
96
|
+
|
|
97
|
+
Inventário da instalação do harness (32 distribuições, `lib/licenses.py`) [V]: **32 permissivas, nenhuma copyleft, nenhuma desconhecida**.
|
|
98
|
+
|
|
99
|
+
| Componente | Licença (metadados) | Ficheiro de licença no wheel | Ação |
|
|
100
|
+
|---|---|---|---|
|
|
101
|
+
| graphifyy 0.9.93 | Apache-2.0 (+ `LICENSE-MIT`, `NOTICE`) | sim (`LICENSE`, `LICENSE-MIT`, `NOTICE`) | copiar os três para `/usr/share/doc/vh-graphify/`; incluir o `NOTICE` no `THIRD_PARTY_NOTICES` do release |
|
|
102
|
+
| networkx 3.7 / 3.6.1 | BSD-3-Clause | sim | notice |
|
|
103
|
+
| numpy 2.5.4 / 2.4.6 | `BSD-3-Clause AND 0BSD AND MIT AND Zlib AND CC0-1.0` (libs incluídas) | sim | notice com a lista das licenças incluídas |
|
|
104
|
+
| RapidFuzz 3.14.6, tree-sitter 0.25.2 (core) | MIT | sim | notice |
|
|
105
|
+
| 27 gramáticas `tree-sitter-*` instaladas | MIT (2 em Apache-2.0: `tree-sitter-elixir`, `tree-sitter-hcl`) | sim, **exceto `tree-sitter-groovy`** | `tree-sitter-groovy` é MIT nos metadados mas **o wheel não traz ficheiro de licença**: obter o texto do repositório upstream e anexar [N] |
|
|
106
|
+
| ripgrep (Debian) | Unlicense/MIT | pacote Debian | notice (D14) |
|
|
107
|
+
| Python 3.11 (Debian) | PSF | pacote Debian | notice |
|
|
108
|
+
|
|
109
|
+
Checklist antes de publicar:
|
|
110
|
+
|
|
111
|
+
- [ ] Gerar o SBOM CycloneDX da imagem final (inclui hashes e licenças de cada wheel) e anexar à release; é a fonte do `THIRD_PARTY_NOTICES`.
|
|
112
|
+
- [ ] Reter `LICENSE`, `LICENSE-MIT` e `NOTICE` do Graphify, sem os alterar (exigência do Apache-2.0 para redistribuição; o `package.json` do agente diz `Proprietary`, o que não impede redistribuir um componente Apache-2.0 com notice).
|
|
113
|
+
- [ ] Obter o texto de licença de `tree-sitter-groovy` e conferir que a gramática distribuída corresponde ao MIT declarado.
|
|
114
|
+
- [ ] Não incluir os extras `leiden` (graspologic), `neo4j`, `falkordb`, `postgres`, `google`, `video`, `office`, `pdf` sem nova revisão: não foram inventariados [N].
|
|
115
|
+
- [ ] Marca: o upstream promove o "Graphify Cloud" no README; a imagem e a documentação VH não usam o nome nem o logótipo comerciais, só o nome do pacote.
|
|
116
|
+
- [ ] Confirmar com quem decide (jurídico) que a redistribuição dentro de uma imagem proprietária cumpre os contratos de cliente. Isto é uma checklist técnica, **não parecer jurídico**.
|
|
117
|
+
- [ ] Repetir o inventário (`lib/licenses.py`) a cada subida de versão; qualquer licença nova fora de MIT/BSD/Apache/PSF/ISC bloqueia o PR.
|
|
118
|
+
- [ ] Decisão pendente do Edu: abandonar o fork `VISIONHAMMER/visionhammer-graphify` (recomendado no plano GA); se se mantiver, manter os cabeçalhos de licença e o `NOTICE` do upstream.
|
|
119
|
+
|
|
120
|
+
## 6. Assinatura e SBOM (D12)
|
|
121
|
+
|
|
122
|
+
- `syft <imagem> -o cyclonedx-json` no workflow de build; o SBOM vai como asset da release e é anexado à imagem com `cosign attach sbom` ou atestação `cosign attest --type cyclonedx`.
|
|
123
|
+
- `cosign sign` da imagem por digest (keyless via OIDC do GitHub Actions, ou chave no KMS; escolha por decidir [N]). O workflow `build-byoc-image.yml` hoje não assina [N, não verificado nesta tarefa].
|
|
124
|
+
- Verificação no instalador/Helm: `cosign verify` do digest antes de arrancar; o Helm deixa de usar `:latest` e passa a referenciar o digest.
|
|
125
|
+
- O hash do lock e o hash do SBOM do pack Graphify ficam no `metadata` do heartbeat para o gateway poder detetar versões não aprovadas (desenho em T4-07/T4-08) [N].
|
|
126
|
+
|
|
127
|
+
## 7. O que falta provar (antes de declarar G9)
|
|
128
|
+
|
|
129
|
+
1. `docker build -f Dockerfile.full` com o bloco da secção 2.2 e medir o delta de imagem (limite 400 MB).
|
|
130
|
+
2. `docker run --read-only --network none --user 10001 ... vh-graphify update /workspace` com sucesso, mesmo grafo que o harness (comparar `signature`).
|
|
131
|
+
3. Lock 3.11 em Linux x86_64 e aarch64; `pip-audit` sobre `requirements-py311.lock`.
|
|
132
|
+
4. `tree-sitter-groovy`: licença anexada.
|
|
133
|
+
5. Wrapper `vh-graphify` com teste de que cada comando proibido sai com 64 e que nenhuma chave LLM é herdada.
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
# Graphify: corrida do harness de validação, 2026-10-28
|
|
2
|
+
|
|
3
|
+
Corrida real de `tools/graphify-validation/run.sh --pip-audit` (T4-06, D8) contra `vh-api-gateway@release/qa`, `visionhammer-agents@release/qa` e
|
|
4
|
+
`deepdebug-local-agent@main` (SHAs na secção 0 do relatório). Máquina de desenvolvimento (macOS arm64, 10 CPUs, 16 GB), **não** um VPS de 2 vCPU / 4 GB.
|
|
5
|
+
Graphify `graphifyy==0.9.93` do PyPI, instalado com `--require-hashes`. Sem LLM, sem rede para o Graphify, sem acesso ao pgvector de PROD nem de QA.
|
|
6
|
+
|
|
7
|
+
**Conclusão curta: o Graphify continua "go com condições", e "Graphify validado" (D5) ainda não pode ser declarado.** Dos limiares propostos, nesta corrida
|
|
8
|
+
falham G1 (só no repo do Local Agent), G3, G4 (memória no incremental), G5 e G8 (versionamento no agents); passam os testes de rede e determinismo; G6, G9 e G10 não foram medidos.
|
|
9
|
+
|
|
10
|
+
## Leitura dos resultados (com as ressalvas que alteram a conclusão)
|
|
11
|
+
|
|
12
|
+
- **G1, extração.** gateway 99,81 % (3 ficheiros sem símbolos), agents 100 %, Local Agent 93,33 % (8 de 120: 7 são testes `node:test` só com código de topo, mais `src/workspace/git-client.js`, **causa não investigada**). Falha o limiar de 95 % só no Local Agent, num repo pequeno onde um ficheiro pesa 0,8 %.
|
|
13
|
+
- **G2, arestas.** INFERRED: gateway 12,22 %, agents 14,05 % (a 1 ponto do limiar de 15 %; a nota do piloto tinha 11,4 % e 13,6 % com a 0.9.86), Local Agent 1,68 %. A precisão da amostra vem de um **oráculo automático** (tipo declarado do recetor), não de revisão humana: gateway 94,6 % nas 56 decididas de 60, agents 100 % nas 53 decididas de 60, Local Agent 100 % em 7 (só 7 arestas INFERRED `calls` fora de testes). O oráculo reproduziu o falso positivo que o piloto cita (`webClientBuilder.build()` apontado para `RootAuditEvent.Builder`, 3 ocorrências na amostra do gateway), o que mostra que não aprova tudo. Fica `PARCIAL`: falta a revisão por 2 pessoas e as linhas `unknown` (construtores, chamadas encadeadas, `var`) estão por julgar nos CSV.
|
|
14
|
+
- **G3, Spring/DI (fixture sintética, 19 verificações).** Falha: interface→implementação 62,5 % (5/8). O grafo liga `StripeGateway implements PaymentGateway` e as chamadas à interface, mas **não liga quem chama a interface às implementações** (3 verificações "chega à implementação" falham). Injeção por Lombok/construtor por tipo: 4/4. `@Bean`: 2/4 (o método `@Bean` aparece como método comum; quem consome o bean não chega ao método). `@Qualifier`: 0/3 (ignorado). Confirma a condição do piloto "sem nós de bean". Consequência para o loop: o contexto de "quem é afetado por mudar `PaymentGateway.charge`" tem de juntar `implements` a mão (ou o fallback `rg`).
|
|
15
|
+
- **G4, desempenho.** A frio: gateway 19,5 s, agents 15,3 s, Local Agent 2,8 s, todos abaixo de 60 s; maior processo filho 440-445 MB (estimativa da soma da árvore: ~795 MB, que conta páginas partilhadas). **Incremental (1 ficheiro): 21,6 s e 18,7 s, maior filho 1,17 e 1,13 GB, acima do limiar de 1 GB** (o piloto tinha 1,06 GB). Confirma que o `update` incremental reconstrói tudo. Em 2 vCPU/4 GB **não medido**; o tempo deve subir, a memória do maior processo não deve descer.
|
|
16
|
+
- **G5, recall.**
|
|
17
|
+
- "Quem é afetado" (`affected`, profundidade 1, só fontes main, `rg -w` como referência): recall agregado 54,8 % no gateway, 76,8 % no agents, 87,5 % no Local Agent; o limiar é 90 %. A precisão contra `rg` ficou em 100 % no gateway e no Local Agent e 79,6 % no agents. Os piores casos são classes cujo uso é por métodos (`PolicyGate`: 10 % a profundidade 1, 15 % a profundidade 2): o nó da classe não recebe as chamadas aos seus métodos. Ou seja, a primitiva certa é `affected` **por método**, não por classe, e isso não foi medido aqui.
|
|
18
|
+
- recall@8 com 22 perguntas escritas à mão (`config/questions.json`; 8 gateway, 8 agents, 6 Local Agent): o grafo fica em 43,8 % (gateway), 62,5 % (agents) e 66,7 % (Local Agent). O baseline lexical **justo** (palavras tiradas só do texto da pergunta, `rg` insensível a maiúsculas) dá 87,5 %, 75,0 % e 91,7 %. As perguntas conceptuais sem nome de símbolo são o ponto fraco do grafo (gateway 25 %, agents 57,1 %), exatamente a lacuna que o piloto não tinha testado e que o D14 (fallback ripgrep) cobre. As perguntas que nomeiam o símbolo: grafo 100 %.
|
|
19
|
+
- A coluna `lexical_curated` (100 % nos três) usa palavras escolhidas por quem já sabia a resposta; **não é comparável** e está no relatório só como teto.
|
|
20
|
+
- **pgvector não medido**: sem o dataset do `RagEvalController` e sem BD, a comparação "grafo ≥ pgvector − 5 pp" do G5 não pode ser feita. O harness aceita `--vector-results` para quando houver.
|
|
21
|
+
- O conjunto de 22 perguntas é pequeno e a verdade (ficheiros) foi verificada à mão por um só agente; serve para comparar backends, não para um número absoluto.
|
|
22
|
+
- **G7, segurança.** Com a rede negada (`sandbox-exec`, verificado por uma sonda TCP que falhou), os 3 repos terminaram com exit 0, **0 eventos `socket.*`** no hook de auditoria e só 2 `subprocess.Popen` (`git rev-parse HEAD`). `pip-audit`: 0 vulnerabilidades conhecidas nos 32 pacotes do lock 3.12 (não foi corrido no lock 3.11). Licenças: 32 permissivas, `tree-sitter-groovy` sem ficheiro de licença no wheel. `PARCIAL`: a lista de comandos desativados é requisito do wrapper, não foi testada.
|
|
23
|
+
- **G8, higiene.** Determinismo: duas corridas a frio dão a mesma impressão de nós/arestas nos 3 repos. `visionhammer-agents@release/qa` **tem 57 ficheiros `graphify-out/` versionados** (gateway e Local Agent: 0). O limiar exige 0, logo falha até haver o PR de remoção.
|
|
24
|
+
- **Versão.** O piloto mediu o fork 0.9.86; esta corrida mede o PyPI 0.9.93. Números próximos (tempos, INFERRED, grafos de ~17 000 nós), sem sinal de que o fork acrescente valor, o que sustenta D8.
|
|
25
|
+
|
|
26
|
+
## Não medido (e porquê)
|
|
27
|
+
|
|
28
|
+
| Item | Motivo |
|
|
29
|
+
|---|---|
|
|
30
|
+
| recall@8 contra pgvector (G5) | sem BD de QA nem dataset do `RagEvalController` |
|
|
31
|
+
| G6, incidentes ponta a ponta sem Voyage | exige agent2/LA com LLM |
|
|
32
|
+
| G9, imagem `local-agent-full` | sem Docker local; plano em `docs/graphify-packaging-plan.md` |
|
|
33
|
+
| G10, Python, SQL, Go, Terraform reais e repo do piloto | fora dos 3 repos pedidos; o piloto anterior só tinha fixtures sintéticas para Go e Terraform |
|
|
34
|
+
| 2 vCPU / 4 GB e hardware do piloto | só há esta máquina |
|
|
35
|
+
| Revisão manual de 2 pessoas das INFERRED | passo humano; CSV pronto |
|
|
36
|
+
| `pip-audit` do lock Python 3.11, instalação do lock em Linux | só macOS arm64 e lock 3.12 auditados |
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Relatório gerado (REPORT.md, sem edição manual)
|
|
41
|
+
|
|
42
|
+
Gerado por `tools/graphify-validation/run.sh`. Marcacao: PASSA/FALHA contra o limiar proposto; PARCIAL = medido mas falta um passo humano ou de ambiente; NAO MEDIDO = nao deu para medir aqui.
|
|
43
|
+
|
|
44
|
+
## 0. Ambiente e versoes
|
|
45
|
+
|
|
46
|
+
| Item | Valor |
|
|
47
|
+
|---|---|
|
|
48
|
+
| Data (UTC) | 2026-10-28T03:57:56Z |
|
|
49
|
+
| Graphify | graphifyy==0.9.93 (fixado por hash) |
|
|
50
|
+
| Python | Python 3.12.10 |
|
|
51
|
+
| SO / CPU / RAM | Darwin 25.3.0 arm64 / 10 CPUs / 16.0 GB |
|
|
52
|
+
| ripgrep | ripgrep 14.1.1 (rev 0ae49f8b6e) |
|
|
53
|
+
| Bloqueio de rede | sandbox-exec (deny network*); eficaz: True |
|
|
54
|
+
| Ambiente do processo | so HOME, LANG, NO_COLOR, PATH, PYTHONHASHSEED (sem chaves LLM nem credenciais) |
|
|
55
|
+
|
|
56
|
+
| Repo | Ref | SHA | Ficheiros de codigo |
|
|
57
|
+
|---|---|---|---|
|
|
58
|
+
| deepdebug-local-agent | origin/main | 854fa34cb8 | 120 |
|
|
59
|
+
| vh-api-gateway | origin/release/qa | ef42fba928 | 1552 |
|
|
60
|
+
| visionhammer-agents | origin/release/qa | 33a705cc9a | 1056 |
|
|
61
|
+
|
|
62
|
+
## 1. Resumo por criterio
|
|
63
|
+
|
|
64
|
+
| ID | Veredito | Medido |
|
|
65
|
+
|---|---|---|
|
|
66
|
+
| G1 | FALHA | deepdebug-local-agent 93.33% (8 sem simbolos); vh-api-gateway 99.81% (3 sem simbolos); visionhammer-agents 100.0% (0 sem simbolos) |
|
|
67
|
+
| G2 | PARCIAL | deepdebug-local-agent INFERRED 1.68%, precisao oraculo 100.0% (n=7, decididas 7); vh-api-gateway INFERRED 12.22%, precisao oraculo 94.6% (n=60, decididas 56); visionhammer-agents INFERRED 14.05%, precisao oraculo 100.0% (n=60, decididas 53); falta revisao manual por 2 pessoas |
|
|
68
|
+
| G3 | FALHA | interface->impl 62.5% (limiar 90%) |
|
|
69
|
+
| G4 | FALHA | deepdebug-local-agent frio 2.83s, arvore 635.4 MB, maior filho 94.6 MB; incremental 2.8s, arvore 635.7 MB, maior filho 121.0 MB; vh-api-gateway frio 19.47s, arvore 795.9 MB, maior filho 444.2 MB; incremental 21.64s, arvore 1093.5 MB, maior filho 1174.2 MB; visionhammer-agents frio 15.27s, arvore 793.7 MB, maior filho 439.7 MB; incremental 18.68s, arvore 1123.9 MB, maior filho 1134.7 MB; maquina de desenvolvimento (nao 2 vCPU/4 GB); soma de RSS da arvore acima de 1 GB em pelo menos uma corrida (pode contar paginas partilhadas) |
|
|
70
|
+
| G5 | FALHA | deepdebug-local-agent affected recall vs rg 87.5% (precisao 100.0%); deepdebug-local-agent recall@8 grafo 66.7% vs lexical (palavras da pergunta) 91.7%; vh-api-gateway affected recall vs rg 54.8% (precisao 100.0%); vh-api-gateway recall@8 grafo 43.8% vs lexical (palavras da pergunta) 87.5%; visionhammer-agents affected recall vs rg 76.8% (precisao 79.6%); visionhammer-agents recall@8 grafo 62.5% vs lexical (palavras da pergunta) 75.0%; pgvector NAO medido (sem BD de QA) |
|
|
71
|
+
| G6 | NAO MEDIDO | exige agent2/LA com LLM e incidentes reais; fora do harness |
|
|
72
|
+
| G7 | PARCIAL | deepdebug-local-agent: 0 eventos socket, exit 0; vh-api-gateway: 0 eventos socket, exit 0; visionhammer-agents: 0 eventos socket, exit 0; pip-audit: 0 vulnerabilidades conhecidas em 32 pacotes (exit 0); comandos de rede desativados = requisito de empacotamento, nao testado aqui |
|
|
73
|
+
| G8 | FALHA | determinismo igual em 3/3 repos; deepdebug-local-agent: 0 ficheiros graphify-out versionados; vh-api-gateway: 0 ficheiros graphify-out versionados; visionhammer-agents: 57 ficheiros graphify-out versionados |
|
|
74
|
+
| G9 | NAO MEDIDO | sem Docker local; ver docs/graphify-packaging-plan.md |
|
|
75
|
+
| G10 | NAO MEDIDO | Java e JS cobertos por repos VH; Python/SQL/Go/Terraform e repo do piloto fora deste conjunto |
|
|
76
|
+
|
|
77
|
+
## 2. Detalhe por repo
|
|
78
|
+
|
|
79
|
+
### deepdebug-local-agent
|
|
80
|
+
|
|
81
|
+
- Corrida a frio: 2.83s, pico de RSS da arvore 635.4 MB (estimativa; maior filho 94.6 MB), nos 1642, arestas 3283, graphify-out 3.2 MB.
|
|
82
|
+
- G1: 112/120 ficheiros de codigo com simbolos (93.33%); 8 sem simbolos, dos quais 7 em testes. Lista: src/workspace/git-client.js, test/bind-host.test.js, test/child-env.test.js, test/file-count.test.js, test/observability-detect.test.js, test/sandbox.test.js, test/search-outcome.test.js, test/adapters/registry.test.js
|
|
83
|
+
- G2: INFERRED 1.68% (1.69% so em fontes main); INFERRED por relacao {'calls': 7, 'indirect_call': 38, 'references': 10}.
|
|
84
|
+
- G8 determinismo: assinatura aab33c2415660429 vs aab33c2415660429, igual: True.
|
|
85
|
+
- Incremental (1 ficheiro alterado: bin/install.js): 2.8s, 635.7 MB; simbolo novo no grafo: True; sem alteracoes: 2.38s.
|
|
86
|
+
- G7 rede bloqueada (sandbox-exec (deny network*)): exit 0, 0 eventos socket, 2 subprocess.Popen (('git', ['git', 'rev-parse', 'HEAD'], '/private/tmp/claude-501/-Users-).
|
|
87
|
+
- G2 amostra (seed 20261028, n=7): oraculo ok/wrong/unknown {'ok': 7}; precisao nas decididas 100.0%; estrato ambiguo {'n': 0, 'ok': 0, 'precision_pct': None, 'unknown': 0, 'wrong': 0}; estrato outros {'n': 7, 'ok': 7, 'precision_pct': 100.0, 'unknown': 0, 'wrong': 0}. Revisao manual: 0 linhas.
|
|
88
|
+
|
|
89
|
+
`affected` (profundidade 1) contra `rg -w`, so fontes main:
|
|
90
|
+
|
|
91
|
+
| Simbolo | grafo | rg | ambos | so grafo | so rg | recall vs rg | precisao vs rg |
|
|
92
|
+
|---|---|---|---|---|---|---|---|
|
|
93
|
+
| confineToRoot (ok) | 2 | 3 | 2 | 0 | 1 | 66.7% | 100.0% |
|
|
94
|
+
| sanitizedChildEnv (ok) | 2 | 2 | 2 | 0 | 0 | 100.0% | 100.0% |
|
|
95
|
+
| verifySignature (ok) | 0 | 0 | 0 | 0 | 0 | n/d | n/d |
|
|
96
|
+
| evaluateCommand (ok) | 0 | 0 | 0 | 0 | 0 | n/d | n/d |
|
|
97
|
+
| resolveBindHost (ok) | 1 | 1 | 1 | 0 | 0 | 100.0% | 100.0% |
|
|
98
|
+
| validateHeaderRoot (ok) | 1 | 1 | 1 | 0 | 0 | 100.0% | 100.0% |
|
|
99
|
+
| BurstSuppressor (ok) | 1 | 1 | 1 | 0 | 0 | 100.0% | 100.0% |
|
|
100
|
+
| isSecretEnvName (ok) | 0 | 0 | 0 | 0 | 0 | n/d | n/d |
|
|
101
|
+
|
|
102
|
+
Agregado (profundidade 1): recall 87.5%, precisao 100.0% (5 simbolos); profundidade 2: recall 87.5%.
|
|
103
|
+
|
|
104
|
+
recall@8 no conjunto de referencia (6 perguntas):
|
|
105
|
+
|
|
106
|
+
| Backend | Tipo | n | recall@8 | acerto@8 |
|
|
107
|
+
|---|---|---|---|---|
|
|
108
|
+
| graph | all | 6 | 66.7% | 66.7% |
|
|
109
|
+
| graph | conceptual | 6 | 66.7% | 66.7% |
|
|
110
|
+
| graph_call_only | all | 6 | 66.7% | 66.7% |
|
|
111
|
+
| graph_call_only | conceptual | 6 | 66.7% | 66.7% |
|
|
112
|
+
| lexical_auto | all | 6 | 91.7% | 100.0% |
|
|
113
|
+
| lexical_auto | conceptual | 6 | 91.7% | 100.0% |
|
|
114
|
+
| lexical_curated | all | 6 | 100.0% | 100.0% |
|
|
115
|
+
| lexical_curated | conceptual | 6 | 100.0% | 100.0% |
|
|
116
|
+
|
|
117
|
+
`lexical_auto` usa so o texto da pergunta (baseline justo para o fallback ripgrep); `lexical_curated` usa palavras escolhidas por quem conhecia a resposta (teto otimista, nao comparar).
|
|
118
|
+
| vector (pgvector) | - | - | NAO MEDIDO | NAO MEDIDO |
|
|
119
|
+
|
|
120
|
+
### vh-api-gateway
|
|
121
|
+
|
|
122
|
+
- Corrida a frio: 19.47s, pico de RSS da arvore 795.9 MB (estimativa; maior filho 444.2 MB), nos 17654, arestas 71062, graphify-out 133.0 MB.
|
|
123
|
+
- G1: 1549/1552 ficheiros de codigo com simbolos (99.81%); 3 sem simbolos, dos quais 0 em testes. Lista: src/main/java/io/visionhammer/gateway/filter/RequestLoggingFilter.java, src/main/java/io/visionhammer/gateway/logs/api/LogsController.java, src/main/java/io/visionhammer/gateway/security/bo/package-info.java
|
|
124
|
+
- G2: INFERRED 12.22% (8.86% so em fontes main); INFERRED por relacao {'calls': 8263, 'references': 423}.
|
|
125
|
+
- G8 determinismo: assinatura 0bbbea2d3be0dc3d vs 0bbbea2d3be0dc3d, igual: True.
|
|
126
|
+
- Incremental (1 ficheiro alterado: src/main/java/io/visionhammer/gateway/admin/api/AdminInsightsController.java): 21.64s, 1093.5 MB; simbolo novo no grafo: True; sem alteracoes: 12.98s.
|
|
127
|
+
- G7 rede bloqueada (sandbox-exec (deny network*)): exit 0, 0 eventos socket, 2 subprocess.Popen (('git', ['git', 'rev-parse', 'HEAD'], '/private/tmp/claude-501/-Users-).
|
|
128
|
+
- G2 amostra (seed 20261028, n=60): oraculo ok/wrong/unknown {'ok': 53, 'unknown': 4, 'wrong': 3}; precisao nas decididas 94.6%; estrato ambiguo {'n': 20, 'ok': 16, 'precision_pct': 84.2, 'unknown': 1, 'wrong': 3}; estrato outros {'n': 40, 'ok': 37, 'precision_pct': 100.0, 'unknown': 3, 'wrong': 0}. Revisao manual: 0 linhas.
|
|
129
|
+
|
|
130
|
+
`affected` (profundidade 1) contra `rg -w`, so fontes main:
|
|
131
|
+
|
|
132
|
+
| Simbolo | grafo | rg | ambos | so grafo | so rg | recall vs rg | precisao vs rg |
|
|
133
|
+
|---|---|---|---|---|---|---|---|
|
|
134
|
+
| TenantAccessGuard (ok) | 21 | 21 | 21 | 0 | 0 | 100.0% | 100.0% |
|
|
135
|
+
| PolicyGate (ok) | 4 | 40 | 4 | 0 | 36 | 10.0% | 100.0% |
|
|
136
|
+
| EgressUrlGuard (ok) | 11 | 13 | 11 | 0 | 2 | 84.6% | 100.0% |
|
|
137
|
+
| SlackRequestVerifier (ok) | 2 | 6 | 2 | 0 | 4 | 33.3% | 100.0% |
|
|
138
|
+
| JwtTokenProvider (ok) | 8 | 8 | 8 | 0 | 0 | 100.0% | 100.0% |
|
|
139
|
+
| AuthRateLimiter (ok) | 4 | 5 | 4 | 0 | 1 | 80.0% | 100.0% |
|
|
140
|
+
| DataErasureService (ok) | 0 | 2 | 0 | 0 | 2 | 0.0% | n/d |
|
|
141
|
+
| EmailService (ok) | 7 | 9 | 7 | 0 | 2 | 77.8% | 100.0% |
|
|
142
|
+
|
|
143
|
+
Agregado (profundidade 1): recall 54.8%, precisao 100.0% (8 simbolos); profundidade 2: recall 57.7%.
|
|
144
|
+
|
|
145
|
+
recall@8 no conjunto de referencia (8 perguntas):
|
|
146
|
+
|
|
147
|
+
| Backend | Tipo | n | recall@8 | acerto@8 |
|
|
148
|
+
|---|---|---|---|---|
|
|
149
|
+
| graph | all | 8 | 43.8% | 50.0% |
|
|
150
|
+
| graph | conceptual | 6 | 25.0% | 33.3% |
|
|
151
|
+
| graph | symbolic | 2 | 100.0% | 100.0% |
|
|
152
|
+
| graph_call_only | all | 8 | 43.8% | 50.0% |
|
|
153
|
+
| graph_call_only | conceptual | 6 | 25.0% | 33.3% |
|
|
154
|
+
| graph_call_only | symbolic | 2 | 100.0% | 100.0% |
|
|
155
|
+
| lexical_auto | all | 8 | 87.5% | 87.5% |
|
|
156
|
+
| lexical_auto | conceptual | 6 | 100.0% | 100.0% |
|
|
157
|
+
| lexical_auto | symbolic | 2 | 50.0% | 50.0% |
|
|
158
|
+
| lexical_curated | all | 8 | 100.0% | 100.0% |
|
|
159
|
+
| lexical_curated | conceptual | 6 | 100.0% | 100.0% |
|
|
160
|
+
| lexical_curated | symbolic | 2 | 100.0% | 100.0% |
|
|
161
|
+
|
|
162
|
+
`lexical_auto` usa so o texto da pergunta (baseline justo para o fallback ripgrep); `lexical_curated` usa palavras escolhidas por quem conhecia a resposta (teto otimista, nao comparar).
|
|
163
|
+
| vector (pgvector) | - | - | NAO MEDIDO | NAO MEDIDO |
|
|
164
|
+
|
|
165
|
+
### visionhammer-agents
|
|
166
|
+
|
|
167
|
+
- Corrida a frio: 15.27s, pico de RSS da arvore 793.7 MB (estimativa; maior filho 439.7 MB), nos 16776, arestas 60583, graphify-out 121.6 MB.
|
|
168
|
+
- G1: 1056/1056 ficheiros de codigo com simbolos (100.0%); 0 sem simbolos, dos quais 0 em testes. Lista: nenhum
|
|
169
|
+
- G2: INFERRED 14.05% (12.87% so em fontes main); INFERRED por relacao {'calls': 5980, 'references': 2534}.
|
|
170
|
+
- G8 determinismo: assinatura 703f4318fc6b20a3 vs 703f4318fc6b20a3, igual: True.
|
|
171
|
+
- Incremental (1 ficheiro alterado: agents/cloud-engineer/src/main/java/io/visionhammer/agents/agent1/controller/Agent1Controller.java): 18.68s, 1123.9 MB; simbolo novo no grafo: True; sem alteracoes: 11.32s.
|
|
172
|
+
- G7 rede bloqueada (sandbox-exec (deny network*)): exit 0, 0 eventos socket, 2 subprocess.Popen (('git', ['git', 'rev-parse', 'HEAD'], '/private/tmp/claude-501/-Users-).
|
|
173
|
+
- G2 amostra (seed 20261028, n=60): oraculo ok/wrong/unknown {'ok': 53, 'unknown': 7}; precisao nas decididas 100.0%; estrato ambiguo {'n': 20, 'ok': 18, 'precision_pct': 100.0, 'unknown': 2, 'wrong': 0}; estrato outros {'n': 40, 'ok': 35, 'precision_pct': 100.0, 'unknown': 5, 'wrong': 0}. Revisao manual: 0 linhas.
|
|
174
|
+
|
|
175
|
+
`affected` (profundidade 1) contra `rg -w`, so fontes main:
|
|
176
|
+
|
|
177
|
+
| Simbolo | grafo | rg | ambos | so grafo | so rg | recall vs rg | precisao vs rg |
|
|
178
|
+
|---|---|---|---|---|---|---|---|
|
|
179
|
+
| ContextAssembler (ok) | 7 | 15 | 7 | 0 | 8 | 46.7% | 100.0% |
|
|
180
|
+
| VoyageEmbeddingClient (ok) | 7 | 9 | 7 | 0 | 2 | 77.8% | 100.0% |
|
|
181
|
+
| PiiEgressMasker (ok) | 21 | 21 | 21 | 0 | 0 | 100.0% | 100.0% |
|
|
182
|
+
| SecretMasker (ok) | 1 | 1 | 1 | 0 | 0 | 100.0% | 100.0% |
|
|
183
|
+
| CorrelationIdDetector (ok) | 2 | 2 | 2 | 0 | 0 | 100.0% | 100.0% |
|
|
184
|
+
| LiteLlmChatAdapter (ok) | 11 | 1 | 0 | 11 | 1 | 0.0% | 0.0% |
|
|
185
|
+
| PolicyGateGuard (ok) | 4 | 4 | 4 | 0 | 0 | 100.0% | 100.0% |
|
|
186
|
+
| ChunkRemaskService (ok) | 1 | 3 | 1 | 0 | 2 | 33.3% | 100.0% |
|
|
187
|
+
|
|
188
|
+
Agregado (profundidade 1): recall 76.8%, precisao 79.6% (8 simbolos); profundidade 2: recall 76.8%.
|
|
189
|
+
|
|
190
|
+
recall@8 no conjunto de referencia (8 perguntas):
|
|
191
|
+
|
|
192
|
+
| Backend | Tipo | n | recall@8 | acerto@8 |
|
|
193
|
+
|---|---|---|---|---|
|
|
194
|
+
| graph | all | 8 | 62.5% | 62.5% |
|
|
195
|
+
| graph | conceptual | 7 | 57.1% | 57.1% |
|
|
196
|
+
| graph | symbolic | 1 | 100.0% | 100.0% |
|
|
197
|
+
| graph_call_only | all | 8 | 62.5% | 62.5% |
|
|
198
|
+
| graph_call_only | conceptual | 7 | 57.1% | 57.1% |
|
|
199
|
+
| graph_call_only | symbolic | 1 | 100.0% | 100.0% |
|
|
200
|
+
| lexical_auto | all | 8 | 75.0% | 75.0% |
|
|
201
|
+
| lexical_auto | conceptual | 7 | 71.4% | 71.4% |
|
|
202
|
+
| lexical_auto | symbolic | 1 | 100.0% | 100.0% |
|
|
203
|
+
| lexical_curated | all | 8 | 100.0% | 100.0% |
|
|
204
|
+
| lexical_curated | conceptual | 7 | 100.0% | 100.0% |
|
|
205
|
+
| lexical_curated | symbolic | 1 | 100.0% | 100.0% |
|
|
206
|
+
|
|
207
|
+
`lexical_auto` usa so o texto da pergunta (baseline justo para o fallback ripgrep); `lexical_curated` usa palavras escolhidas por quem conhecia a resposta (teto otimista, nao comparar).
|
|
208
|
+
| vector (pgvector) | - | - | NAO MEDIDO | NAO MEDIDO |
|
|
209
|
+
|
|
210
|
+
## 3. Spring/DI (fixture sintetica)
|
|
211
|
+
|
|
212
|
+
| Grupo | Encontrado | Esperado | % |
|
|
213
|
+
|---|---|---|---|
|
|
214
|
+
| bean_methods | 2 | 4 | 50.0% |
|
|
215
|
+
| interface_impl | 5 | 8 | 62.5% |
|
|
216
|
+
| lombok_injection | 4 | 4 | 100.0% |
|
|
217
|
+
| qualifier | 0 | 3 | 0.0% |
|
|
218
|
+
|
|
219
|
+
Itens em falta:
|
|
220
|
+
|
|
221
|
+
- bean_methods / bean-clock-consumed-by-audit-bean: not reachable within 2 hops
|
|
222
|
+
- bean_methods / bean-consumer-checkout-audit: not reachable within 2 hops
|
|
223
|
+
- interface_impl / checkout-reaches-stripe-impl: not reachable within 2 hops
|
|
224
|
+
- interface_impl / refund-reaches-paypal-impl: not reachable within 2 hops
|
|
225
|
+
- interface_impl / refund-reaches-stripe-impl: not reachable within 2 hops
|
|
226
|
+
- qualifier / qualifier-stripe-selected: not reachable within 2 hops
|
|
227
|
+
- qualifier / qualifier-stripe-not-paypal: vacuous: companion check qualifier-stripe-selected failed, so absence proves nothing
|
|
228
|
+
- qualifier / qualifier-fastcache-bean: not reachable within 2 hops
|
|
229
|
+
|
|
230
|
+
## 4. Licencas das dependencias instaladas
|
|
231
|
+
|
|
232
|
+
32 distribuicoes; permissivas 32; a rever nenhuma; desconhecidas nenhuma; sem ficheiro de licenca ['tree-sitter-groovy'].
|
|
233
|
+
|
|
234
|
+
## 5. pip-audit
|
|
235
|
+
|
|
236
|
+
0 vulnerabilidades conhecidas em 32 pacotes (exit 0)
|
|
237
|
+
|
|
238
|
+
## 6. O que este relatorio nao mediu
|
|
239
|
+
|
|
240
|
+
- recall@8 contra pgvector: precisa do dataset do RagEvalController e da BD de QA (passar `--vector-results`).
|
|
241
|
+
- G6 (incidentes ponta a ponta sem Voyage), G9 (imagem local-agent-full) e G10 (repo do piloto, Python/SQL/Go/Terraform reais).
|
|
242
|
+
- Revisao manual por 2 pessoas das arestas INFERRED (o CSV em `review/` esta pronto para isso).
|
|
243
|
+
- Desempenho em 2 vCPU/4 GB e no hardware do piloto.
|