chore: bootstrap repo with AGENTS.md, gitignore, and project-specific agent specs

- AGENTS.md adapted from alkcall: shared Rust conventions (no comments,
  thiserror, tokio, no-env-vars, OperationEnv trait) plus alkhttp-specific
  rules (HTTP surface as stable contract, gateway endpoints, feature flags
  h2/http1/mcp, five-subsystem module structure, adapter Internal-by-default)
- Root .gitignore (target/, node_modules/, .worktrees/)
- Purged alkcall/alknet residuals from agent specs: coordinator prompt
  template now targets @alkdev/alkhttp with h2/http1/mcp feature flags,
  architect deferral examples reference alkhttp, implementation-specialist
  conventions rewritten for the HTTP crate (no BAST/chunk-header/abort-cascade
  rules), code-reviewer feature flags updated, sdd_process.md package name fixed
- Dropped the preconfigured git-identity note (glm-specific, not applicable)

Verification: no cargo code changed; grep confirms no residual
alkcall/alknet/glm/iroh/quinn/acme/BAST references outside intentional
alkcall-dependency and alknet-extraction-origin mentions
This commit is contained in:
2026-08-25 08:44:26 +00:00
commit 9c714382dc
11 changed files with 3169 additions and 0 deletions
+239
View File
@@ -0,0 +1,239 @@
# AGENTS.md
Operating instructions for opencode agents working in this repo. opencode
auto-loads this file as instructions, overriding the built-in defaults for
this project. Custom agents in `.opencode/agents/` inherit these rules
unless their own prompts say otherwise.
## Git Workflow
**Commit and push when reasonable.** When a change is complete and
verified (build + lint + tests pass), commit and push to `origin/main`
without asking. This overrides the built-in default of "only commit when
explicitly asked."
The workflow:
1. Make the change
2. Verify: `cargo test`, `cargo clippy --all-targets -- -D warnings`,
`cargo fmt --check`, `cargo doc --no-deps` if docs changed
3. Inspect `git status` and `git diff` before staging — stage only the
intended files, never secrets
4. Write a concise commit message matching the repo style (see `git log
--oneline -10`). For multi-point changes, use a summary line plus a
body with bullet points and a verification block.
5. `git push origin main`
6. Report the commit hash and the verification summary
Exceptions — **do not** commit or push without asking:
- The change is exploratory / speculative (you're not sure the user wants
it kept)
- The user is actively reviewing the diff and may ask for changes
- The change touches published wire formats or semver-relevant public API
(this crate will be on crates.io; the HTTP surface and the
HTTP-backed call-protocol adapters are the stable contract — see the
ADRs in `docs/architecture/decisions/`)
- You'd be force-pushing, amending a published commit, creating an empty
commit, or skipping hooks
Never commit secrets, keys, or credentials. If a commit fails or hooks
reject it, fix the issue and create a new commit — do not amend the
failed one.
## Project Conventions (Rust / HTTP interface crate)
This is the HTTP interface crate — the extraction of `alknet-http` from
the alknet mono-repo (`/workspace/@alkdev/alknet`). It serves HTTP/1.1 +
HTTP/2 on standard ALPNs (with WebSocket upgrade for browser bidirectional
access to the call protocol) and hosts the HTTP-backed call-protocol
adapters (`from_openapi`, `to_openapi`, `from_mcp`, `to_mcp`). The
conventions below apply to all work in `src/` and `tests/`. They mirror
`.opencode/agents/implementation-specialist.md` §Project Conventions and
are repeated here so they apply to every session, not just spawned
implementation agents.
1. **No comments in code** unless the user explicitly asks. This is a
project-wide convention. Doc comments (`///`, `//!`) are fine and
expected on public API. Inline `//` comments only when the user asks
or when a non-obvious safety/correctness constraint would otherwise be
missed.
2. **Error handling** — `thiserror` for library error types. No panics
in library code. No `unwrap()` or `expect()` outside tests. If you
reach for `unwrap`, the error path wasn't specified — stop and decide
what should actually happen. For poisoned `RwLock`/`Mutex`, use
`unwrap_or_else(|e| e.into_inner())` so a panic in one operation does
not cascade to other operations.
3. **`tokio` is the async runtime** — all I/O is async. The HTTP
server, the WebSocket upgrade path, and the reqwest-backed adapters
are all async. Do not introduce blocking I/O on the async path. Use
`tokio::sync` primitives (`oneshot`, `mpsc`) for request correlation
and subscription channels; `parking_lot` for short-held internal locks.
4. **No secret material on the wire** — the HTTP surface carries no
private keys, API keys, mnemonics, or decrypted credentials in
request/response payloads or headers. Outbound credentials flow
through `Capabilities` injected at the assembly layer →
`HandlerRegistration.capabilities` → `OperationContext.capabilities`
→ handler. The `from_openapi`/`from_mcp` adapters are the credential
injection point. See the no-env-vars invariant below.
5. **No-env-vars invariant** — no handler reads outbound credentials
from any source other than `OperationContext.capabilities`. The
credential injection path is vault → assembly layer → `Capabilities`
→ `HandlerRegistration.capabilities` → `OperationContext.capabilities`
→ handler. Downstream consumers' `std::env::var` reads are
unreachable because the assembly layer never calls `Default::default()`.
This is a spec-level invariant, not a runtime convention.
6. **`OperationEnv` must remain a trait** — the trait-based design
enables registry layering (session overlays, connection overlays,
peer-keyed composition). Making `OperationEnv` concrete or hardcoding
the global registry into the dispatch path would close the
session-overlay and connection-overlay patterns. This is the same
integration-point pattern as `IdentityProvider`.
7. **The HTTP surface is the stable contract** — the server serves
REST APIs, the `to_openapi`/`to_mcp` projections of local
call-protocol operations, the `/healthz` operational endpoint, and
the decoy surface for stealth mode. The gateway endpoints
(`/search`/`/schema`/`/call`/`/batch`/`/subscribe`) are the sole
invoke path for HTTP callers; the WebSocket path carries the native
call-protocol session, not the gateway shape. HTTP/3 + WebTransport
(`h3`) is deferred — browsers use WebSocket. See ADR-044, ADR-047,
ADR-048.
8. **Producer/consumer, not server/client** — both sides of a call or
channels connection can initiate. A producer exposes operations
(call) or opens data channels (channels); a consumer calls operations
or opens channels. Both sides can be both simultaneously — connection
direction (who opened it) is independent of call/channel direction
(who calls/opens). Avoid "server" and "client" framing in docs and
API names; use "producer" and "consumer," or "accept side" / "connect
side" for the connection-establishment half specifically.
9. **Dependency on the call crate** — this crate consumes the call
protocol from the alkcall crate (`/workspace/@alkdev/alkcall`), which
owns the vendored core types (`Connection`, `ProtocolHandler`,
`BiStream`, `BidiStreamSource`, `AuthContext`, `IdentityProvider`,
`Identity`, `AuthToken`, `Capabilities`, `OwnershipProvider`,
`HandlerError`, `StreamError`) and the `EventEnvelope` wire format.
Do not re-implement or fork those types here; do not add a separate
`alkcore` dependency. Keep this crate lean (no TLS, no transport
coupling, no endpoint/accept-loop); the dial and the TLS config are
concerns of the consumer, not of this crate.
10. **Feature flags** — the HTTP transports are feature-gated: `h2` and
`http1` are default features (hyper), `mcp` gates the
`from_mcp`/`to_mcp` adapters (rmcp). The base crate should compile
lean (no `rmcp` unless the `mcp` feature is on). Verify both
`cargo test` (default) and `cargo test --all-features` pass if
features are added.
11. **Naming** — Rust standard: `snake_case` for functions/variables/
modules, `PascalCase` for types/traits, `SCREAMING_SNAKE_CASE` for
constants.
12. **Module structure** — one module per file under `src/`, re-exported
from `src/lib.rs`. Public API surface is `lib.rs` re-exports. The
crate has five subsystems: `server` (HttpAdapter, auth, stealth,
`/healthz`, gateway routes), `websocket` (upgrade, native session
overlay), `adapters` (`from_openapi`, `to_openapi`, `from_mcp`,
`to_mcp`, `from_jsonschema`), `client` (reqwest-backed HTTP client
host), and `gateway` (dispatch, error mapping).
13. **Adapter-registered ops are `Internal` by default** — operations
registered by the adapters are `Visibility::Internal` unless
explicitly marked otherwise. Peer authorization is via
`AccessControl::check(peer_identity)` — no `remote_safe` flag, no
`trusted_peer` bypass. See ADR-015, ADR-024.
14. **Error fidelity across the HTTP boundary** — `from_openapi`/
`from_jsonschema`/`to_openapi` map call-protocol errors to HTTP
status codes with `HTTP_<status>` error codes. The gateway is the
sole invoke path; per-caller `AccessControl`-filtered `/search` is
the discovery. See ADR-023, ADR-047.
## Verification Commands
Run these before committing. All must pass.
```bash
cargo test # full suite
cargo clippy --all-targets -- -D warnings
cargo fmt --check
cargo doc --no-deps # if docs changed
cargo publish --dry-run --allow-dirty # before a release
```
If feature flags are added, also run `cargo test --all-features` and
`cargo clippy --all-features --all-targets -- -D warnings`.
## Architecture Context
- `docs/architecture/` — the authoritative spec. Read it before
non-trivial changes. ADRs are numbered 001..066; OQs (open questions)
track resolved/deferred decisions.
- This crate is the extraction of `alknet-http` from the alknet mono-repo
(`/workspace/@alkdev/alknet`). The source architecture docs were ported
from `/workspace/@alkdev/alknet/docs/architecture/crates/http/` and
renumbered as alkhttp ADRs. The ALPN strings (`h2`, `http/1.1`) and
the gateway endpoint contract are wire-stable going forward.
- Key ADRs that inform this crate's design:
**Server:**
- ADR-001 — ALPN-based protocol dispatch (`HttpAdapter` registers on
standard HTTP ALPNs)
- ADR-002 — `ProtocolHandler` trait (`HttpAdapter` implements it)
- ADR-004 — auth as shared core (Bearer → `resolve_from_token`)
- ADR-010 — ALPN router and endpoint (stealth mode = HTTP handler on
standard ALPNs)
- ADR-039 — HTTP server and client host colocated in one crate
- ADR-046 — assembly-layer custom HTTP routes on `HttpAdapter`
(`extra_routes: Option<Router>`)
- ADR-047 — remove the direct-call HTTP surface; the 5 gateway
endpoints are the sole invoke path
- ADR-048 — WebSocket carries the native call-protocol session, not
the gateway shape
**WebSocket:**
- ADR-044 — defer h3/WebTransport; browsers use WebSocket ("browser
is not a peer" rationale)
- ADR-048 — WebSocket native session (framing, dispatch,
bidirectionality, connection-local Layer 2 overlay)
**Adapters:**
- ADR-014 — secret material flow (`from_openapi`/`from_mcp` are the
credential injection point)
- ADR-015 — privilege model (adapter-registered ops are `Internal` by
default)
- ADR-017 — call protocol client and adapter contract
(`OperationAdapter` trait; `to_*` are projections)
- ADR-022 — handler registration, provenance, and composition
authority (adapters produce leaf bundles)
- ADR-023 — operation error schemas (`HTTP_<status>` error codes)
- ADR-041 — MCP tool-gateway pattern for `to_mcp` (4 fixed gateway
tools, not one tool per operation)
- ADR-042 — OpenAPI gateway pattern for `to_openapi` (5 fixed gateway
endpoints, not one path per operation)
- ADR-045 — `to_openapi` gateway-spec versioning (`info.version`
tracks the gateway endpoint contract)
- ADR-049 — streaming handler for subscription operations
(`HandlerKind::Stream`; SSE → `BoxStream<ResponseEnvelope>`)
- ADR-051 — YAML input format for `from_openapi` (`yaml_serde` 0.10.x
is YAML 1.2)
- ADR-066 — `from_jsonschema` as HTTP-backed single-endpoint adapter
**Client:**
- ADR-039 — one crate for server + client host (shared HTTP deps,
shared mapping)
- If a TODO references a "Phase B" or a design direction that an ADR
has since decided against, the TODO is stale — remove it and align
the comments with the ADR. Do not implement the rejected design.
- The call protocol's `EventEnvelope` shape is owned by the alkcall
crate (`/workspace/@alkdev/alkcall`), which was extracted from the
alknet mono-repo alongside this crate. The WebSocket path carries the
native `EventEnvelope` session.