deepdebug-local-agent 1.0.22 → 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.
Files changed (188) hide show
  1. package/.claude/context.md +76 -0
  2. package/.claude/skills/README.md +25 -0
  3. package/.claude/skills/tdd-debugging/SKILL.md +53 -0
  4. package/.claude/skills/tdd-debugging/references/anti-patterns.md +43 -0
  5. package/.claude/skills/tdd-debugging/references/command-reference.md +46 -0
  6. package/.claude/standards/README.md +51 -0
  7. package/.claude/standards/clean-code.md +75 -0
  8. package/.claude/standards/code-review-checklist.md +61 -0
  9. package/.claude/standards/layered-architecture.md +163 -0
  10. package/.claude/standards/node.md +45 -0
  11. package/.claude/standards/security-owasp.md +114 -0
  12. package/.claude/standards/simplicity.md +42 -0
  13. package/.claude/standards/solid.md +64 -0
  14. package/.claude/standards/testing.md +59 -0
  15. package/.cursor/rules/standards.mdc +17 -0
  16. package/.github/workflows/build-byoc-image.yml +315 -0
  17. package/.github/workflows/deploy-gcp.yml +102 -0
  18. package/.github/workflows/npm-publish.yml +69 -8
  19. package/AGENTS.md +21 -0
  20. package/CHANGELOG.md +69 -0
  21. package/CLAUDE.md +13 -0
  22. package/CLAUDE_CODE_AGENT_INFINITE.md +5 -3
  23. package/Dockerfile +4 -0
  24. package/Dockerfile.full +55 -3
  25. package/README.md +81 -0
  26. package/bin/install.js +16 -5
  27. package/cloudbuild-full.yaml +57 -0
  28. package/docker-compose.yml +5 -3
  29. package/docs/OBSERVABILITY.md +164 -0
  30. package/docs/byoc-agent-image.md +70 -0
  31. package/docs/graphify-packaging-plan.md +133 -0
  32. package/docs/graphify-validation-run-2026-10-28.md +243 -0
  33. package/docs/legacy-connectors.md +268 -0
  34. package/docs/releasing.md +43 -0
  35. package/docs/signed-commands.md +163 -0
  36. package/env.example +30 -0
  37. package/helm/Chart.yaml +2 -2
  38. package/helm/templates/deployment.yaml +13 -3
  39. package/helm/templates/networkpolicy.yaml +22 -0
  40. package/helm/templates/secret.yaml +1 -0
  41. package/helm/values.yaml +13 -14
  42. package/mcp-server.js +4 -1
  43. package/package.json +12 -4
  44. package/scripts/sync-fixtures.mjs +246 -0
  45. package/scripts/upgrade-observability-push.sh +227 -0
  46. package/src/adapters/as400/as400-parser.js +12 -0
  47. package/src/adapters/as400/index.js +134 -0
  48. package/src/adapters/cobol/cobol-parser.js +37 -0
  49. package/src/adapters/cobol/index.js +151 -0
  50. package/src/adapters/ibmmq/ibmmq-parser.js +23 -0
  51. package/src/adapters/ibmmq/index.js +137 -0
  52. package/src/adapters/index.js +79 -0
  53. package/src/adapters/oracle-forms/index.js +145 -0
  54. package/src/adapters/oracle-forms/oracle-forms-parser.js +11 -0
  55. package/src/adapters/sap/index.js +146 -0
  56. package/src/adapters/sap/sap-parser.js +41 -0
  57. package/src/ai-engine.js +22 -22
  58. package/src/config/trusted-signing-keys.json +3 -0
  59. package/src/detectors/build-tool-detector.js +50 -0
  60. package/src/detectors/multi-project-detector.js +22 -22
  61. package/src/detectors/service-detector.js +24 -24
  62. package/src/exec-utils.js +26 -4
  63. package/src/git/base-git-provider.js +117 -117
  64. package/src/git/bitbucket-provider.js +2 -2
  65. package/src/git/git-provider-registry.js +18 -18
  66. package/src/git/github-provider.js +13 -13
  67. package/src/git/multi-repo-pr.js +161 -0
  68. package/src/mcp-http-server.js +32 -16
  69. package/src/observability/bundle-files.js +418 -0
  70. package/src/observability/detect.js +151 -0
  71. package/src/observability/provision.js +212 -0
  72. package/src/observability/render-cli.js +59 -0
  73. package/src/observability/routes.js +88 -0
  74. package/src/patch/patch-engine.js +28 -28
  75. package/src/patch-manager.js +85 -85
  76. package/src/patch_bkp.js +14 -14
  77. package/src/ports.js +4 -4
  78. package/src/routes/workspace.route.js +47 -16
  79. package/src/runtimes/base-runtime.js +75 -75
  80. package/src/runtimes/java/gradle-runtime.js +17 -17
  81. package/src/runtimes/java/java-integrations.js +25 -25
  82. package/src/runtimes/java/maven-runtime.js +34 -34
  83. package/src/runtimes/node/node-integrations.js +1 -1
  84. package/src/runtimes/node/npm-runtime.js +14 -14
  85. package/src/runtimes/node/yarn-runtime.js +4 -4
  86. package/src/runtimes/runtime-registry.js +25 -25
  87. package/src/server-local.js +4 -4
  88. package/src/server.js +1412 -335
  89. package/src/siblings/burst-suppressor.js +89 -0
  90. package/src/siblings/capabilities.js +273 -0
  91. package/src/siblings/env-compat.cjs +58 -0
  92. package/src/siblings/incident-forwarder.js +320 -0
  93. package/src/siblings/logger.js +15 -0
  94. package/src/siblings/metrics.js +94 -0
  95. package/src/utils/agent-auth.js +184 -0
  96. package/src/utils/approval-token.js +56 -0
  97. package/src/utils/bind-host.js +29 -0
  98. package/src/utils/branch-refspec.js +98 -0
  99. package/src/utils/child-env.js +34 -0
  100. package/src/utils/environment-diagnostics.js +55 -55
  101. package/src/utils/exec-policy.js +962 -0
  102. package/src/utils/exec-utils.js +15 -15
  103. package/src/utils/file-count.js +46 -0
  104. package/src/utils/fs-utils.js +10 -10
  105. package/src/utils/multi-repo-open.js +132 -0
  106. package/src/utils/open-gate.js +183 -0
  107. package/src/utils/ranged-read.js +65 -0
  108. package/src/utils/request-guards.js +77 -0
  109. package/src/utils/request-signing.js +342 -0
  110. package/src/utils/root-recovery.js +244 -0
  111. package/src/utils/safe-exec-args.js +58 -0
  112. package/src/utils/sandbox.js +182 -0
  113. package/src/utils/search-outcome.js +56 -0
  114. package/src/utils/workspace-root.js +80 -0
  115. package/src/validators/syntax-validator.js +149 -0
  116. package/src/workspace/detect-port.js +16 -16
  117. package/src/workspace/file-reader.js +1 -1
  118. package/src/workspace/process-manager.js +46 -46
  119. package/src/workspace-manager.js +86 -24
  120. package/test/adapters/as400/as400.test.js +121 -0
  121. package/test/adapters/cobol/cobol.test.js +217 -0
  122. package/test/adapters/ibmmq/ibmmq.test.js +122 -0
  123. package/test/adapters/oracle-forms/oracle-forms.test.js +180 -0
  124. package/test/adapters/registry.test.js +108 -0
  125. package/test/adapters/sap/sap.test.js +142 -0
  126. package/test/agent-auth.test.js +194 -0
  127. package/test/bind-host.test.js +26 -0
  128. package/test/branch-refspec.test.js +224 -0
  129. package/test/burst-suppressor.test.js +60 -0
  130. package/test/capabilities-access.test.js +47 -0
  131. package/test/capabilities.test.js +87 -0
  132. package/test/child-env.test.js +23 -0
  133. package/test/env-compat.test.js +71 -0
  134. package/test/exec-policy.test.js +463 -0
  135. package/test/file-count.test.js +64 -0
  136. package/test/incident-forwarder.test.js +231 -0
  137. package/test/multi-repo-open.test.js +180 -0
  138. package/test/multi-repo-pr.test.js +129 -0
  139. package/test/observability-detect.test.js +199 -0
  140. package/test/open-gate.test.js +341 -0
  141. package/test/ranged-read.test.js +60 -0
  142. package/test/request-guards.test.js +91 -0
  143. package/test/request-signing.test.js +284 -0
  144. package/test/root-recovery-sidecar.test.js +171 -0
  145. package/test/root-recovery.test.js +274 -0
  146. package/test/safe-exec-args.test.js +58 -0
  147. package/test/sandbox.test.js +98 -0
  148. package/test/search-outcome.test.js +50 -0
  149. package/test/workspace-root.test.js +64 -0
  150. package/tools/graphify-validation/README.md +92 -0
  151. package/tools/graphify-validation/config/questions.json +31 -0
  152. package/tools/graphify-validation/config/repos.json +9 -0
  153. package/tools/graphify-validation/config/symbols.json +6 -0
  154. package/tools/graphify-validation/fixtures/spring/expected.json +34 -0
  155. package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/AppConfig.java +29 -0
  156. package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/AuditLogger.java +15 -0
  157. package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/Cache.java +7 -0
  158. package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/CheckoutService.java +25 -0
  159. package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/InMemoryCache.java +18 -0
  160. package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/OrderController.java +22 -0
  161. package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/PaymentGateway.java +5 -0
  162. package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/PaypalGateway.java +11 -0
  163. package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/RefundService.java +14 -0
  164. package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/StripeGateway.java +11 -0
  165. package/tools/graphify-validation/lib/affected_vs_rg.py +81 -0
  166. package/tools/graphify-validation/lib/audit_hook/sitecustomize.py +22 -0
  167. package/tools/graphify-validation/lib/graph_metrics.py +96 -0
  168. package/tools/graphify-validation/lib/gv_common.py +130 -0
  169. package/tools/graphify-validation/lib/licenses.py +47 -0
  170. package/tools/graphify-validation/lib/pip_audit_summary.py +18 -0
  171. package/tools/graphify-validation/lib/recall.py +115 -0
  172. package/tools/graphify-validation/lib/report.py +226 -0
  173. package/tools/graphify-validation/lib/run_all.py +237 -0
  174. package/tools/graphify-validation/lib/run_graphify.py +144 -0
  175. package/tools/graphify-validation/lib/rusage_helper.py +17 -0
  176. package/tools/graphify-validation/lib/sample_edges.py +246 -0
  177. package/tools/graphify-validation/lib/spring_fixture.py +81 -0
  178. package/tools/graphify-validation/requirements-py311.lock +508 -0
  179. package/tools/graphify-validation/requirements-tools.in +1 -0
  180. package/tools/graphify-validation/requirements-tools.lock +371 -0
  181. package/tools/graphify-validation/requirements.in +1 -0
  182. package/tools/graphify-validation/requirements.lock +502 -0
  183. package/tools/graphify-validation/run.sh +43 -0
  184. package/tools/graphify-validation/tests/test_harness.py +151 -0
  185. package/cloudbuild-agent-qa.yaml +0 -43
  186. package/cloudbuild-agent-staging.yaml +0 -40
  187. package/cloudbuild.yaml.deprecated +0 -42
  188. package/src/local-agent-source.tar.gz +0 -0
@@ -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.