- backend.md: git/repo/update pinned as PATCH (omitted fields unchanged; present fields replaced wholesale) — R-3; 'already_exists' disclosure posture recorded (accepted create-scope oracle; resolve-side stays collapsed) — R-6; repo-id grammar pinned (owner/name segments, rejection- only parsing, percent-encoded flat record-file naming, two-placeholder http-route note) — R-5 - doors.md: alkssh hand-off tuple gains service + authorized-repo marker (ADR-016/D-3 amendment rounds missed this third tuple site) — R-4 - ADR-016: collapsed-space and broken code-span glitches — R-8 - AGENTS.md: lifecycle paragraph records both gate reviews complete and decomposition unblocked Verification: docs-only; cargo doc/test/clippy/fmt clean
11 KiB
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."
Commit in small, focused units — one commit per unit of work (a fix, a feature, a doc change), not one large commit covering many topics. Push regularly so work is never stranded locally.
The workflow:
- Make the change
- Verify:
cargo test,cargo clippy --all-targets -- -D warnings,cargo fmt --check,cargo doc --no-depsif docs changed - Inspect
git statusandgit diffbefore staging — stage only the intended files, never secrets - Write a concise commit message in conventional commits style:
<type>(<scope>): <summary>— types arefeat,fix,docs,chore,refactor,test,perf,build,ci; the scope is optional. For multi-point changes, use a summary line plus a body with bullet points and a verification block. git push origin main- 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
(crates will be published to crates.io once architecture lands; see the
ADRs in
docs/architecture/decisions/for the stable-contract list once it exists) - 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.
Git identity is preconfigured (glm-5.3-flash <glm-5.3-flash@alk.dev>).
Do not change git config, skip hooks, or use git commit -i.
Project Conventions (Rust / protocol crate)
This is alkgit — a pure protocol crate (ADR-010, the alktty/alktunnels
template): the git smart protocol as producer/consumer halves on alkcall
channels (the alk/git ALPN), backend traits with a feature-gated
gitoxide (gix) implementation, no binary, no front doors (doors — alkhttp
git feature, alkssh — are family infrastructure; see
docs/architecture/doors.md). Single crate at the repo root (src/,
tests/). 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.
-
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 (e.g., "ACL must run before the first advertised ref line — ref names leak repository existence"). -
Error handling —
thiserrorfor library error types. No panics in library code. Nounwrap()orexpect()outside tests. If you reach forunwrap, the error path wasn't specified — stop and decide what should actually happen. For poisonedRwLock/Mutex, useunwrap_or_else(|e| e.into_inner())so a panic in one operation does not cascade to other operations. -
tokiois the async runtime — all I/O is async. Git protocol sessions are long-lived streaming conversations; do not introduce blocking I/O on the async path. Usetokio::syncprimitives (oneshot,mpsc) for correlation;parking_lotfor short-held internal locks. -
No secret material on the wire or in plaintext at rest — the git protocol, the call protocol payloads, and the metadata store carry no private keys, API keys, tokens, or decrypted credentials. Stored credential material lives in alkvault; metadata holds vault references. Outbound credentials flow through
Capabilitiesinjected at the assembly layer →OperationContext.capabilities→ handler. See the no-env-vars invariant below and the alkcall ADRs (ADR-010, ADR-017). -
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→ handler. This is a spec-level invariant, not a runtime convention. -
Visible-surface = authorized-surface — the reason this project exists (a self-hosted gitea got pwned via an internal API reachable over the internet). Every operation exposed on the wire is gated by alkcall's
AccessControl::check(peer_identity); ops withVisibility::Internalare never wire-callable; no endpoint exists that a caller cannot see themselves authorized for. There is no unauthenticated endpoint unless a repo is explicitly public, and push is always authenticated. This is a spec-level invariant. -
gitoxide for git primitives, no shelling out — storage and pkt-line come from the gix crates (pinned in the crate manifest). The serving path never spawns the
gitbinary (no GPL dependency, no process-injection surface). The server half of the smart protocol is ours; seedocs/research/git-protocol.mdfor the inventory. -
License hygiene — this project is MIT OR Apache-2.0. Never copy or derive code from MPL-2.0-licensed reference projects; reading gitoxide (MIT OR Apache-2.0) is fine. The policy and the rationale live in
docs/research/reference-policy.md— do not name the incompatible-licensed project in docs, README, or commit messages. -
Wire formats are stable one-way doors — once a wire surface is published (the git smart protocol is defined upstream and not ours to change; the one existing piece of alkgit-specific framing — the native session preamble, ADR-016 — is pinned and in the OQ-03 freeze inventory), its shape must not change. Protocol capability advertisement is honest: never advertise what we don't serve.
-
Feature flags — optional surface is feature-gated:
gix(default-on; the backend implementations) andsha256(hash algorithm passthrough;sha1is the default and pinned in the crate manifest). The wire layer compiles without gix (default-features = false). Verify bothcargo test(default) andcargo test --all-featurespass if features are added. -
Bounded resources — every protocol session carries wall-clock, size, and round limits (max negotiation rounds, max receive-pack size, session timeout). Git servers are internet-facing; unbounded loops and unbounded buffers are bugs.
-
Repo identity is registry-resolved — repo names arriving on the wire are IDs resolved against a server-side registry to configured storage roots. Wire-supplied names are never used as filesystem paths directly (path traversal is a protocol-level input, not a config value).
-
Naming — Rust standard:
snake_casefor functions/variables/ modules,PascalCasefor types/traits,SCREAMING_SNAKE_CASEfor constants. -
Module structure — one module per file under
src/, re-exported fromsrc/lib.rs(the alktty crate-root pattern). Public API surface islib.rsre-exports. Single crate at the repo root (ADR-010); the gix backend implementation lives under the default-ongixfeature;default-features = falsegives the wire/protocol layer only. -
Composability boundary ("ALPN as a service") — alkgit is front-door-blind: it consumes (identity, repo id, duplex stream, limits) and depends on alkcall types only, never on alkhttp/alkssh, and carries no http/channel-specific types below the stream. Doors (alkhttp
gitfeature, alkssh) are family infrastructure; a downstream app embeds alkgit and brings its own doors. v1 is the reduction to wire layer + backend traits + one gix implementation.
Verification Commands
Run these before committing. All must pass.
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 (per crate)
If feature flags are added, also run cargo test --all-features and
cargo clippy --all-features --all-targets -- -D warnings.
Lifecycle Status
The project is in SDD phase 1 (architecture committed, implementation
not yet begun). Phase 0 research is in docs/research/; the committed
architecture is docs/architecture/ (ADR-010 is the structural
decision: pure protocol crate, alk/git ALPN, backend traits, no
binary/doors). POC code never merges to main — two modes: branch mode
(POC builds on repo code; git branch, findings merged into research
docs, branch dropped) or standalone mode (POC independent of repo code;
scratch project at /workspace/<poc-name>). POCs have relaxed
constraints (comments/unwrap acceptable) — they are exploration tools,
not production code. Open architecture questions live in
docs/architecture/open-questions.md (the active set: OQ-03 —
partially resolved, the publish freeze inventory — and OQ-05 —
sha256 policy, deferred on ecosystem need; OQ-16 — grant-key identity
namespace, deferred on the distributed phase. OQ-04 receive-pack, OQ-06
registry backing, and OQ-08 identity model are resolved: ADR-013,
ADR-012, ADR-011). Both gate reviews (001 pre-decomposition, 002
post-remediation) are complete and all their findings are resolved
(resolutions: ADR-015/016/017/018); the specs are in reviewed status
and decomposition into implementation tasks may begin.
Architecture Context
docs/research/— phase 0 research (current source of truth for design direction):vision.md— vision, guiding principles, non-goalsgitoxide.md— gix capability/version alignment (sha1 feature pinning is mandatory;default-features = falsewithout a hash feature does not compile)alk-stack.md— alkcall/alkhttp/alktls/alkvault integration surfacegit-protocol.md— server-side smart-protocol inventory (what we own: advertisement, ls-refs, fetch negotiation, receive-pack)reference-policy.md— license/reuse policy (MPL-2.0 prior art: facts only, never code)pocs.md— POC-1..3 plan gating phase 0 completion
- Sibling crates (published, MIT OR Apache-2.0):
alkcall 0.8(call + channels RPC with ACL;BiStreamis the git-session substrate),alkhttp 0.5(HTTP serving + call adapters),alktls 0.1(rustls + ACME),alkvault 0.1(secret encryption). Their architecture docs live in their own repos; alkcall ADRs referenced above are at/workspace/@alkdev/alkcall/docs/architecture/decisions/. - gitoxide reference clone:
/workspace/gitoxide(matches published 0.87.1 plus a few unreleased commits — pin crates.io versions; treat the clone as a reading aid only, never a path dependency).