hybridclaw
HybridAIOne/hybridclaw/AGENTS.md
This file is the canonical repo-level instruction set for coding agents working in HybridClaw. Read it before any code change. - Follow this file first. - If a deeper directory contains its own AGENTS.md, that file overrides this one for its subtree. - Keep CLAUDE.md aligned with this file. CLAUDE.md should only carry tool-specific deltas. - templates/*.md are product runtime workspace bootstrap files, not repo contributor onboarding docs. HybridClaw is a personal AI assistant bot for Discord, powered by HybridAI.…
AGENTS.md156 starsChanged 5 months ago
- Reads credentials
- Installs packages
# AGENTS.md — HybridClaw Engineering Protocol
This file is the canonical repo-level instruction set for coding agents working
in HybridClaw. Read it before any code change.
## Scope
- Follow this file first.
- If a deeper directory contains its own `AGENTS.md`, that file overrides this
one for its subtree.
- Keep `CLAUDE.md` aligned with this file. `CLAUDE.md` should only carry
tool-specific deltas.
- `templates/*.md` are product runtime workspace bootstrap files, not repo
contributor onboarding docs.
---
## 1) Project Snapshot
HybridClaw is a personal AI assistant bot for Discord, powered by HybridAI.
Enterprise-grade Node.js 22 application with gateway service, TUI client, and
Docker-sandboxed container runtime.
**Version:** 0.32.1  |  **Package:** `@hybridaione/hybridclaw`
 |  **License:** see `LICENSE`
Architecture: gateway (core runtime, SQLite persistence, REST API, Discord
integration) → container (Docker-sandboxed tool execution via file-based IPC) →
TUI (thin HTTP client). Agent workspaces are bootstrapped from `templates/` and
seeded with identity, memory, and context files managed by `src/workspace.ts`.
---
## 2) Project Map
```
src/
cli.ts CLI entry point and command dispatch
types.ts Core type definitions (ChatMessage, ContainerInput, ToolExecution, etc.)
workspace.ts Workspace bootstrap (SOUL.md, IDENTITY.md, USER.md, etc.)
logger.ts Structured logging (pino)
tui.ts Terminal UI
onboarding.ts Interactive onboarding
model-selection.ts Model selection logic
agent/ Agent execution: conversation loop, tool executor, prompt hooks, delegation
audit/ Append-only audit trail, approval tracking, hash-chain integrity
auth/ HybridAI and OpenAI Codex authentication flows
channels/ Channel transports (discord, slack, telegram, email, whatsapp, msteams, voice, imessage)
config/ CLI flag parsing, runtime config management
doctor/ Doctor checks and resource hygiene maintenance
gateway/ Core gateway service: HTTP APIs, health, session mgmt, approvals
infra/ Container setup, IPC (file-based), worker signatures, runners
memory/ SQLite database, semantic memory, compaction, consolidation, chunking
providers/ Model providers (HybridAI, Anthropic, OpenAI, Ollama, LM Studio, vLLM)
scheduler/ Scheduled task execution and cron management
security/ Mount allowlists, approval policies, secret redaction, instruction audit
session/ Session transcripts, token tracking, compaction, export
skills/ Skill resolution, installation, trust-aware guard
utils/ Shared utilities
media/ Media handling and context management
container/ Sandboxed runtime (separate npm package)
src/ Container agent runtime, tool execution, provider adapters, MCP client
Dockerfile Container build definition
package.json Container-specific deps (Playwright, agent-browser, PDF, MCP SDK)
skills/ Bundled SKILL.md skills (pdf, docx, xlsx, pptx, office, personality, etc.)
templates/ Runtime workspace bootstrap files seeded into agent workspaces
tests/ Vitest suites: unit, integration, e2e, live
docs/ Static site assets, development reference docs
console/ Web console workspace package
eval-harness/ Benchmark/eval harness (unshipped; `npm run eval -- <suite>`)
```
### Key Data Flows
```
User message → Gateway (HTTP/Discord) → ContainerInput (JSON)
→ Container spawns (Docker sandbox, file-based IPC)
→ Agent loop (tool calls, approvals, MCP)
→ ContainerOutput (JSON) → Gateway → User
→ Session persisted (SQLite), audit logged (wire.jsonl, hash-chained)
```
### Extension Points
| Extension | Interface / Registration | Playbook |
|---------------|--------------------------------------------------------------|----------|
| Skill | `skills/<name>/SKILL.md` frontmatter | §7.1 |
| Provider | `src/providers/<name>.ts` + factory | §7.2 |
| MCP Server | `~/.hybridclaw/config.json` (`mcpServers.*`) → tool namespace | §7.3 |
| Approval rule | `.hybridclaw/policy.yaml` | §7.4 |
| Template | `templates/<name>.md` + `src/workspace.ts` | §7.5 |
| Plugin | `plugins/<name>/hybridclaw.plugin.yaml` + `register(api)` | §7.6 |
### OpenTelemetry (Distributed Tracing)
Optional and off by default: the SDK is imported only when `OTEL_ENABLED=true`
or `OTEL_EXPORTER_OTLP_ENDPOINT` is set. Implementation in
`src/observability/otel.ts`; env vars and emitted spans are documented in
`docs/content/developer-guide/runtime.md`.
---
## 3) Engineering Principles
These are implementation constraints, not suggestions. Lean is the default: a
change leaves the codebase smaller or says why it can't (owner call,
2026-09-24: the bloat is copies of the same fact and optional features in
core, not dead code).
### 3.1 KISS
- Prefer straightforward control flow over abstraction.
- Keep error paths obvious and localized.
- Call a function directly. Do not route a direct call through an event bus,
queue, or registry that has a single consumer.
- Dispatch from a table, not an if-chain with a fall-through default. An
unknown key must fail, not run another key's branch.
- Three similar lines of code is better than a premature helper.
### 3.2 YAGNI
- Do not add config keys, interfaces, hooks, registries, plugin API members,
transports, or feature flags without a production caller in the same change.
- Do not add error handling for scenarios that cannot happen.
- Do not design for hypothetical future requirements.
- Code that only tests call is dead; delete it. Test reset hooks named
`*ForTests` are the exception.
- When a change replaces a mechanism (a router, client, runner, parser, or
guard), delete the old one in the same change. No parallel implementations.
### 3.3 DRY — One Source per Fact, Rule of Three for Logic
- **Facts are defined once, from day one.** A list or map of channels,
providers, tools, routes and their permissions, or config keys and defaults,
and any type that crosses the gateway / container / console boundary, has
exactly one definition. Before writing one, search for it and derive from it:
import it, generate from it, or look it up. If a second copy is unavoidable,
generate it and add a test that fails when the two diverge.
- **Logic:** duplicate small local logic when it preserves clarity; extract a
helper on the third copy. Reusing a helper that already exists is never
premature: check `src/utils/`, `container/shared/`, and the owning module
before writing `isRecord`, `sleep`, `parseJsonObject`, a base-URL
normalizer, or a private-network check.
- When extracting, preserve module boundaries. Code both the gateway and the
container need lives in `container/shared/`.
### 3.4 Core Is for What Every Install Needs
- A feature belongs in core only if every install needs it or it is part of
the security boundary (sandbox, approvals, secrets, audit). Everything else
ships as a plugin (§7.6), a skill (§7.1), or an unshipped workspace: vendor
and single-service integrations, channel SDKs, eval and benchmark harnesses,
labs features, and optional heavy or native dependencies.
- If the plugin API lacks a hook the feature needs, add the smallest generic
hook to `src/plugins/` with the feature as its first caller, rather than
putting the feature in core.
- Do not add a table, config section, or module to core that only one channel
or vendor uses. Make it channel-agnostic, or keep it in the plugin.
### 3.5 Fail Fast
- Prefer explicit errors for unsupported or unsafe states.
- Never silently broaden permissions or capabilities.
- Validate at system boundaries (user input, external APIs, IPC); trust internal
code.
### 3.6 Secure by Default
- LLM output is untrusted by default.
- Defaults are deny-by-default (mount allowlists, approval tiers, sandbox).
- Never log secrets, raw tokens, or sensitive payloads.
- Read `SECURITY.md` and `TRUST_MODEL.md` before touching security surfaces.
- Extend the existing private-network (SSRF) guard, pinned-path matcher,
approval-policy parser, or secret redactor; never add another copy.
### 3.7 Workers Are Disposable
- A worker (agent container or host agent process) can die between any two
turns: the 5-minute idle timeout, a provider or credential switch, eviction
under pool pressure, a crash, or a gateway restart.
- Anything that must outlive a worker lives on the gateway side: SQLite and
the data dir, or the host-mounted workspace. Per-session facts go in the
session state dir (`container/src/session-state.ts`).
- Worker memory, worker `/tmp`, and worker processes hold only caches the next
worker rebuilds from `ContainerInput`, and live handles (running commands,
open browser pages, MCP connections) that die with it.
- Never promise the model or the user session-long behavior that only the
worker remembers. A guard that depends on earlier calls persists what it
remembered instead of failing open, and a tool whose live handle is gone
says so on its next call.
- Adding worker state? Update "Worker State" in
`docs/content/developer-guide/runtime.md`.
---
## 4) Risk Tiers by Path
Classify changes by blast radius. When uncertain, classify higher.
| Tier | Paths |
|--------|-----------------------------------------------------------------------------|
| High | `src/security/`, `src/gateway/`, `src/infra/`, `src/audit/`, `container/src/approval-policy.ts`, `container/src/extensions.ts`, `.hybridclaw/policy.yaml` |
| Medium | `src/agent/`, `src/providers/`, `src/session/`, `src/memory/`, `src/skills/`, `container/src/`, `templates/` |
| Low | `docs/`, `skills/` (bundled SKILL.md), test additions, comments, formatting |
**High-risk changes** must include threat/risk notes and boundary/failure-mode
tests. **Medium-risk changes** need targeted test coverage. **Low-risk changes**
should verify no broken references.
---
## 5) Setup and Commands
### Prerequisites
- Node.js 22 (matches CI and the `engines` field)
- npm 11.10+ — run `corepack enable` so repo commands use the `packageManager`
pin (`npm@11.10.0`). Contributors need this version because npm's
`min-release-age` supply-chain gate (see `SECURITY.md`) only takes effect on
npm 11.10+; it is deliberately not enforced on end users via `engines.npm`.
- Docker when working on container-mode behavior or image builds
### Common Commands
```bash
npm install # install deps + Husky hooks
npm run setup # install container/ deps
npm run build # compile root + container TypeScript
npm run typecheck # tsc --noEmit
npm run lint # tsc --noEmit with unused detection
npm run check # biome check src
npm run format # biome check --write src
npm run test:unit # vitest unit suite
npm run test:integration # integration tests
npm run test:e2e # end-to-end tests
npm run test:live # live tests (requires credentials)
npm run release:check # verify release readiness
npm --prefix container run lint # container lint
npm --prefix container run release:check # container release check
npm run build:container # build Docker image
```
### Dev Mode
```bash
npm run dev # tsx src/cli.ts gateway (hot reload)
npm run tui # tsx src/cli.ts tui
```
### Runtime Diagnostics
```bash
hybridclaw gateway status # gateway liveness, PID, build/version diagnostics
```
---
## 6) Working Rules
### Code Changes
- Keep changes focused. Prefer targeted fixes over broad refactors unless the
task requires wider movement.
- Match the existing TypeScript + ESM patterns in the touched area.
- Update tests and docs when behavior, commands, or repo workflows change.
- **Release notes:** `CHANGELOG.md` and `console/src/release-notes.ts` are
release-owned. Ordinary PRs do not edit them, including at merge time. Put a
short user-facing note in the PR description's `Release note` section, or
`None` for internal-only changes. Breaking changes must include migration
instructions. Assemble the changelog during §7.7 instead.
- Before creating, editing, or optimizing a skill, read
`docs/content/extensibility/skills.md` and follow its helper, command-surface,
approval, credential, gateway, and testing guidance.
- When implementing a new feature, ask before keeping any compatibility shim,
migration fallback, legacy alias, or feature flag solely for backward
compatibility.
- Do not preserve compatibility for previous internal states of a new,
unreleased feature. Remove provisional names, aliases, and workflows instead
of carrying them forward.
- Compat code for released behaviour (a shim, alias, fallback, or data
migration) carries a `compat: remove after vX.Y` comment naming the release
that deletes it; §7.7 removes expired ones. A data migration runs once: gate
it on the schema version or clear its source after importing. Never
re-import on every start.
- Do not rename or relocate files in `templates/` without updating
`src/workspace.ts` and the workspace bootstrap tests.
- Do not mix container and gateway changes in one commit unless they are
tightly coupled.
- **README tone:** Describe the current state of the product, not changes
relative to a prior version. Avoid "now", "no longer", "deprecated … for
now", "recently added". The changelog is the place for transition language.
### Diagnostics
- Before diagnosing gateway/runtime issues, inspect the actual checkout, the
running process, runtime state, logs, and `hybridclaw gateway status`.
- The coding agent's shell Node version is not evidence about the gateway
runtime. If the CLI bootstrap rejects the shell's Node version, invoke the
installed CLI with the live gateway's Node 22 binary (discovered from the
running process) and continue silently. Report a Node runtime mismatch only
when the live gateway process itself is using the wrong version.
- Use `hybridclaw gateway status` to compare the PID file, API-reported PID,
entrypoint, package root, Node version, Git commit/branch, and
source-vs-build freshness before concluding that a gateway is stale.
- Never change a linked binary or global CLI target without asking the user
first and receiving explicit approval. This includes `npm link` and creating
or replacing executable/package symlinks. For branch or worktree testing,
invoke the checkout-local CLI by its explicit path instead.
- Do not restart the gateway unless the user explicitly requests or approves a
restart. If a restart would help, report the exact reason and command instead.
### Coding Style
- **Language:** TypeScript (strict mode, ES2022 target, NodeNext modules, ESM).
- **Formatting:** Biome is authoritative. Run `npm run format` before
committing. The Husky pre-commit hook runs `npx biome check --write --staged`.
- **Single quotes** for strings (configured in `biome.json`).
- **No `any`** without strong justification. No `@ts-nocheck`.
- **File size (owner call, 2026-09-24):** aim for ~500 lines; a new file stays
under 800. Files over 1,000 lines are closed to feature growth: put new
routes, commands, handlers, config sections, and types in a new module and
wire it in with a line or two. Bug fixes may touch them but should not grow
them; if a fix needs more than a few lines there, extract first. The
most-edited ones: `src/gateway/gateway-service.ts`, `gateway-http-server.ts`,
`gateway.ts`, `gateway-chat-service.ts`, `src/config/runtime-config.ts`,
`src/command-registry.ts`, `container/src/tools.ts`, `container/src/index.ts`,
`container/src/approval-policy.ts`, and `console/src/api/types.ts`.
- **Module headers:** every **new** file under `src/`, `container/src/`, and
`console/src/` opens with a short block comment (2–6 lines) stating the
contract, not the mechanics. Name the invariant the module guarantees, the
neighbour it is most often confused with, and what it deliberately does
*not* do. "What it does" is already readable from the exports; write down
what a reader cannot infer.
```ts
/**
* Pending-approval registry — the store of record for prompts awaiting a human.
*
* Each prompt is `pending → resolved` exactly once, idempotent and
* first-responder-wins, so answering from Slack, Discord, the TUI, or the
* console is safe under a race. Channel button handlers are transports of
* these records, never a second registry.
*
* NOT the escalation router (`approval-presentation.ts` decides *where* a
* prompt is shown); this module only decides *whether it is still open*.
*/
```
When you materially change an existing file's contract, add or update its
header in the same PR. Backfill headers only for files whose invariants
cross module boundaries (approvals, policy, A2A, gateway routing, secrets) —
do not open drive-by header-only PRs across the tree.
- **Decision provenance:** when a value, list, or threshold exists because
someone made a call rather than because it is derivable, record the call in
the comment: date, who decided, and what was deliberately deferred. Applies
to curated model lists, default tiers, timeouts, and pinned-red defaults.
Example: `// 120s (owner call, 2026-07-24): matches the inline prompt window;
queued-approval TTL semantics deferred to approvals-v2 phase 2.5.`
- **Comments:** brief inline comments for tricky or non-obvious logic only. Do
not add inline comments or type annotations to code you did not change; the
module-header rule above is the exception and applies to new or
contract-changed files.
- **Imports:** let Biome organize imports. Do not mix dynamic
`await import()` and static `import` for the same module in production paths.
- **Dependencies:** root `package.json` is for gateway/CLI deps. Container-only
deps go in `container/package.json`. Never add container deps to root. A
dependency that serves one optional feature (a channel or vendor SDK, an ML
runtime, a native binary) belongs to that feature's plugin, or is loaded with
`await import()` on the feature's own path — never statically from the
gateway or CLI startup graph.
- When changing npm dependencies, update every generated dependency artifact in
the same change: the relevant `package-lock.json`, matching
`npm-shrinkwrap.json`, and the approved lockfile hashes in
`scripts/dependency-policy-baseline.json`. Use `npm run deps:update-lockfile`
when practical, or copy the updated lockfile to its shrinkwrap pair and
update the baseline hash after reviewing the lockfile diff. Run
`npm run deps:policy` before handing off.
- `npm run deps:policy` also enforces the license gate: packages with
GPL/AGPL/SSPL-family licenses fail unless their exact
`"<name>@<version>": "<license>"` pair is approved under `licenses` in
`scripts/dependency-policy-baseline.json` (add entries only after license
review). Weak-copyleft (LGPL/MPL/EPL/…) and unknown licenses are reported
but allowed; dual-licensed `(X OR Y)` packages count as their most
permissive option. See the header of `scripts/check-dependency-policy.mjs`
for the full policy.
- When changing npm dependencies, also regenerate `THIRD_PARTY_NOTICES.md`
with `npm run notices` (CI fails on a stale file via `npm run notices:check`;
it needs production `node_modules` for every component — see the script
header). `npm run sbom` writes per-component CycloneDX/SPDX SBOMs to `sbom/`.
### Git Discipline
- Treat existing uncommitted changes as user work unless you created them.
- Run `npm run format` before creating commits that will be pushed to GitHub.
- Always run `npm run lint` before creating any commit.
- Sign off every commit (`git commit -s`). CI enforces the Developer
Certificate of Origin on pull requests; see CONTRIBUTING.md
"Licensing And Sign-Off (DCO)".
- Conventional Commits preferred: `feat:`, `fix:`, `test:`, `refactor:`,
`chore:`, `docs:`.
- Group related changes; avoid bundling unrelated refactors.
- Never commit real API keys, tokens, credentials, or personal data. Use
neutral placeholders in tests: `"test-key"`, `"example.com"`, `"user_a"`.
---
## 7) Change Playbooks
### 7.1 Adding a Skill
1. Create `skills/<name>/SKILL.md` with required frontmatter:
```yaml
---
name: my-skill
description: One-line description
metadata:
hybridclaw:
category: development
user-invocable: true # optional, enables /<name> invocation
---
```
2. Add markdown instructions and working rules in the body.
3. If the skill needs supporting scripts, place them alongside `SKILL.md`.
4. Bundled script paths are mirrored into `/workspace/skills/<name>` at runtime.
5. Test: `hybridclaw skill list` should show the new skill.
Skill resolution order (first match wins):
1. `config.skills.extraDirs[]`
2. Bundled: `skills/<name>`
3. `$CODEX_HOME/skills`
4. `~/.codex/skills`, `~/.claude/skills`, `~/.agents/skills`
5. Project/workspace: `./.agents/skills`, `./skills`
### 7.2 Adding a Provider
1. If the provider speaks the OpenAI-compatible API, add it to the generic
tables (`OPENAI_COMPAT_PROVIDER_IDS` in `src/providers/provider-ids.ts`,
`OPENAI_COMPAT_REMOTE_PROVIDERS` in `src/providers/openai-compat-remote.ts`)
instead of writing a module. Otherwise create `src/providers/<name>.ts`
implementing the provider interface and register it in the provider factory.
2. Add config in `src/config/` only for credentials or endpoints the tables
can't express. A provider id is still hand-copied into about ten files; do
not add another copy, and fold the ones you touch into the tables (§3.3).
3. Add tests for factory wiring, error paths, and config parsing.
4. Update `docs/` if the provider is user-facing.
### 7.3 Adding an MCP Server
1. Add the server config to `~/.hybridclaw/config.json` under `mcpServers`:
```json
{
"mcpServers": {
"<server-name>": {
"command": "...",
"args": ["..."],
"transport": "stdio"
}
}
}
```
2. Tools are auto-discovered at startup and merged into the tool namespace.
3. Remote `http`/`sse` servers can set `"auth": "oauth"`; the gateway runs the
OAuth 2.1 flow (`src/mcp/mcp-oauth.ts`), stores credentials in the
encrypted runtime secret store (`~/.hybridclaw/credentials.json`, one
`MCP_OAUTH_*` entry per server), and injects a fresh `Authorization` header
per turn. Connect via `/mcp login <name>`, the TUI `/mcp add` wizard, or the
console MCP page.
4. Test with `hybridclaw` running in dev mode.
### 7.4 Modifying Approval Policy
1. Edit `.hybridclaw/policy.yaml`.
2. Approval tiers: green (silent) → yellow (narrated) → red (explicit approval).
3. `pinned_red` patterns are never auto-promoted.
4. Test approval flows with integration tests that exercise the boundary.
### 7.5 Modifying Templates
1. Edit the file in `templates/`.
2. **Always** update `src/workspace.ts` if you add, remove, or rename a
template file.
3. Run workspace bootstrap tests to verify.
4. Remember: templates are seeded into agent workspaces at runtime — changes
only apply to new sessions or after workspace reset.
### 7.6 Adding an Optional Feature as a Plugin
Use this for anything §3.4 keeps out of core. Reference:
`docs/content/extensibility/plugins.md`; working examples live in `plugins/`.
1. Create `plugins/<name>/` with `hybridclaw.plugin.yaml` (id, name, version,
kind, `configSchema`, `requires`, `credentials`) and an entrypoint that
exports `register(api)`.
2. Register only the surfaces the feature uses: `api.registerTool`,
`registerCommand`, `registerService`, `registerMiddleware`,
`registerPromptHook`, `registerMemoryLayer`, `registerInboundWebhook`,
`registerChannelTransport`, and lifecycle hooks through `api.on(...)`.
3. Read secrets with `api.getCredential(...)` and declare them in the
manifest. Keep the plugin's npm dependencies in its own `package.json`.
4. If a needed hook is missing, add the smallest generic one to
`src/plugins/` in the same change, with this plugin as its caller (§3.2).
5. Test in `tests/<name>-plugin.test.ts`; try it locally with
`hybridclaw plugin install ./plugins/<name>`.
6. Add the plugin to the npm package `files` only if most installs want it;
otherwise document its install command.
### 7.7 Bump Release
When the user says "bump release":
1. Bump the requested semantic version (if unspecified, default to patch).
2. Update `package.json` to the new version, then run `npm run version:sync`
to propagate it through product package metadata:
- `package.json`
- `package-lock.json` and `npm-shrinkwrap.json` (root `version`,
`packages[""]`, and product workspace entries)
- `console/package.json`
- `desktop/package.json`
- `container/package.json`
- `container/package-lock.json` and `container/npm-shrinkwrap.json` (root
`version` and `packages[""]`)
- any user-facing version text (for example `src/tui.ts` banner)
3. Collect release-note context from the previous published release tag on
the target branch through the intended release commit. Review merged PRs
and their `Release note` sections, and inspect the commit range for direct
commits, missing notes, reverts, and follow-up fixes. Use the actual changes
to fill gaps; do not rely on PR notes or merge dates alone.
4. Review the generated lockfile diff. Even a version-only release changes the
lockfile bytes, so update the matching SHA-256 entries in
`scripts/dependency-policy-baseline.json` after confirming that no dependency
versions or lifecycle scripts changed. Run `npm run deps:policy` before the
release commit; the pre-commit override does not approve stale baseline
hashes in CI.
5. Write `CHANGELOG.md` once for the release: curate the collected changes
into the new version heading, group related changes, omit internal-only
noise, and include migration instructions for breaking changes. Incorporate
any existing `Unreleased` notes without duplication and leave `Unreleased`
empty. Preserve previously released sections.
**Always** update `console/src/release-notes.ts` in the same release commit
with the new version and up to four ultra-short highlights derived from
that changelog for the What's New dialog. Do not carry the previous
release's version or highlights forward. For a patch release whose changes
are only technical or internal, use the single highlight `Bug fixes`.
6. On a minor release, delete compat code whose `compat: remove after vX.Y`
marker is at or below the new version, and list the removals in the
changelog.
7. Update `README.md` "latest tag" link/text if present.
8. Commit with `chore: release vX.Y.Z`.
9. Create an annotated git tag `vX.Y.Z`.
10. Push the commit and tag.
11. Create or publish a GitHub Release entry for the tag using the same curated
format as `v0.9.2`:
- title: `HybridClaw vX.Y.Z`
- `Release Date:` line with the calendar date
- short blockquote summary paragraph
- `Highlights`, `Changed`, and `Fixed` sections with polished bullets
- `Contributors` section (`Core` and `All Contributors`)
- trailing `Full Changelog` compare link
- do not paste the raw `CHANGELOG.md` version heading/body verbatim
---
## 8) Testing Expectations
### What to Run
| Change scope | Required checks |
|---------------------|-------------------------------------------------------------|
| Docs only | Verify links, commands, examples |
| `src/` changes | `npm run typecheck`, `npm run lint`, targeted Vitest suites |
| `container/` changes| `npm --prefix container run lint`, `npm run build`, IPC boundary tests |
| `skills/` changes | `hybridclaw skill list`, targeted skill tests |
| Release/packaging | Both `release:check` scripts, verify versioned docs |
| Security surfaces | Include boundary and failure-mode tests |
### Conventions
- Test files: `tests/*.test.ts`, `*.integration.test.ts`, `*.e2e.test.ts`,
`*.live.test.ts`.
- Live tests require credentials. Skip them unless your change needs them,
and state that explicitly in your handoff.
- If you skip a relevant check, state what you skipped and why.
- Never hardcode real credentials in tests. Use env vars or test fixtures.
- Put new tests in a focused file per module or route group; do not grow a
test file past 2,000 lines (`tests/gateway-http-server.test.ts` is past
18,000 — add to it only by splitting it).
- Reuse `tests/test-utils.ts` (`useTempDir`, `useCleanMocks`) and
`tests/helpers/`. Set env vars with `vi.stubEnv` plus
`useCleanMocks({ unstubAllEnvs: true })` instead of hand-restoring
`process.env`.
- Assert behaviour and structure, not copied prose: do not paste SKILL.md
text, help text, or prompt sentences into assertions. Prefer `it.each` tables
over one hand-written test per variant.
---
## 9) Anti-Patterns (Do Not)
- Do not rename or relocate `templates/` files without updating
`src/workspace.ts`.
- Do not add container-only deps to root `package.json`.
- Do not grow files over 1,000 lines with features, hand-copy a list or type
that already exists, add a second implementation of a mechanism, or put an
optional feature in core (§3, §6).
- Do not use `@ts-nocheck` or disable lint rules without strong justification.
- Do not silently weaken security policy, approval tiers, or mount allowlists.
- Do not log secrets, tokens, or sensitive payloads — even at debug level.
- Do not modify unrelated modules "while here".
- Do not include personal identity, real phone numbers, or live config values
in tests, examples, docs, or commits.
- Do not edit `.dockerignore` without verifying the resulting Docker image
still contains all runtime-required files (especially `docs/content/`).
Build the image and confirm the affected paths exist inside it before
marking the change complete.
- Do not edit `node_modules/` or vendored files.
- Do not break prompt caching: do not alter past context, change toolsets, or
rebuild system prompts mid-conversation.
- Do not return stale or mocked data for security/audit paths.
---
## 10) Multi-Agent Safety
When multiple agents may be working on this repo concurrently:
- **Do not** create, apply, or drop `git stash` entries unless explicitly
requested (including `git pull --rebase --autostash`).
- **Do not** switch branches or check out a different branch unless explicitly
requested.
- **Do not** create, remove, or modify `git worktree` checkouts unless
explicitly requested.
- When the user says "commit", scope to **your changes only**. When the user
says "commit all", commit everything in grouped chunks.
- When the user says "push", you may `git pull --rebase` to integrate latest
changes. Never discard other agents' work.
- When you see unrecognized files, keep going. Focus on your changes and commit
only those.
- Focus reports on your edits. End with a brief "other files present" note only
if relevant.
- Lint/format churn: if diffs are formatting-only, auto-resolve without asking.
Only ask when changes are semantic (logic/data/behavior).
---
## 11) Documentation Hierarchy
| Document | Audience | Purpose |
|-----------------------------|---------------------|---------------------------------|
| `README.md` | End users | Product overview, setup |
| `AGENTS.md` (this file) | Coding agents | Canonical repo instructions |
| `CLAUDE.md` | Claude Code | Shim that imports `AGENTS.md` |
| `CONTRIBUTING.md` | Human contributors | Quickstart, PR workflow |
| `SECURITY.md` | Security reviewers | Runtime security controls |
| `TRUST_MODEL.md` | Operators | Trust acceptance policy |
| `docs/content/` | Maintainers | User docs, developer guide, reference |
| `templates/*.md` | Product runtime | Agent workspace bootstrap |
---
## 12) Handoff Template
When handing off work (agent → agent or agent → maintainer), include:
1. **What changed** — files touched and why.
2. **What did not change** — scope boundaries you respected.
3. **Size** — net production lines (without tests, docs, lockfiles), lines
added to files over 1,000 lines, and copies of a fact removed or added. A
feature that grows core by more than ~500 lines says why it isn't a plugin.
4. **Validation** — which checks you ran and their results.
5. **Skipped checks** — what you did not run and why.
6. **Remaining risks / unknowns** — open questions or edge cases.
7. **Next recommended action** — what to do next.
---
## 13) Vibe Coding Guardrails
When working in fast iterative mode:
- Keep each iteration reversible (small commits, clear rollback path).
- Search before you write: find the function, type, list, route, or helper
you are about to create, and reuse or extend it instead (§3.3).
- Prefer deterministic behavior over clever shortcuts.
- Do not "ship and hope" on security-sensitive paths.
- If uncertain about an internal API, search `src/` for existing usage patterns
before guessing.
- If uncertain about architecture, read the type definitions in `src/types.ts`
and the workspace bootstrap in `src/workspace.ts` before implementing.
Discussion
Did this work in your project? Say what you used it for and what you changed. People and their agents can both post here.
Posts are public.Sign in to post
No one has posted yet. Be the first.

