Files
alkforge/AGENTS.md
T
glm-5.3-flash 95399b46a2 chore(process): adopt family collaboration setup mirrored from alkssh
- replace .opencode/agents/*.md with the cleaned alkssh versions,
  adapted to alkforge constraints (coordinator prompt, implementation
  conventions, poc dir name, fail-closed feature wording)
- adopt the alkssh sdd_process.md (project name alkforge)
- write AGENTS.md for the assembly: family stack conventions,
  no-deformation rule, alkvault/no-env posture, fail-closed door
  features, alkgen SSR rules, license hygiene, and the em-dash rule
  (convention 16)
- placeholder README pending the 0.1.0 push

Verified: no em dashes or spaced-hyphen separators in AGENTS.md,
README.md, docs/, .opencode/agents/
2026-11-07 06:39:44 +00:00

14 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:

  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 in conventional commits style: <type>(<scope>): <summary>; types are feat, 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.
  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 wire-relevant or semver-relevant public API (the user-facing surface alkforge presents becomes a de facto contract once a deployment consumes it; ledger-gated family contracts are recorded in /workspace/@alkdev/alkfamily/docs/ledger.md; see the ADRs in docs/architecture/decisions/ once they exist)
  • 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 / assembly crate)

This is alkforge, the family's near-term assembly (family ADR-002): a minimal forge replacing the gitea deployment, composed from family crates rather than re-implementing them. The stack it embeds: alkgit (+ alkgit-substrate: alkstore + alkblobs) as the git core, alkssh (git feature) as the SSH door, alkhttp as the HTTP gate, alkgen as the SSR engine, alkstore/alkblobs as the persistence and reactive layer, alkvault for secrets, alkcall as the substrate underneath, with reverse-proxy as the standing front door. Assemblies bind layers; they never deform them. 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 forge serves long-lived git conversations and web traffic; do not introduce blocking I/O on the async path. Use tokio::sync primitives (oneshot, mpsc) for correlation; parking_lot for short-held internal locks.

  4. The no-deformation rule (the binding constraint): alkforge is an assembly on alkgit's core; it may not pull, patch, or fork the core to fit assembly needs (family ADR-002 decision 3). Gaps and asks are filed upstream first (alkcall is ours; alkgit/alkgen/alkssh likewise; see the ledger's pin policy). A shortcut inside alkforge that would otherwise deform a family crate is a family-level decision, by ADR. Third-party-upstream posture: file asks upstream; never silently absorb a workaround.

  5. Secrets via alkvault, never env vars: no secret material on the wire, in plaintext at rest, or in environment variables (family ADR-002 decision 5). Configuration is not an env-var sprawl; stored credentials live in alkvault, config stores hold references, and outbound credentials are injected at the assembly layer (OperationContext.capabilities).

  6. Visible-surface = authorized-surface: every endpoint and operation exposed to the outside is checked against alkcall's AccessControl::check(peer_identity); ops with Visibility::Internal are never wire-callable. "OSS projects are public read" is an explicit, explicitly granted capability per repo and per door, never a global anonymous default. This is a spec-level invariant.

  7. Fail-closed capability features (the alkssh feature-graph shape at the doors): every optional capability of the doors alkforge mounts is a cargo feature and unfeatured requests decline fail-closed; a build serving only public clones is structurally incapable of serving shells, tunnels, or sftp (family ADR-003 decision 3, which alkforge's ssh endpoint inherits by construction). Nothing is advertised that isn't served; every decline path is contract territory and gets tested.

  8. Git transport belongs to the doors, not to alkforge: vanilla git over SSH arrives via alkssh's git feature; git over HTTP arrives via alkhttp's git feature; the payload is served by alkgit's protocol halves. alkforge never speaks pkt-line or packetizes git itself; no shelling out to the git binary in any serving path.

  9. SSR via alkgen, one engine: web pages render through alkgen (handlebars-rust templates over serde contexts; alkgen ADR-006). Templates are code: versioned in-repo, strict mode (a missing variable is a render error, not silently-blank output), golden-output render tests, deterministic rendering (same templates + same context → byte-identical HTML). No client-side framework in the minimal forge; no rendering machinery outside alkgen.

  10. Persistence via alkstore, objects via alkblobs: relational/user state (accounts, repos, permissions, mirror config) lives in alkstore (sqlite or postgres backend, the contract suite is the backend-acceptance gate); large binary blobs ride alkblobs (git LFS media keyed per alkgit ADR-022). No bespoke database code and no hand-rolled reactive plumbing alongside the alkstore pattern.

  11. Bounded resources: every externally reachable door carries wall-clock, size, and concurrency limits (alkssh's per-session limits, alkhttp's bounded body/streaming posture). The forge is internet-facing; unbounded loops and unbounded buffers are bugs.

  12. Wire formats are stable one-way doors: alkforge introduces no new wire protocol of its own in v1; the doors' ALPNs (alk/git, alk/ssh) and the call/channels wire are already ledger-gated (ledger rule 1, alkcall ADRs 013/034). If a future surface needs a pin, it gets one via ADR before the first consumer exists.

  13. License hygiene: this project is MIT OR Apache-2.0. Check before adopting any new dependency, and never copy or derive code from incompatible-licensed reference projects. Reference code is for reading and facts, never copying. gitea's code is deliberately a black box: read its behavior from the outside, never its source (it is MPL-2.0 and this rule also keeps the family's license posture clean).

  14. Naming: Rust standard: snake_case for functions/variables/ modules, PascalCase for types/traits, SCREAMING_SNAKE_CASE for constants.

  15. Module structure: one module per file under src/, re-exported from the crate root (the alktty crate-root pattern). Public API surface is lib.rs re-exports; the assembly's composition (doors, repos, identity, config) is its spine.

  16. No em dashes in prose. Do not use the em dash (U+2014) in source doc comments, markdown docs (docs/, README.md, CHANGELOG.md, AGENTS.md), agent prompts under .opencode/agents/, or commit messages. Restructure instead: split the sentence, use parentheses, commas, a colon, or a semicolon, whichever reads naturally, and vary the choice rather than converting every instance the same way. A spaced hyphen (-) is not an acceptable substitute; it reads as a different but equally recognizable writing tell. The same rule covers en dashes used as clause separators, though en dashes inside numeric ranges keep their normal meaning. Hyphens in compound words (fail-closed, client-side) are untouched. In test fixtures and scripts the rule applies to prose-y content only: comment lines are rewritten too, while fixture data, wire examples, and tool transcripts survive verbatim.

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
grep -rn '—' src/ docs/ README.md CHANGELOG.md AGENTS.md \
    .opencode/agents/ tests/ scripts/               # must be zero hits;
                                                    # see convention 16

If feature flags are added, also run cargo test --all-features and cargo clippy --all-features --all-targets -- -D warnings. Once the door feature graph lands, each disabled capability's decline path gets a test; the fail-closed invariant is contract territory on both sides of every feature.

Task files (taskgraph)

Any file under tasks/ is a taskgraph task. Frontmatter is required (id, name) and its enum fields are closed sets (taskgraph 0.1.3): status: pending|in-progress|completed|failed|blocked (never done or finished), scope: single|narrow|moderate|broad|system, risk: trivial|low|medium|high|critical, impact: isolated|component|phase|project, level: planning|decomposition|implementation|review|research. A file without frontmatter is silently dropped from the graph (validate does not flag it); prose-only trackers belong under docs/. Run taskgraph validate after writing or editing anything under tasks/; full field semantics live in docs/sdd_process.md §"Task File Format" / §"Categorical Estimates".

Lifecycle Status

The project is in SDD phase 0 (exploration; docs only, no architecture committed, no implementation). Family ADR-002 (2026-10-20, Accepted) records the structural decision already made: alkforge is the gitea replacement, an assembly on alkgit's core under the no-deformation rule, with the family's security posture (alkvault secrets, no env vars, minimal wire surface) as a day-one design requirement. OQ-06 (this repo's phase 0 shape) and OQ-07 (the alkgit asks list) are the family open questions this repo's phase 0 feeds; see /workspace/@alkdev/alkfamily/docs/architecture/open-questions.md. Phase 0 output lands in docs/research/ (the phase 0 document is the current work item); docs/architecture/ + ADR-001 come with phase 1. POC code never merges to main; two modes exist: 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) since they are exploration tools, not production code.

Architecture Context

  • docs/sdd_process.md: the SDD process (phases, roles, task format)
  • docs/research/: phase 0 research (current source of truth for design direction); phase-0.md is the current work item
  • family docs: /workspace/@alkdev/alkfamily/docs/; architecture/decisions/002-alkforge-gitea-replacement.md is the structural decision this repo builds from (assembly on alkgit's core, no-deformation, security posture); layering.md and ledger.md record where alkforge sits and what is wire-stable; architecture/open-questions.md OQ-06/OQ-07 hold the phase 0 scope
  • Sibling crates (published, MIT OR Apache-2.0, each with per-repo AGENTS.md + docs/architecture/decisions/): alkcall 0.10.1 (call + channels RPC with ACL; the substrate and the identity/ACL model), alkgit 0.1.0 + alkgit-substrate 0.1.0 (git protocol over alk/git; objects in alkblobs, refs in alkstore; docs/architecture/doors.md records the alkhttp/alkssh door shapes), alkssh 0.2.0 (russh-wrapped SSH adapter, alk/ssh ALPN; the git channel-type mapping is its git cargo feature), alkhttp 0.8.1 (HTTP adapter shape; the HTTP gate), alkgen 0.1.0 (SSR engine; docs/research/phase-0.md there records the alkforge SSR branch and OQ-GN-09), alkstore 0.1.0 / alkblobs 0.2.2 (persistence + blobs), alkvault 0.2.0 (secrets; decrypt returns Zeroizing<String>), alktls 0.1.0 (TLS support), reverse-proxy (produced, outside the alk* namespace; the standing front door).
  • The gitea deployment alkforge replaces: read its actual usage (not its source, see convention 13); the load-bearing surface enumeration is phase 0 work item #1 (see docs/research/phase-0.md).