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,76 @@
1
+ # deepdebug-local-agent: context for AI agents
2
+
3
+ > Read this, then `.claude/standards/` (`node.md` for this stack), then `README.md`.
4
+
5
+ ## What this repo is
6
+
7
+ The platform's **hands inside the customer's infrastructure**: a Node.js (ESM, Node 18+)
8
+ agent that watches application logs, detects errors and forwards incidents, serves the
9
+ workspace (file tree, reads, patches, git, exec) to the platform, keeps a WebSocket
10
+ reverse tunnel to the gateway, exposes an MCP bridge, and ships legacy-stack connectors
11
+ (SAP, IBM MQ, AS/400, Oracle Forms, COBOL; opt-in through `LEGACY_STACKS`).
12
+
13
+ Distributed as the npm package `deepdebug-local-agent` (published from `main`) and as a
14
+ Cloud Run service (`deepdebug-local-agent-qa` / `deepdebug-local-agent`) for the hosted
15
+ variant.
16
+
17
+ **Does not:** call LLMs or hold provider keys (the thinking happens in vh-agents via
18
+ vh-litellm), decide tenant policy, or merge anything.
19
+
20
+ ## Neighbours
21
+
22
+ ```
23
+ customer apps/logs --> [local-agent] --HTTP /api/v1/internal/incident/create, /api/v1/agent/authorize--> vh-api-gateway
24
+ [local-agent] <==WS tunnel /api/v1/agent/ws==> vh-api-gateway <--> vh-agents
25
+ [local-agent] --MCP bridge (5056)-- local tools; git providers (GitHub/Bitbucket) for multi-repo PRs
26
+ ```
27
+ The gateway base URL comes from `GATEWAY_URL` / `~/.deepdebug/config.json`. For QA use
28
+ `https://api-qa.visionhammer.io`, not the `*-575662086506.run.app` URLs in old notes,
29
+ which are dead.
30
+
31
+ ## Where things live
32
+
33
+ | Path | What |
34
+ |---|---|
35
+ | `src/server.js` | main Express server (very large; extract new code into modules) |
36
+ | `src/routes/` | route modules (`workspace.route.js`) |
37
+ | `src/workspace/`, `src/utils/` | scanning, file reads, process manager, multi-repo open, open gate, sandbox, path helpers |
38
+ | `src/patch/`, `patch-manager.js` | patch engine and backups |
39
+ | `src/git/` | git providers, multi-repo PRs |
40
+ | `src/siblings/` | incident forwarder, burst suppressor, capabilities, logger, metrics |
41
+ | `src/adapters/` | legacy-stack connectors (fixed 6-key error shape) |
42
+ | `src/mcp-http-server.js` | MCP HTTP bridge |
43
+ | `test/` | `node:test` suites plus fixtures |
44
+ | `bin/install.js`, `helm/`, `Dockerfile*` | install and packaging |
45
+
46
+ ## Run and test
47
+
48
+ ```bash
49
+ npm install
50
+ npm test # node --test
51
+ npm start # port 5055, MCP bridge on 5056
52
+ ```
53
+
54
+ ## Deploy and rollback
55
+
56
+ - PR to **`main`**. Merging to `main` with changes under `src/`, `bin/` or `package.json`
57
+ **publishes the npm package** (`npm-publish.yml`), so bump the version deliberately.
58
+ Customers pick it up on install or update.
59
+ - Cloud Run deploy is **manual** (`deploy-gcp.yml`, `workflow_dispatch`, pick `qa` or
60
+ `prod`).
61
+ - Rollback: npm, by publishing a fixed patch version (don't unpublish); Cloud Run, via
62
+ `update-traffic` to the revision that was serving (read it live, with percentages).
63
+
64
+ ## Gotchas that matter here
65
+
66
+ - Runs with the customer's privileges: every exec is an argument array with a confined
67
+ `cwd`; every path is confined to the workspace; branch and ref names are validated
68
+ (`branch-refspec.js`).
69
+ - Customer logs contain secrets and PII: redact before forwarding; never log
70
+ `DEEPDEBUG_API_KEY`, tokens or credentialed git URLs.
71
+ - Tenant identity comes from the agent's registration and token, verified by the
72
+ gateway. Never trust a tenant id sent by a remote caller.
73
+ - `CLAUDE_CODE_AGENT_INFINITE.md` and similar root notes are **historical**. They
74
+ reference the dead `insptechai` project, dead hosts and inline credentials. Do not run
75
+ them or copy values from them.
76
+ - Committed tarballs (`local-agent-source.tar.gz`) are build leftovers; don't add more.
@@ -0,0 +1,25 @@
1
+ # Project skills
2
+
3
+ Claude Code skills copied verbatim from the team's `programming-skills` collection
4
+ (`programming-skills/skills/<name>/`, June 2026 snapshot). Each one is loaded on demand
5
+ when its `description` matches the task.
6
+
7
+ | Skill | Use for |
8
+ |---|---|
9
+ | `tdd-debugging` | proving a bug with a failing test before fixing it (Java, Python and Node commands) |
10
+
11
+ Only language-neutral skills were copied here. The rest of the collection is Java/Spring
12
+ specific (the gateway and vh-agents carry those). The shared rules for this repo's stack
13
+ are in `.claude/standards/`.
14
+
15
+ **Precedence:** where a skill disagrees with `CLAUDE.md`, `.claude/context.md` or
16
+ `.claude/standards/`, the repo files win.
17
+
18
+ ## Licence and provenance
19
+
20
+ The `programming-skills` collection has **no LICENSE file**. `skill-creator` describes it
21
+ as the VisionHammer programming-skills repository, and part of it looks adapted from an
22
+ external collection whose origin is not recorded. Treat these files as internal. **Do not
23
+ publish or redistribute them** (this matters for the Forge app, which may ship to the
24
+ Marketplace; `.claude/` is not part of the Forge bundle) until provenance and licence are
25
+ confirmed.
@@ -0,0 +1,53 @@
1
+ ---
2
+ name: tdd-debugging
3
+ description: >
4
+ Fix production bugs by proving them with a failing test before changing code.
5
+ Preserve existing tests as immutable evidence. Workflow: RED -> GREEN ->
6
+ REFACTOR across Java, Python, and Node.js.
7
+ argument-hint: [bug-description]
8
+ allowed-tools: Read Grep Bash
9
+ ---
10
+
11
+ ## When to Use
12
+
13
+ Use `tdd-debugging` for bugs or regressions that require a proof-of-failure test.
14
+ Use [test-quality](../test-quality/SKILL.md) for test design, flakiness, or general coverage issues.
15
+
16
+ ## Core Rules
17
+
18
+ 1. **Existing tests are sacred.** Never modify or delete an existing test to make the suite pass.
19
+ 2. **Prove RED first.** If tests pass but the bug exists, add a new regression test that fails before the fix.
20
+ 3. **Keep it narrow.** One bug, one scenario, one assertion focus.
21
+ 4. **No side effects.** Report technically broken tests (dead endpoints, etc.) before changing them.
22
+ 5. **Verify in stages.** Targeted test -> Module suite -> Full project suite.
23
+
24
+ ## Workflow
25
+
26
+ ### Phase 1: RED
27
+ 1. Extract failing behavior from report/stack trace.
28
+ 2. Detect nearest module (look for `pom.xml`, `build.gradle`, `pyproject.toml`, `package.json`).
29
+ 3. Run relevant existing tests. If one fails, use it as proof. If all pass, add a new one.
30
+ 4. **Heuristics for new tests**: focus on null/missing input, boundary values, invalid state transitions, or wrong exception types.
31
+
32
+ ### Phase 2: GREEN
33
+ 1. Patch production code to satisfy the failing scenario.
34
+ 2. Re-run targeted test until it passes.
35
+ 3. Run local module suite to check for immediate regressions.
36
+
37
+ ### Phase 3: REFACTOR
38
+ 1. Run full project suite.
39
+ 2. Verify all tests pass, including the new or formerly failing proof test.
40
+ 3. Summarize: scenario, regression proof, patch, and validation results.
41
+
42
+ ## References
43
+ - [command-reference.md](./references/command-reference.md): essential commands by stack.
44
+ - [anti-patterns.md](./references/anti-patterns.md): critical mistakes to avoid.
45
+
46
+ ## Response Format
47
+ Include:
48
+ 1. **Scenario**: Bug description and proof type (existing vs new).
49
+ 2. **Regression Proof**: Name of failing test kept unchanged or added.
50
+ 3. **Patch**: Summary of production code changes.
51
+ 4. **Validation**: Results for targeted test, module suite, and full suite.
52
+ 5. **Existing tests modified**: `0`
53
+
@@ -0,0 +1,43 @@
1
+ # TDD Debugging Anti-Patterns
2
+
3
+ Avoid these failure modes to ensure debugging reliability.
4
+
5
+ ## Critical Mistakes
6
+
7
+ - **Modifying existing tests to force success**: This destroys your only baseline evidence. Existing tests must remain unchanged as proof of the regression.
8
+ - **Deleting failing tests**: A failing test is your "RED" proof. Deleting it makes the fix unverified.
9
+ - **Changing production code before RED**: If you don't see the test fail first, you haven't proven that your test actually covers the bug.
10
+ - **Swallowing exceptions**: Using generic `catch (Exception e)` or empty catch blocks hides the root cause and makes future debugging harder.
11
+ - **Using try/catch for control flow in a fix**: This often masks the symptom instead of fixing the logic error.
12
+ - **Asserting internal implementation details**: Avoid mocking private methods or checking internal call order if an externally visible behavior assertion is possible. This makes tests brittle.
13
+ - **Leaving debug leftovers**: Hardcoded values, `print` statements, or temporary branches must be removed before the "REFACTOR" phase is complete.
14
+
15
+ ## Canonical Example: Preserving Evidence
16
+
17
+ Bad (Rewriting the evidence):
18
+
19
+ ```java
20
+ @Test
21
+ void shouldFindUser() {
22
+ User user = userService.findById(1L); // changed from null to force success
23
+ assertNotNull(user);
24
+ }
25
+ ```
26
+
27
+ Good (Preserving the proof and adding a fix):
28
+
29
+ ```java
30
+ @Test
31
+ void shouldFindUser() {
32
+ User user = userService.findById(null); // unchanged failing proof
33
+ assertNotNull(user);
34
+ }
35
+
36
+ @Test
37
+ void shouldRejectNullId() {
38
+ assertThatThrownBy(() -> userService.findById(null))
39
+ .isInstanceOf(IllegalArgumentException.class);
40
+ }
41
+ ```
42
+
43
+ **Why this matters**: Changing the input in an existing test to make it pass hides the fact that the original code couldn't handle the edge case. Always add a new test for missing coverage.
@@ -0,0 +1,46 @@
1
+ # Essential Command Reference
2
+
3
+ Prefer the smallest command that proves the bug. Run from the module directory first.
4
+
5
+ ## Module Detection
6
+
7
+ Locate the nearest build file:
8
+ - **Maven**: `pom.xml`
9
+ - **Gradle**: `build.gradle` or `build.gradle.kts`
10
+ - **Python**: `pyproject.toml`
11
+ - **Node.js**: `package.json`
12
+
13
+ ## Test Execution by Stack
14
+
15
+ ### Maven
16
+ ```bash
17
+ mvn test -Dtest=ClassName # Run specific test class
18
+ mvn test -Dtest=ClassName#methodName # Run specific test method
19
+ mvn test -pl :module-name # Run module from root
20
+ ```
21
+
22
+ ### Gradle
23
+ ```bash
24
+ ./gradlew test --tests ClassName # Run specific test class
25
+ ./gradlew test --tests ClassName.methodName # Run specific test method
26
+ ./gradlew :module-path:test # Run module from root
27
+ ```
28
+
29
+ ### Python (pytest)
30
+ ```bash
31
+ pytest path/to/test_file.py # Run specific file
32
+ pytest path/to/test_file.py::test_name # Run specific test
33
+ ```
34
+
35
+ ### Node.js (npm/jest)
36
+ ```bash
37
+ npm test -- FileName # Run specific file
38
+ npm test -- -t "test name" # Run specific test
39
+ ```
40
+
41
+ ## Git Triage
42
+
43
+ Use only when the bug is hard to localize:
44
+ - `git show <commit> -- <file>`: Inspect recent changes to a specific file.
45
+ - `git blame -L <start>,<end> <file>`: Identify when a specific line changed.
46
+ - `git bisect`: Automate finding the commit that introduced a regression.
@@ -0,0 +1,51 @@
1
+ # VisionHammer engineering standards
2
+
3
+ These files are the same in every VisionHammer repo. Every AI coding agent (Claude Code,
4
+ Cursor, Copilot, Codex, Gemini) and every human contributor follows them. Repo-specific
5
+ facts, such as what the repo is, how to run it and its gotchas, live in `.claude/context.md`.
6
+ Repo rules in `CLAUDE.md` / `AGENTS.md` win over anything here.
7
+
8
+ | File | Use it when |
9
+ |---|---|
10
+ | [clean-code.md](clean-code.md) | naming, method size, comments, errors, local readability |
11
+ | [solid.md](solid.md) | splitting classes, adding variants, choosing abstractions |
12
+ | [simplicity.md](simplicity.md) | before adding any abstraction, flag, option or dependency |
13
+ | [layered-architecture.md](layered-architecture.md) | placing new code; feature/layer boundaries; ports |
14
+ | [security-owasp.md](security-owasp.md) | every endpoint, input, secret, outbound call, log line |
15
+ | [testing.md](testing.md) | writing or reviewing tests; proving a fix |
16
+ | [code-review-checklist.md](code-review-checklist.md) | before opening a PR and when reviewing one |
17
+ | [java-spring.md](java-spring.md) | Java 17+/21, Spring Boot WebFlux, Reactor, R2DBC (gateway, agents) |
18
+ | [typescript-react.md](typescript-react.md) | TypeScript, React, Vite, Forge UI Kit (frontend, forge app) |
19
+ | [python.md](python.md) | Python 3.12 (codescan, vh-litellm plugins) |
20
+ | [node.md](node.md) | plain JavaScript on Node.js, ESM (deepdebug-local-agent) |
21
+
22
+ A repo only ships the language files for its own stack.
23
+
24
+ ## Standards the build enforces
25
+
26
+ A rule that no test enforces is a wish. These guard tests fail the build. **Never widen an
27
+ allowlist or a baseline to make one pass. Fix the code.**
28
+
29
+ | Guard test | Repo | What it enforces |
30
+ |---|---|---|
31
+ | `LayeredArchitectureTest` | vh-api-gateway | ADR-002: in the `LAYERED_FEATURES` features, the domain is framework-free, `api` never reaches `infrastructure`, `application` never depends on `api`/`infrastructure`, and features talk only through `application` |
32
+ | `LlmAgnosticRatchetTest` | vh-api-gateway | ADR-001: no new class or file couples to an LLM provider, router or model id. The baselines in `src/test/resources/architecture/` only shrink |
33
+ | `ControllerTenantSourceArchTest` | vh-api-gateway | a controller never takes `tenantId` from the body, query or path, and never falls back to client input. The tenant comes only from `TenantAccessGuard` |
34
+ | `SecretsNeverReachLogsTest` | vh-api-gateway | credentials in URLs are redacted before logging, and decrypted secrets never hit a logger |
35
+ | `AutomationGateArchTest` | vh-api-gateway, orchestrator | every path that can apply a patch goes through `AutomationGateService` |
36
+ | `NoAutonomousMergeArchitectureTest` | vh-api-gateway, vh-agents | nothing merges a PR except the human approval path (`PrApprovalService.approve`) |
37
+ | `NoDirectProviderCallsGuardTest` | vh-agents | no provider SDK, host or key outside the LiteLLM adapter package |
38
+ | `EgressUrlGuardTest`, `NoEcbEncryptionArchTest`, `QaProfileArchTest` | vh-api-gateway | SSRF guard on tenant URLs; no AES/ECB and no fail-open decrypt; QA-only beans stay `@Profile("qa")` |
39
+ | `tests/test_no_direct_provider.py` | codescan | no provider SDK or host outside `src/llm/litellm.py` |
40
+ | `tests/test_config_loads.py`, `tests/test_smart_router.py` | vh-litellm | every alias has a `residency_class`; no fallback crosses a class; tier routing |
41
+
42
+ If you add a rule that matters, add the guard that enforces it in the same PR.
43
+
44
+ ## Where this came from
45
+
46
+ This is distilled and rewritten from the team's `programming-skills` collection:
47
+ `clean-code`, `solid-principles`, `architecture-review`, `security-audit`, `test-quality`,
48
+ `tdd-debugging`, `java-guidelines`, `reactive-webflux`, `r2dbc-patterns`, `logging-patterns`
49
+ and `multi-tenant-saas`, then corrected against the v2 architecture (ADR-001..003) and the
50
+ code as it is. Where the source skills disagree with our code, these files follow the code.
51
+ One example: cross-tenant access answers **404**, not 403.
@@ -0,0 +1,75 @@
1
+ # Clean code
2
+
3
+ Aim: someone new to the code understands a change in one read. Keep behaviour stable and
4
+ prefer the smallest change that makes the code easier to read, name, test and change.
5
+
6
+ ## Names
7
+
8
+ - Name things in the domain's words: `IncidentFixRunner`, `resolveWorkspaceForTenant`,
9
+ `allowedTiers`. Avoid `data`, `info`, `manager`, `helper`, `util`, `process`, `handle`
10
+ used on its own.
11
+ - Booleans read as questions: `isEnabled`, `hasRepo`, `canAutoMerge`, `shouldRetry`.
12
+ - A method named `validateAndSaveAndNotify` does three things. Split it.
13
+ - Spell words out. Only use well-known acronyms (`URL`, `JWT`, `DTO`, `SSE`, `PR`).
14
+ - One concept, one word, across all repos. Tenant, workspace, incident and scan mean the
15
+ same thing everywhere; don't mix in synonyms like "org", "project" or "ticket" for them.
16
+
17
+ ## Functions
18
+
19
+ - A function does one thing at one level of abstraction. Keep orchestration ("load, check,
20
+ act, emit") apart from the details of each step.
21
+ - Use guard clauses so the main path isn't buried under nesting:
22
+
23
+ ```java
24
+ if (workspace == null) return Mono.error(new WorkspaceNotFoundException(id));
25
+ if (!workspace.isActive()) return Mono.empty();
26
+ return runScan(workspace);
27
+ ```
28
+ - More than 3 or 4 parameters, or a `boolean` that changes behaviour: use a parameter
29
+ object, a record or an enum (`ScanMode.FULL`, not `run(repo, true)`).
30
+ - No hidden side effects. A method called `get*` or `is*` must not write, publish or
31
+ mutate state.
32
+
33
+ ## Literals and types
34
+
35
+ - Give magic numbers and strings a name (`MAX_ITERATIONS = 15`, `CALLBACK_TTL`).
36
+ - Use a value object or enum when mixing primitives up is a real risk (tenant id vs
37
+ workspace id, both `UUID`).
38
+ - Keep units in names or types: `timeoutMs`, `Duration timeout`.
39
+
40
+ ## Comments
41
+
42
+ - A comment explains **why**: a constraint, an incident, an ordering requirement, a link
43
+ to an ADR. It never narrates what the code does.
44
+ ```java
45
+ // 404, not 403: a 403 confirms the id exists (TenantAccessGuard javadoc).
46
+ ```
47
+ - If you need a comment to explain *what*, rename or extract first.
48
+ - Delete dead code. Don't comment it out and don't hide it behind a flag "just in case".
49
+ Git remembers it. (Gateway: removals follow CLAUDE.md rule 1.)
50
+ - Keep TODOs to a minimum. Each one says what, why and who or which ticket.
51
+
52
+ ## Errors
53
+
54
+ - Don't use exceptions for normal control flow.
55
+ - Never swallow an error. Log it with context, or propagate it, or both at one boundary,
56
+ not at every layer.
57
+ - When wrapping an exception, keep the cause: `new ScanFailedException("trivy", e)`.
58
+ - Fail closed. Missing config, a secret you can't read or a check that errors means deny,
59
+ not allow.
60
+
61
+ ## Duplication
62
+
63
+ - DRY applies to **knowledge**, not to lines that happen to look alike. Two blocks that
64
+ will change for different reasons can stay separate.
65
+ - Extract only once the third copy shows up, or once the first copy has a bug you had to
66
+ fix twice.
67
+
68
+ ## Do / don't
69
+
70
+ | Do | Don't |
71
+ |---|---|
72
+ | Rename, extract, add guard clauses in their own commit | Mix a refactor with a behaviour change in one commit |
73
+ | Prefer linear code in small orchestration classes | Add a generic helper that forces readers to jump across files |
74
+ | Keep a method's inputs and outputs explicit | Communicate through mutable fields or `ThreadLocal` |
75
+ | Leave the file cleaner than you found it (small, local) | Reformat whole files you are not otherwise changing |
@@ -0,0 +1,61 @@
1
+ # Code review checklist
2
+
3
+ Use it on your own diff before opening the PR, and on others' PRs. Report findings by
4
+ severity, with a file and line and the smallest fix. If nothing is wrong, say what you did
5
+ **not** verify.
6
+
7
+ ## Blockers (must fix before merge)
8
+
9
+ - [ ] **Tenant:** the tenant comes from the JWT (`TenantAccessGuard`), never the client;
10
+ every lookup by id checks ownership; foreign ids answer 404; there is a
11
+ cross-tenant test.
12
+ - [ ] **Secrets:** nothing secret in code, config, tests, logs, errors or the PR body; no
13
+ provider key outside vh-litellm; env changes use `--update-*`.
14
+ - [ ] **LLM boundary:** no model id, provider host, provider SDK or router outside
15
+ vh-litellm / the LiteLLM adapter; the gateway calls agents through `agenttask` ports
16
+ only.
17
+ - [ ] **Automation:** anything that applies patches goes through `AutomationGateService`;
18
+ no new merge path; agents stop at "PR opened".
19
+ - [ ] **Internal endpoints:** internal bearer via `InternalTokenAuthenticator`
20
+ (constant-time, fail closed), or a per-purpose token (HMAC callback).
21
+ - [ ] **Input:** validated at the boundary; SQL is parameterized; shell and git use
22
+ argument arrays; paths are confined; URLs pass the egress guard.
23
+ - [ ] **Schema:** changes only through a versioned migration; no runtime DDL; new column
24
+ writes are tested against real Postgres.
25
+ - [ ] **Guards green:** no allowlist or baseline widened to pass. Baselines only shrink.
26
+ - [ ] **Java:** ASCII only (`LC_ALL=C grep -n '[^ -~]' $(git ls-files '*.java')`); no
27
+ `.block()` on Netty threads.
28
+
29
+ ## Design
30
+
31
+ - [ ] New code lives in the right feature and layer; dependencies point inward; no new
32
+ code in the legacy `service/` or `controller/` tree.
33
+ - [ ] One intent per PR; mechanical moves, removals and behaviour changes are separate.
34
+ - [ ] No speculative abstraction, flag or dependency (`simplicity.md`). Every new flag has
35
+ an owner and an exit condition.
36
+ - [ ] Contracts between repos (FE routes, `/api/v2` DTOs, codescan result schema, Forge
37
+ gateway contract) are changed on both sides, or stay backward compatible.
38
+ - [ ] Consumers were checked by grepping the other repos, not assumed from javadoc.
39
+
40
+ ## Readability
41
+
42
+ - [ ] Names in domain words; functions do one thing; guard clauses; no magic values.
43
+ - [ ] Comments explain why; no commented-out code; no stray debug output (`console.log`,
44
+ `print`, `System.out`).
45
+ - [ ] Errors are propagated or logged once, with context and cause.
46
+
47
+ ## Tests
48
+
49
+ - [ ] Behaviour and scenarios covered (happy, edge, invalid, error, auth, cross-tenant).
50
+ - [ ] A bug fix has a test that failed before the fix.
51
+ - [ ] No existing test was weakened to make the suite pass.
52
+ - [ ] Reactive code is tested with `StepVerifier` / `WebTestClient`.
53
+
54
+ ## Delivery
55
+
56
+ - [ ] PR targets the integration branch: `release/qa` (gateway, agents) or `main` (FE,
57
+ codescan, litellm, local-agent, forge). Merging `release/qa` / FE `main` deploys QA.
58
+ - [ ] The PR states how it was verified and how to verify it on QA after the merge (curl,
59
+ log query, bundle grep) and the rollback revision (read live, with traffic
60
+ percentages).
61
+ - [ ] Conventional commit subject: `type(scope): summary`.
@@ -0,0 +1,163 @@
1
+ # Layered architecture
2
+
3
+ Decision records, all in `vh-api-gateway/docs/adr/`: ADR-001 (only LiteLLM knows models),
4
+ ADR-002 (package-by-feature with inner layers), ADR-003 (rename to vh-api-gateway,
5
+ `io.visionhammer.*`), ADR-004 (the workspace automation level drives PolicyGate).
6
+
7
+ ## Platform boundaries (v2)
8
+
9
+ ```
10
+ browser -> vision-heart-glow (React SPA)
11
+ | /api/v1/* -> vh-api-gateway (BFF, auth, tenants, integrations,
12
+ | incidents, notifications, PolicyGate)
13
+ | /api/v2/agents/software-engineer/* -> agent2 (debug/fix/implement/PR, CodeScan product)
14
+ | /api/v2/* (everything else) -> orchestrator (vh-agents)
15
+ vh-api-gateway --AgentTaskPort/AgentRunPort/AgentReviewPort--> orchestrator / agents
16
+ vh-agents --LlmPort (vh-smart + vh_tier + vh_task)--> vh-litellm --> model providers
17
+ vh-agents --PolicyGateGuard--> gateway /api/v1/internal/policy/evaluate (before every repo write)
18
+ agent2 --Cloud Run Job codescan-scan (europe-west1)--> codescan worker --HMAC callback--> agent2
19
+ Jira --Forge app (glue)--> vh-api-gateway; client infra <--WS tunnel/MCP--> deepdebug-local-agent
20
+ ```
21
+
22
+ Hard lines:
23
+ - **Only vh-litellm knows model ids, provider URLs and provider keys.** Everyone else asks
24
+ for a capability: `model: vh-smart` plus `metadata.vh_tier` (`max|reasoning|routine|bulk`)
25
+ plus `metadata.vh_task`.
26
+ - **The gateway does not call LLMs.** AI work goes to vh-agents through the `agenttask`
27
+ ports. The gateway keeps the control plane (PolicyGate: tiers, budget, residency, kill
28
+ switch), expressed without model names.
29
+ - **One source of autonomy (ADR-004).** The workspace automation level decides the
30
+ PolicyGate mode per action; `tenant_policies.operation_mode` is only a ceiling
31
+ (`effective = min(ceiling, workspace level)`). Every repo-writing agent tool is gated and
32
+ sends `diffFiles`. `RUN_SHELL_SAFE` covers proven-safe build/test/lint commands.
33
+ `DELETE_BRANCH`, `CLOSE_PR` and `SETUP_ENVIRONMENT` always need a human.
34
+ - **No autonomous merge anywhere.** Agents open PRs. Only a human approval in the gateway
35
+ merges.
36
+ - Services talk over HTTP APIs and ports, never through another service's tables. A table
37
+ shared on purpose (for example `invites`) is documented in both repos' context.
38
+
39
+ ## Inside a service: features with four layers, plus a shared kernel
40
+
41
+ ```
42
+ io.visionhammer.gateway.
43
+ <feature>/ admin agenttask cloud governance identity incidents integrations
44
+ api/ logs notifications onboarding tenancy trigger workspace
45
+ application/
46
+ domain/
47
+ infrastructure/
48
+ shared/ persistence crypto mail pii localagent idtoken audit
49
+ config/ filter/ health/ migration/ security/ util/ web/ (kernel: no feature code)
50
+ ```
51
+
52
+ | Layer | Holds | May depend on |
53
+ |---|---|---|
54
+ | `api` | controllers, request/response DTOs, HTTP/SSE mapping, validation | own `application`, `domain`; kernel; other features' `application` |
55
+ | `application` | use cases (one intent per class), orchestration, transactions, guards; ports that carry kernel types | own `domain`; kernel; other features' `application` |
56
+ | `domain` | entities, value objects, pure rules, ports over domain/JDK types | JDK and Reactor `Mono`/`Flux` only |
57
+ | `infrastructure` | R2DBC stores, HTTP clients, Redis, adapters that implement the ports | own `application`, `domain`; kernel; other features' `application` |
58
+
59
+ Dependencies point inward: `api -> application -> domain`, and `infrastructure` implements
60
+ ports. Concretely:
61
+
62
+ - **`domain` is framework-free.** No Spring, R2DBC, Reactor Netty, and no kernel class
63
+ (`shared`, `config`, `security`, ...). No other layer either.
64
+ - **`api` never touches `infrastructure`.** Controllers call use cases, never repositories
65
+ or clients.
66
+ - **`application` never depends on `api` or `infrastructure`.** A use case knows a port,
67
+ never the adapter, the SQL or the HTTP.
68
+ - **Cross-feature calls go only through the other feature's `application`.** Never import
69
+ another feature's `api`, `domain` or `infrastructure`. Need its data? Call its use case or
70
+ its application port (for example `incidents.application.IncidentReader`).
71
+ - DTOs stay at the edge; entities and rows are never returned as JSON.
72
+ - No LLM provider, router or model type in any layer of the gateway (ADR-001).
73
+
74
+ ### Where ports and adapters go
75
+
76
+ - **Port = interface**, named for what the use case needs (`*Store`, `*Reader`,
77
+ `*Writer`, `*Port`, `*Gateway`). **Adapter = the implementation** in
78
+ `<feature>.infrastructure` (`*R2dbcStore`, `*Client`, `*Adapter`).
79
+ - **Default: the port lives in `domain`**, when its signature uses only domain and JDK
80
+ (or `Mono`/`Flux`) types.
81
+ - **Port in `application` when it carries a legacy or kernel type.** If a method takes or
82
+ returns a type the domain may not import (a shared R2DBC row such as `UserEntity`, the
83
+ Mongo-mapped `IncidentDocument`, a `shared.persistence` entity), the port goes in
84
+ `application`. Say so in its Javadoc, and say when it moves to `domain` (for example once
85
+ the Mongo fallback is retired). Examples: `identity.application.UserAccountStore`,
86
+ `incidents.application.IncidentReader`.
87
+ - A store port for an existing repository mirrors it one for one, so every statement is
88
+ the one that ran before the port existed.
89
+ - **The shared kernel never depends on a feature.** When kernel code needs something a
90
+ feature owns, the kernel declares the port and the feature implements it (for example
91
+ `shared.localagent.CustomerCloudRunIdTokens`, implemented in `logs`).
92
+
93
+ ### The shared kernel
94
+
95
+ Code that several features use and no single feature owns: `shared.persistence` (rows
96
+ several features read or write), `shared.crypto`, `shared.mail`, `shared.pii`,
97
+ `shared.localagent`, `shared.idtoken`, `shared.audit`, plus the cross-cutting `config`,
98
+ `filter`, `health`, `migration`, `security`, `util` and `web`. Code goes there only when two
99
+ or more features need it and none owns it. It is not a `common/` dumping ground: if one
100
+ feature owns it, it lives in that feature.
101
+
102
+ The legacy by-type packages (`service`, `controller`, `entity`, `repository`, `model`,
103
+ `client`) are gone, and a test keeps them gone.
104
+
105
+ ## Adding a feature, or moving code into one
106
+
107
+ Each move is its own PR series, with no behaviour change mixed in:
108
+
109
+ 1. **Characterize first** (`test(...)`: *characterize X before the Y move*). Before moving
110
+ anything, pin today's behaviour in `*CharacterizationTest` classes: the exact SQL text
111
+ and binds (a recording `DatabaseClient`), HTTP status and body shapes, webhook
112
+ signatures, audit rows, ordering on every path. Build the service in exactly one
113
+ helper ("the only line that knows the constructor"), so the move changes one line per
114
+ test. The tests must pass on the old layout.
115
+ 2. **Ports** (`refactor(...)`: *ports for ...*). Put a `*Store` or `*Port` interface in
116
+ front of every repository or client the use cases touch, following the placement rule
117
+ above. Characterization stays green.
118
+ 3. **Move** (`refactor(...)`: *move X into X.{api,application,domain,infrastructure}*).
119
+ This is a mechanical move. Add the feature to `LayeredArchitectureTest.LAYERED_FEATURES`
120
+ in the same PR (it is never removed afterwards). If repositories moved, update
121
+ `@EnableR2dbcRepositories` base packages. Characterization still passes, with only
122
+ imports and the constructor helper changed.
123
+ 4. **Clean up.** Keep the sources ASCII-only (`LC_ALL=C grep -n '[^ -~]'` prints nothing).
124
+ Record in the repo's `.claude/context.md` where things now live.
125
+ 5. **Verify by use** after the QA deploy: boot logs, a real call on each moved endpoint.
126
+ A green suite alone does not prove the wiring.
127
+
128
+ New code starts directly in a feature. Ask which feature owns it, then put the rule in
129
+ `domain`, the flow in `application`, the HTTP in `api` and the I/O in `infrastructure`.
130
+
131
+ ## Guard tests (gateway, `src/test/java/io/visionhammer/gateway/architecture/`)
132
+
133
+ | Test | Enforces |
134
+ |---|---|
135
+ | `LayeredArchitectureTest` | each listed feature uses the layout; domain is framework- and kernel-free; api does not reach infrastructure; application does not depend on api or infrastructure; features only talk through `application`; `sharedKernelDependsOnNoFeature`; no class or source file in the retired by-type packages; no LLM provider type in a feature |
136
+ | `LlmAgnosticRatchetTest` | ADR-001 ratchet: provider coupling (bytecode and source text) can only shrink; baselines in `src/test/resources/architecture/` |
137
+ | `R2dbcRepositoryScanCoverageTest` | every R2DBC repository sits under a scanned package and the right scanner (R2DBC vs Mongo) owns it |
138
+ | `BeanConstructorResolvabilityTest` | every bean has one resolvable constructor, so the context still boots |
139
+ | `ComponentScanCoversNewPackagesTest` | the scan root is `io.visionhammer.gateway`; nothing under the old `com.deepdebug` |
140
+ | `NoAutonomousMergeArchitectureTest` | exactly one merge call, reached only from the human approval |
141
+ | `AutomationGateArchTest` | every patch-applying entry point depends on `AutomationGateService` |
142
+
143
+ vh-agents has its own guards in `shared/src/test/.../governance/`
144
+ (`NoDirectProviderCallsGuardTest`, `NoAutonomousMergeArchitectureTest`, the
145
+ `PolicyGateGuard*Test` family) and `orchestrator/.../automation/AutomationGateArchTest`.
146
+ Its root package is `io.visionhammer.agents.*`. The layered layout is not enforced there
147
+ yet: new packages follow the same four layers, and only `shared/.../llm/` (`LlmPort`,
148
+ `LiteLlmEndpoint`, `LiteLlmChatAdapter`) may reach the LiteLLM proxy.
149
+
150
+ Never weaken a guard to get a PR green: fix the dependency instead.
151
+
152
+ ## Other stacks, same idea
153
+
154
+ | Repo | Edge | Application | Domain / core | Adapters |
155
+ |---|---|---|---|---|
156
+ | frontend | `pages/`, route guards | hooks (React Query) | `lib/` pure functions and types | `lib/api/*` (only place that does HTTP) |
157
+ | codescan | `src/main.py` CLI, `src/worker/` | worker pipeline | `src/scoring`, `src/normalization` (pure) | engines (semgrep/gitleaks/trivy), `src/llm/litellm.py`, `src/db` |
158
+ | vh-litellm | `config.yaml` | `vh_plugins/smart_router.py` hook | `vh_plugins/smart_router_policy.py` (no litellm import) | LiteLLM runtime |
159
+ | local-agent | `src/routes`, `server.js` | `src/workspace`, `src/patch` | `src/detectors`, `src/validators` | `src/adapters/*`, `src/git`, tunnel |
160
+ | forge app | `manifest.yml` endpoints | `src/resolvers` | none (glue only) | `vh-gateway` remote |
161
+
162
+ The same rules apply: the core does not import the edge or the adapters, I/O goes behind
163
+ one adapter module, and pure logic has tests without network or database.
@@ -0,0 +1,45 @@
1
+ # JavaScript on Node.js
2
+
3
+ Applies to deepdebug-local-agent: plain JavaScript, ESM (`"type": "module"`), Node 18+
4
+ (CI publishes with Node 20), and the `node:test` runner. It runs **inside customer
5
+ infrastructure**, so every mistake is ours on their machine.
6
+
7
+ ## Style
8
+
9
+ - ESM `import`/`export` only; no `require`. Use `node:` prefixes for built-ins
10
+ (`node:fs/promises`, `node:child_process`).
11
+ - `async`/`await`, no callback pyramids. Every promise is awaited or explicitly handled.
12
+ An unhandled rejection is a bug.
13
+ - Add JSDoc `@param`/`@returns` types on exported functions. Validate inputs at route
14
+ handlers.
15
+ - New code goes in focused modules under `src/` (`routes/`, `workspace/`, `adapters/`,
16
+ `git/`, `validators/`). Don't grow `server.js` further. When you touch it, extract.
17
+ - Constants for ports, timeouts and limits; config from `~/.deepdebug/config.json` and env
18
+ vars.
19
+ - Use a logger with levels, not ad-hoc `console.log` spam, and never log secrets
20
+ (`DEEPDEBUG_API_KEY`, tokens, git credentials in URLs).
21
+
22
+ ## Security (local-agent specific)
23
+
24
+ - **Process execution:** `spawn`/`execFile` with an argument array and an explicit `cwd`
25
+ inside the workspace. Never `exec` with an interpolated string or `shell: true` with any
26
+ user, gateway or repo input. Validate branch names, refs and file paths against strict
27
+ patterns first.
28
+ - **Filesystem:** resolve and confine every path to the workspace root (reject `..`,
29
+ absolute paths and symlink escapes) before reading, writing or patching. Keep backups
30
+ under the configured backup dir, never `/tmp` on shared mounts.
31
+ - **Network:** the agent talks to the configured gateway (HTTP and WS tunnel) and to local
32
+ services it was told to watch. No other egress. New listeners (HTTP, MCP) must bind to
33
+ localhost by default and require the agent token; don't widen existing ones.
34
+ - **Log ingestion:** customer log lines can contain secrets and PII. Redact known token
35
+ shapes before forwarding, and forward only what the incident needs.
36
+ - **No LLM calls from here.** AI work happens in vh-agents through vh-litellm; the agent is
37
+ the hands (logs, workspace, git, tunnel), not the brain.
38
+ - **Supply chain:** the npm package is published from `main` (`npm-publish.yml`). Keep
39
+ dependencies few and pinned; never commit `.tar.gz` bundles, keys or credentials.
40
+
41
+ ## Tests
42
+
43
+ `npm test` (`node --test`). Adapters are tested against fixtures in `test/fixtures/`;
44
+ process and git calls are tested with fakes at the `child_process` boundary. Every guard
45
+ (path confinement, open gate, capability access) has a test proving the reject path.