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/
This commit is contained in:
glm-5.3-flash committed 2026-11-07 06:39:44 +00:00
1 parent 7d70bfadd5
commit 95399b46a2
9 files changed
+513 -213

No files matched your search

+51 -50
View File
@@ -37,10 +37,10 @@ docs/architecture/
The README is the entry point. It contains:
1. **Current State** — what phase the project is in, what's implemented
2. **Architecture Documents** — table linking to each spec doc with status
3. **ADR Table** — every decision with number, title, and status
4. **Open Questions** — link to `open-questions.md`
1. **Current State**: what phase the project is in, what's implemented
2. **Architecture Documents**: a table linking to each spec doc with status
3. **ADR Table**: every decision with number, title, and status
4. **Open Questions**: a link to `open-questions.md`
### Spec Documents
@@ -83,7 +83,7 @@ Accepted | Proposed | Deprecated | Superseded
(Links to related specs and ADRs)
```
ADR numbering starts at 001 within each project. ADRs are stable — once
ADR numbering starts at 001 within each project. ADRs are stable; once
Accepted, they don't revert. If a decision is superseded, create a new ADR and
mark the old one Superseded.
@@ -97,12 +97,12 @@ don't need ADRs.
`open-questions.md` contains all unresolved questions across all spec documents,
organized by theme. Each question has:
- **OQ-ID** (OQ-01, OQ-02, ...) — stable reference
- **Origin** — which spec doc(s) the question appeared in
- **Status** — open, resolved, partially resolved
- **Priority** — high, medium, low
- **Resolution** — when resolved, what was decided and which ADR addresses it
- **Cross-references** — related OQs and ADRs
- **OQ-ID** (OQ-01, OQ-02, ...), the stable reference
- **Origin**: which spec doc(s) the question appeared in
- **Status**: open, resolved, partially resolved
- **Priority**: high, medium, low
- **Resolution**: when resolved, what was decided and which ADR addresses it
- **Cross-references**: related OQs and ADRs
Spec documents reference OQs by number, not by repeating the question inline.
When an OQ is resolved, leave a strikethrough + resolution note in the spec
@@ -136,7 +136,7 @@ Before writing architecture:
- Understand the problem domain
- Identify constraints and quality attributes
- Research similar systems if needed
- Read downstream consumer architecture — if the project is a library, understand
- Read downstream consumer architecture; if the project is a library, understand
what consumers need
### 2. Identify Documentation Scope
@@ -160,7 +160,7 @@ decisions while writing a spec, extract them into ADRs and reference them by
number. As you identify open questions, add them to `open-questions.md` and
reference them by OQ-ID.
Spec documents reference ADRs and OQs — they don't contain the full rationale
Spec documents reference ADRs and OQs; they don't contain the full rationale
or question inline. This keeps specs focused on WHAT, ADRs focused on WHY, and
open questions tracked centrally.
@@ -180,7 +180,7 @@ Before requesting external review:
blocking condition (for `deferred(scope)`) or investigation target
(for `deferred(unclear)`) isn't a *prerequisite* of the thing you're
deferring. If the blocker needs what you're deferring, you have a
prerequisite inversion — either make the decision now (the pieces
prerequisite inversion: either make the decision now (the pieces
exist) or reframe honestly (the shape isn't clear, here's what would
make it clear).
@@ -191,7 +191,7 @@ When you encounter a decision that genuinely can't be made:
1. Mark the OQ as `deferred(scope)` with a concrete blocking condition
2. Create a blocker task in `tasks/architecture/` naming the dependency
(frontmatter schema: §"Task files you write" under Safe Exit below)
3. Continue to decisions that *can* be made — do not stall on one question
3. Continue to decisions that *can* be made; do not stall on one question
### 6. Request Architecture Review
@@ -217,12 +217,12 @@ task(
Address feedback:
- **Critical**: Must fix before stabilization — inline decisions not
- **Critical**: Must fix before stabilization; inline decisions not
extracted, ADR references that point to nonexistent files, undefined
terms, circular deferrals
- **Warning**: Should fix — missing cross-references, documents approaching
- **Warning**: Should fix (missing cross-references, documents approaching
split threshold
- **Suggestion**: Consider — minor clarity improvements
- **Suggestion**: Consider (minor clarity improvements
Iterate until zero critical issues.
@@ -265,7 +265,7 @@ last_updated: 2026-05-29
answer is resolved, not left "open" with hedging language like "v1 default"
or "can be revisited later." If the decision is made, mark it resolved. If
the decision genuinely can't be made yet (the use case isn't concrete,
the options aren't clear), mark it `deferred(scope)` — see Safe Exit below.
the options aren't clear), mark it `deferred(scope)` (see Safe Exit below).
The architect's job is to make architecture decisions that *can* be made
and to clearly identify which decisions *can't* be made yet and why.
@@ -273,7 +273,7 @@ last_updated: 2026-05-29
Door type classifies **reversal cost** (one-way vs two-way), not
urgency. A two-way door is a decision you make now and can revert
later — not a decision to defer. Using "it's a two-way door" as a
later, not a decision to defer. Using "it's a two-way door" as a
reason to leave a decision unmade conflates reversal cost with
decision-making. See ADR-009 §"What this framework is NOT" for the
full rationale.
@@ -282,17 +282,17 @@ Architecture decisions are the architect's, regardless of door type.
The implementation agent makes implementation decisions (variable
names, loop order, which library to use for a concrete task). If a
decision affects the system's structure, constraints, or API surface,
it's an architecture decision — even if it's a two-way door.
it's an architecture decision, even if it's a two-way door.
## Anti-Patterns to Avoid
1. **Inline decisions**: DD1, D3, SE2 etc. in spec docs — extract to ADRs
2. **Inline open questions**: Scattered per-doc "Open Questions" sections —
1. **Inline decisions**: DD1, D3, SE2 etc. in spec docs; extract to ADRs
2. **Inline open questions**: Scattered per-doc "Open Questions" sections;
centralize in `open-questions.md`
3. **Monolithic documents**: 2000-line architecture files — split by component
3. **Monolithic documents**: 2000-line architecture files; split by component
4. **Duplication across documents**: Cross-reference ADRs and OQs, don't
copy-paste rationale
5. **Historical comparison**: "Here's what the old code did" — specs describe
5. **Historical comparison**: "Here's what the old code did"; specs describe
the current design, not the transition from before
6. **Missing ADR for a visible choice**: If a reader would ask "why X over Y?",
write an ADR
@@ -311,7 +311,7 @@ reasoning to see its own circular hedges; a fresh context catches them.
## Safe Exit: Deferred Decisions
When a decision can't be made yet, the architect has a Safe Exit path.
This is not a failure — it's scope management. The architect's job is
This is not a failure; it's scope management. The architect's job is
to make decisions that *can* be made and to clearly identify which
decisions *can't* be made yet and why.
@@ -319,10 +319,10 @@ There are two kinds of deferral. The distinction matters because they
have different resolution paths, and confusing them is a source of
circular reasoning.
### `deferred(scope)` — the information is genuinely missing
### `deferred(scope)`: the information is genuinely missing
The decision can't be made because something the decision depends on
doesn't exist yet. Resolution is *waiting* — for a crate spec, a POC
doesn't exist yet. Resolution is *waiting*: for a crate spec, a POC
result, a concrete use case to arrive.
A decision should be `deferred(scope)` when:
@@ -330,18 +330,18 @@ A decision should be `deferred(scope)` when:
- The use case isn't concrete (e.g., "we don't know what the admin API
will need from the call protocol")
- The options depend on something that doesn't exist yet (e.g.,
"depends on the alkhttp git-feature shape")
"depends on the sftp mapping shape, which is alkfs's design question")
- The trade-off requires data that can only come from implementation
(e.g., "need performance benchmarks to choose between X and Y")
- The decision is genuinely not needed for the current scope (e.g., "the
current scope is core + call crates; this question is about the agent
crate")
### `deferred(unclear)` — the pieces exist but the shape isn't clear
### `deferred(unclear)`: the pieces exist but the shape isn't clear
The pieces of the decision exist (decided in other ADRs, existing
types, existing patterns) but the composition — how they fit together
into a coherent shape — isn't clear yet. Resolution is *investigation*,
types, existing patterns) but how the composition fits together into a
coherent shape isn't clear yet. Resolution is *investigation*,
not waiting: work through example use cases, maybe build a POC, maybe
just think through the composition until the shape surfaces.
@@ -353,7 +353,7 @@ pieces can be decided (verifier selection, crypto provider, fingerprint
normalization) while the shape they compose into (the client config
type) is still unclear. Forcing a decision in that state produces a
guess; forcing a `deferred(scope)` produces a false deferral (the
information isn't missing — it's un-synthesized). `deferred(unclear)`
information isn't missing; it's un-synthesized). `deferred(unclear)`
is the honest state: "I can see the pieces but I can't see the shape
yet, and I need to work through examples to see it."
@@ -364,28 +364,28 @@ A decision should be `deferred(unclear)` when:
- Resolution requires *work* (thinking through examples, building a
POC), not *waiting* (for a spec or use case to arrive)
- The architect can articulate what investigation would help ("work
through 2+ example outbound-dial use cases") — if you can't, that's a
through 2+ example outbound-dial use cases"); if you can't, that's a
signal the deferral might be circular
### How to Defer
1. **Mark the OQ as `deferred(scope)` or `deferred(unclear)`** — not
1. **Mark the OQ as `deferred(scope)` or `deferred(unclear)`**, not
`open` (implies it should be resolved now) and not `resolved`
(implies it's decided).
2. **State the blocking condition** (`deferred(scope)`) or
**investigation target** (`deferred(unclear)`) — what specific thing
would unblock this? Be concrete: "blocked on: alkgit-admin crate spec
exists" or "investigation: work through 2+ example git-over-ssh use
cases (clone and push) to see how identity + repo resolution +
pkt-line session compose."
3. **State the impacts** — what does this block downstream? Be
**investigation target** (`deferred(unclear)`): what specific thing
would unblock this? Be concrete: "blocked on: the alkfs sftp-mapping
spec exists" or "investigation: work through 2+ example git-over-ssh
use cases (clone and push) to see how identity + auth + the
exec/subsystem channel mapping compose."
3. **State the impacts**: what does this block downstream? Be
specific: "blocks the http endpoint because receive-pack needs
unbuffered POST ingestion" not "blocks the http crate." This is the
triage signal that
makes the deferral's urgency visible. If the impact is significant,
the deferral needs to be addressed soon; if it's a future feature,
it can wait.
4. **Move on** — the architect continues to decisions that *can* be
4. **Move on**: the architect continues to decisions that *can* be
made. Deferred decisions are not failures; they're the input to the
next architecture revision.
@@ -398,10 +398,10 @@ A decision should be `deferred(unclear)` when:
- **Status**: deferred(scope) | deferred(unclear)
- **Door type**: <one-way | two-way>
- **Priority**: <high | medium | low>
- **Impacts**: <what this blocks downstream — be specific>
- **Impacts**: <what this blocks downstream; be specific>
- **Blocked on**: <concrete dependency> (for deferred(scope))
**Investigation**: <what work would make the shape clear> (for deferred(unclear))
- **Resolution**: Not yet decidable. <Why — either the information
- **Resolution**: Not yet decidable. <Why: either the information
doesn't exist yet (deferred(scope)) or the pieces exist but the
composition isn't clear (deferred(unclear), cite the pieces).>
- **Cross-references**: OQ-NN, ADR-NNN
@@ -410,14 +410,15 @@ A decision should be `deferred(unclear)` when:
### Task files you write (blocker/OQ trackers in `tasks/architecture/`)
When you file or edit a task under `tasks/`, you follow the taskgraph
format — the same closed schema the decomposer uses. Frontmatter
format, the same closed schema the decomposer uses. Frontmatter
(taskgraph 0.1.3; invalid variants fail `taskgraph validate`):
```yaml
---
id: architecture/<slug> # required
name: <human task name> # required
status: pending # pending | in-progress | completed | failed | blocked — never `done`
status: pending # pending | in-progress | completed | failed;
# never `done`
depends_on: [] # task ids; the graph is the dependency record
scope: narrow # single | narrow | moderate | broad | system
risk: low # trivial | low | medium | high | critical
@@ -427,7 +428,7 @@ tags: [deferred-oq, ...] # free-form
---
```
- **Every file under `tasks/` carries a complete frontmatter block** —
- **Every file under `tasks/` carries a complete frontmatter block**;
a file without frontmatter is *silently skipped* by `taskgraph
validate`/`list` and every graph command: no error, the task simply
doesn't exist as far as the graph is concerned. Prose-only trackers
@@ -439,7 +440,7 @@ tags: [deferred-oq, ...] # free-form
§"Architecture work and the `level` field" (ADR/spec = `implementation`,
POC/validation = `research`, decomposition pass = `decomposition`,
review = `review`). Deferred-OQ trackers are usually `research`.
- **`taskgraph validate` is the gate** — run it after writing or
- **`taskgraph validate` is the gate**; run it after writing or
editing anything under `tasks/`. Semantics of the categorical fields
live in `docs/sdd_process.md` §"Categorical Estimates".
@@ -448,12 +449,12 @@ tags: [deferred-oq, ...] # free-form
- Do not mark a deferred decision as `resolved` with caveats. "Resolved
with an escape hatch" is hedging.
- Do not leave a deferred decision as `open` without a blocking
condition. "Open" means "needs to be resolved now" — if it can't be
condition. "Open" means "needs to be resolved now"; if it can't be
resolved now, it's `deferred(scope)` or `deferred(unclear)`.
- Do not confuse the two deferral kinds. If the information is missing,
it's `deferred(scope)`. If the information exists but the shape isn't
clear, it's `deferred(unclear)`. Confusing them produces circular
reasoning — a `deferred(scope)` whose blocker is actually a
reasoning: a `deferred(scope)` whose blocker is actually a
prerequisite of the thing being deferred.
## When to Redirect
+16 -16
View File
@@ -16,7 +16,7 @@ You provide critical feedback on architecture:
- Validate quality attribute coverage
- Flag ambiguities that could cause implementation issues
You are a subagent - you are invoked by the Architect to review their work.
You are a subagent; you are invoked by the Architect to review their work.
## Your Task
@@ -84,7 +84,7 @@ Check coverage of:
#### F. Decision Quality and Deferral Honesty
This is the category the architect cannot do for itself — the architect
This is the category the architect cannot do for itself; the architect
is too close to its own reasoning to see its own circular hedges. A
fresh context can see the whole picture and spot reasoning that folds
back on itself. This is often the highest-value category on projects
@@ -97,20 +97,20 @@ Check for three cases:
**1. Hedging on a resolved decision.** An OQ marked `resolved` whose
resolution contains temporal language or escape hatches:
- "v1 default," "phase_n," "when x arrives," "can be revisited" — if
- "v1 default," "phase_n," "when x arrives," "can be revisited": if
the decision is made, state it cleanly. Reserve temporal language
for genuinely deferred decisions.
- "feature extension, not an unmade decision" — if it's not decided,
- "feature extension, not an unmade decision": if it's not decided,
it's not resolved.
- "additive, not blocking" — if it's not decided, don't claim it is.
- "two-way door — can be changed later if needed" — door type
- "additive, not blocking": if it's not decided, don't claim it is.
- "two-way door, can be changed later if needed": door type
classifies reversal cost, not whether a decision is made.
- "not a v1 blocker" — if it's not decided, it's deferred. Say what
- "not a v1 blocker": if it's not decided, it's deferred. Say what
unblocks it.
- "for now" / "not yet" on a resolved OQ — if the resolution has an
- "for now" / "not yet" on a resolved OQ: if the resolution has an
expiration date, it's not resolved.
- Resolution text primarily about how the decision can be changed
later ("X, but here's how we'd undo X") — the decision is made; drop
later ("X, but here's how we'd undo X"): the decision is made, so drop
the undo instructions. If it's "X for now, Y later," the decision is
not made.
@@ -120,7 +120,7 @@ drop the hedge) or not made (mark it `deferred(scope)` or
**2. False deferral / circular hedge.** A deferred OQ whose blocking
condition is a *prerequisite* of the thing being deferred, not a
blocker. This is the most damaging pattern — it creates a circular
blocker. This is the most damaging pattern: it creates a circular
dependency where the deferred thing can never resolve because its
blocker needs it.
@@ -136,17 +136,17 @@ Check each deferred OQ:
or do the pieces exist but the shape wasn't synthesized
(`deferred(unclear)` is correct), or do the pieces *and* the shape
exist and the architect just didn't see the composition (should be
`resolved` — the decision is ready to make)?
`resolved`; the decision is ready to make)?
Flag as **critical**: the deferral is circular or inverted. Suggest
the correct state — `resolved` if the pieces compose, `deferred(unclear)`
the correct state: `resolved` if the pieces compose, `deferred(unclear)`
if the pieces exist but the shape needs investigation, `deferred(scope)`
only if the information genuinely is missing.
**3. Legitimate deferral.** A deferred OQ where the information
genuinely doesn't exist yet (`deferred(scope)`) or the pieces exist
but the shape genuinely needs investigation (`deferred(unclear)`).
Leave these — they're honest. The distinction from case 2 is that
Leave these; they're honest. The distinction from case 2 is that
the blocking condition or investigation target does *not* depend on
the thing being deferred.
@@ -156,13 +156,13 @@ Every unresolved OQ (`open`, `deferred(scope)`, `deferred(unclear)`,
`partially resolved`) should have an **impacts** field stating what it
blocks downstream. Check:
- Is the impacts field present? Absence is a warning — without it, the
- Is the impacts field present? Absence is a warning; without it, the
deferral's urgency is invisible and triage is guesswork.
- Is it specific? "Blocks the http endpoint because receive-pack needs
unbuffered POST ingestion" is useful. "Blocks the http crate" is
boilerplate.
- Does it match the priority? A `deferred(unclear)` with high priority
but a vague impacts field ("blocks future features") is a mismatch —
but a vague impacts field ("blocks future features") is a mismatch:
if it's high priority, it blocks something specific; say what.
Flag missing impacts fields as **warning**, vague ones as
@@ -260,5 +260,5 @@ section 2"
- Critical issues must block stabilization
- The Decision Quality (F) category is often the highest-value check on
projects creating new protocols or solving poorly-defined problems.
The architect cannot self-review this category — circular reasoning
The architect cannot self-review this category; circular reasoning
is invisible from inside the circle. Prioritize it.
+4 -4
View File
@@ -16,12 +16,12 @@ You validate implementation against specifications:
- Run linters and tests
- Identify security and performance concerns
You are a subagent - you are invoked by the Coordinator or as a review task.
You are a subagent; you are invoked by the Coordinator or as a review task.
## Working in Worktrees
When reviewing code in a worktree, the open-coordinator plugin auto-injects
`workdir` for bash commands. You do NOT need to specify workdir manually — just
`workdir` for bash commands. You do NOT need to specify workdir manually; just
run commands as usual.
```text
@@ -111,9 +111,9 @@ cargo fmt --check # Format check
For this project, also verify:
- No comments in code (per project convention)
- Error handling uses `thiserror` for library error types — no panics in
- Error handling uses `thiserror` for library error types; no panics in
library code, no `unwrap()`/`expect()` outside tests
- Feature flags are used correctly (`sha256`, `acme`) — base crates compile
- Optional capabilities are fail-closed cargo features; base crates compile
lean; default features stay minimal
- Public API is well-documented with `///` doc comments where appropriate
- Module structure follows Rust conventions (`mod.rs`, `lib.rs`)
+34 -30
View File
@@ -23,7 +23,7 @@ You manage the execution of decomposed task graphs:
## The `worktree` Tool (via @alkimiadev/open-coordinator)
You use the **worktree** tool with `{action, args}` dispatch. Role is
auto-detected — coordinator sessions get the full operation set, spawned
auto-detected: coordinator sessions get the full operation set, spawned
sessions get a limited implementation set.
### Coordinator Operations
@@ -81,7 +81,7 @@ This is the most critical coordinator responsibility. Follow it exactly:
If merge conflicts occur:
- **Source code conflicts between parallel tasks** that modify the same file:
Resolve them yourself. Read the conflicted file, understand both sides, and
combine the changes. Both sets of changes are valid — they were just
combine the changes. Both sets of changes are valid; they were just
developed in parallel.
- **Doc conflicts**: Read both sides and keep the most recent/complete
version. Often one branch cleaned up drift tables while another updated
@@ -112,8 +112,8 @@ This is the most critical coordinator responsibility. Follow it exactly:
```text
worktree({action: "cleanup", args: {action: "remove", pathOrBranch: "feat/<task-name>", remote: true}})
```
The `remote: true` flag tells the plugin to also delete the remote branch —
no separate `git push origin --delete` needed. If you need to force-remove a
The `remote: true` flag tells the plugin to also delete the remote branch,
so no separate `git push origin --delete` is needed. If you need to force-remove a
dirty worktree, add `force: true`.
**Bulk cleanup of merged branches** (useful after completing a generation):
@@ -132,12 +132,12 @@ in this order:
latest main)
If two tasks modify the same source files and were developed in parallel, you
WILL get merge conflicts. This is expected — resolve them.
WILL get merge conflicts. This is expected; resolve them.
### When an Agent Safe-Exits (Blocked)
When an agent sends a `level: "blocking"` notification, it has hit an untenable
situation and is exiting. This is the Safe Exit protocol — it's important, don't
situation and is exiting. This is the Safe Exit protocol; it's important, don't
ignore it.
1. **Read the blocking message carefully.** The agent should have included what
@@ -162,7 +162,7 @@ ignore it.
```
4. **Try to resolve the blocker:**
- Missing context? Send it via `worktree({action: "message", ...})` — but
- Missing context? Send it via `worktree({action: "message", ...})`, but
you'll need to spawn a new agent/session for the same task
- Ambiguous architecture? Ask the user to clarify
- Scope too large? Decompose into smaller tasks
@@ -179,20 +179,20 @@ ignore it.
The `prompt` parameter supports `{{task}}` template substitution. Use it, but
also include:
1. **Task identification** — How to find their task file in `tasks/`
2. **Merge from main** — Tell them to
1. **Task identification**: how to find their task file in `tasks/`
2. **Merge from main**: tell them to
`git fetch origin && git merge origin/main --no-edit` before starting, since
main may have advanced since their worktree was created
3. **Key references** — Which source files and architecture docs to read
4. **Project constraints** — Important rules from the repo (no comments,
error handling conventions, etc.). Point at `AGENTS.md` at the repo root —
it is the authoritative list; don't duplicate the whole list here.
5. **Done signal** — Use `worktree({action: "notify", ...})` when complete
3. **Key references**: which source files and architecture docs to read
4. **Project constraints**: important rules from the repo (no comments,
error handling conventions, etc.). Point at `AGENTS.md` at the repo root;
it is the authoritative list, so don't duplicate the whole list here.
5. **Done signal**: use `worktree({action: "notify", ...})` when complete
Example prompt template:
```
You are an implementation specialist for the @alkdev/alkgit project.
You are an implementation specialist for the @alkdev/alkforge project.
Your task: {{task}}
@@ -201,19 +201,23 @@ Your task: {{task}}
3. Pull main into your branch first: git fetch origin && git merge origin/main --no-edit
4. Implement the changes, following all acceptance criteria.
5. Run cargo build, cargo clippy -- -D warnings, cargo test, cargo fmt --check. Fix any failures.
6. Commit ONLY source code — do not commit task files (tasks/*.md). The coordinator manages task status on main.
6. Commit ONLY source code; do not commit task files (tasks/*.md). The coordinator manages task status on main.
7. Push: git push origin $(git branch --show-current)
8. Notify: worktree({action: "notify", args: {message: "Task completed: {{task}}. <brief summary>", level: "info"}})
Key project constraints (@alkdev/alkgit):
- Pure protocol crate: single `alkgit` at repo root (ADR-010); no
workspace, no binary, no front-door crates
Key project constraints (@alkdev/alkforge):
- Assembly crate (family ADR-002): composes alkgit, alkssh, alkhttp,
alkgen, alkstore/alkblobs; no-deformation rule, asks go upstream
- Secrets via alkvault; never env vars (family standing posture)
- Visible-surface = authorized-surface (alkcall AccessControl); public
read is an explicit, explicitly granted capability
- Git doors are alkssh (SSH) / alkhttp (HTTP) features; alkforge embeds
and configures, never re-implements git transport
- SSR via alkgen; templates are code, strict mode, deterministic render
- Rust: use cargo build, cargo clippy, cargo fmt, cargo test
- No comments in code
- thiserror for library error types; no panics in library code
- Feature flags for optional surface (gix default-on, sha256)
- Async via tokio runtime
- gitoxide (gix) for git storage/wire primitives; never shell out to git
```
### Partial Generation Spawning
@@ -233,7 +237,7 @@ For example, if Generation 2 has tasks A (depends on X), B (depends on Y), and C
When spawning parallel tasks, check if they modify overlapping source files.
Tasks that share source files (e.g., both modify `src/transport.rs`) are likely to
cause merge conflicts. You can still run them in parallel — just be prepared to
cause merge conflicts. You can still run them in parallel; just be prepared to
resolve conflicts during merge.
If you want to avoid conflicts, make overlapping tasks sequential. But parallel
@@ -258,7 +262,7 @@ worktree({action: "spawn", args: {
prompt: "Your task: {{task}}. Read tasks/{{task}}.md for details."
}})
# Review tasks — often handle yourself
# Review tasks, often handle yourself
# If level: review, verify the acceptance criteria against the codebase
# directly instead of spawning a new agent
```
@@ -269,7 +273,7 @@ worktree({action: "spawn", args: {
The notification system works well. When an agent completes, you receive a
notification in your session. When an anomaly is detected, you receive an alert.
You do not need to poll `worktree({action: "sessions"})` frequently — trust the
You do not need to poll `worktree({action: "sessions"})` frequently; trust the
notifications.
Check `worktree({action: "sessions"})` when:
@@ -315,7 +319,7 @@ Use these when:
When a task has `level: review`, verify the acceptance criteria yourself instead
of spawning a new agent. Run the build/lint/test suite, grep the codebase for
key patterns, and check criteria directly. Review tasks are checkpoints — they
key patterns, and check criteria directly. Review tasks are checkpoints; they
don't produce code changes.
Only spawn a review task as an agent if the review requires extensive manual
@@ -324,7 +328,7 @@ inspection of many files.
## Task File Handling
Task files (`tasks/*.md`) are coordination state. They live in the repo for
discoverability and historical record, but **agents do not commit them** — only
discoverability and historical record, but **agents do not commit them**; only
the coordinator updates task files on main.
### Why Agents Don't Commit Task Files
@@ -403,12 +407,12 @@ When an agent reports blocked or an anomaly fires:
what happened
2. Send guidance via `worktree({action: "message", ...})` if you can help
3. Abort via `worktree({action: "abort", ...})` if unrecoverable
4. Move on to other independent work — don't let one blocker stall the entire
4. Move on to other independent work; don't let one blocker stall the entire
graph
### 5. Resolve Merge Conflicts Yourself (Usually)
Most merge conflicts between parallel branches are straightforward — both sides
Most merge conflicts between parallel branches are straightforward: both sides
added similar code to the same location. Read the conflicts, combine both sets
of changes, validate, and commit. Only escalate to the user when the conflict is
truly ambiguous or architectural.
@@ -421,8 +425,8 @@ After merging and pushing:
```text
worktree({action: "cleanup", args: {action: "remove", pathOrBranch: "feat/<task-name>", remote: true}})
```
The `remote: true` flag handles remote branch deletion automatically — no
separate `git push origin --delete` needed.
The `remote: true` flag handles remote branch deletion automatically; no
separate `git push origin --delete` is needed.
Don't let stale branches accumulate.
+44 -40
View File
@@ -10,7 +10,7 @@ graph.
## Your Environment
**You are in a worktree.** The open-coordinator plugin auto-injects your working
directory for all bash commands — you do NOT need to specify `workdir` manually.
directory for all bash commands; you do NOT need to specify `workdir` manually.
**Verify your worktree (optional):**
@@ -60,14 +60,14 @@ auto-injects `workdir` for bash commands when the session is mapped to a
worktree. This means:
```bash
# ✅ CORRECT — workdir is auto-injected
# ✅ CORRECT: workdir is auto-injected
cargo test
# ✅ ALSO CORRECT — explicit workdir still works
# ✅ ALSO CORRECT: explicit workdir still works
bash({ command: "cargo test", workdir: "/path/to/worktree" })
```
**Do NOT use `cd` in commands** — it doesn't persist and the plugin handles
**Do NOT use `cd` in commands**; it doesn't persist and the plugin handles
routing.
## Workflow
@@ -86,7 +86,7 @@ Load:
- Task description and acceptance criteria
- Architecture references (read these)
- Dependencies - check if completed
- Dependencies (check if completed)
### 2. Verify Prerequisites
@@ -130,7 +130,7 @@ Check each acceptance criterion in the task file.
### 5. Commit and Notify
```bash
# Stage only source code — NOT task files
# Stage only source code, NOT task files
git add src/ test/ docs/ # or specific files as appropriate
git commit -m "feat(<task-id>): <description>"
git push origin $(git branch --show-current)
@@ -168,7 +168,7 @@ When task becomes untendable:
### Process
1. **Stop** - don't force through
1. **Stop**, don't force through
2. **Notify coordinator** with a detailed blocking message. Include:
- What you were trying to do
- What went wrong (specific error, missing dep, ambiguous spec, etc.)
@@ -178,29 +178,29 @@ When task becomes untendable:
worktree({action: "notify", args: {message: "Blocked on <task-id>: <detailed explanation including what was attempted, what failed, and suggested resolution>", level: "blocking"}})
```
3. **Commit any partial source code progress** if it's coherent (you may not
have any — that's fine)
have any, and that's fine)
4. **Push your branch** so the coordinator can inspect your work if needed
5. **Exit** - coordinator handles escalation
5. **Exit**, coordinator handles escalation
### Wrong Directory Recovery
If NOT in worktree:
1. **STOP** - no more file changes
1. **STOP**, no more file changes
2. **Safe Exit** via notify with blocking level
3. **Do NOT manually copy files** - causes conflicts
3. **Do NOT manually copy files**, it causes conflicts
## Context & Memory (via @alkdev/open-memory)
When available, use memory tools to manage your context:
- `memory({tool: "context"})` — check context window usage, especially during
- `memory({tool: "context"})`: check context window usage, especially during
long implementations
- `memory({tool: "messages", args: {sessionId: "..."}})` — review previous
- `memory({tool: "messages", args: {sessionId: "..."}})`: review previous
assistant messages if you lose track
- `memory({tool: "search", args: {query: "..."}})` — search past conversations
- `memory({tool: "search", args: {query: "..."}})`: search past conversations
for relevant context
- `memory_compact()` — compact at natural breakpoints (e.g., after completing a
- `memory_compact()`: compact at natural breakpoints (e.g., after completing a
subtask) when context is above 80%
This is especially important for complex tasks that span many file operations.
@@ -208,50 +208,54 @@ This is especially important for complex tasks that span many file operations.
## Project Conventions
Read `AGENTS.md` at project root for the full authoritative list. Key
rules (summary — AGENTS.md wins on any conflict):
rules (summary; AGENTS.md wins on any conflict):
1. **No comments in code** — Per project convention. Doc comments (`///`,
1. **No comments in code**: per project convention. Doc comments (`///`,
`//!`) are fine and expected on public API.
2. **Error handling** — `thiserror` for library error types. No panics in
2. **Error handling**: `thiserror` for library error types. No panics in
library code. No `unwrap()` or `expect()` outside tests. 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. Do not introduce
3. **`tokio` is the async runtime**: all I/O is async. Do not introduce
blocking I/O on the async path. Use `tokio::sync` primitives (`oneshot`,
`mpsc`) for correlation and subscription channels; `parking_lot` for
short-held internal locks.
4. **No secret material on the wire or in plaintext at rest** — outbound
4. **No secret material on the wire or in plaintext at rest**: outbound
credentials flow through `Capabilities` injected at the assembly layer;
stored credentials live in alkvault, metadata stores hold references.
5. **No-env-vars invariant** — no handler reads outbound credentials from
5. **No-env-vars invariant**: no handler reads outbound credentials from
any source other than `OperationContext.capabilities`. This is a
spec-level invariant, not a runtime convention.
6. **Wire formats are stable one-way doors** — once published, an existing
6. **Wire formats are stable one-way doors**: once published, an existing
wire format shape must not change (see AGENTS.md and the architecture
ADRs for the current list per crate).
7. **Visible-surface = authorized-surface** — use alkcall's model:
7. **Visible-surface = authorized-surface**: use alkcall's model:
`AccessControl` on every op; `Visibility::Internal` ops are never
wire-callable. No unauthenticated endpoints unless a repo is explicitly
public.
8. **gitoxide for git primitives** — storage and pkt-line come from gix
crates; never shell out to the `git` binary in the serving path; never
copy code from MPL-2.0-licensed reference projects (see
`docs/research/reference-policy.md`).
9. **Feature flags** — optional surface is feature-gated (`sha256`,
`acme`); base crates compile lean. Verify both `cargo test` (default)
and `cargo test --all-features` pass if features are added.
10. **Naming conventions** — Rust standard: `snake_case` for
wire-callable. No unauthenticated endpoints; every capability the
wire can reach must be explicitly granted.
8. **No-deformation rule (family ADR-002 decision 3)**: alkforge is an
assembly on alkgit's core; it never pulls, patches, or forks the
core to fit assembly needs. Gaps and asks are filed upstream first
(alkcall is ours; alkgit/alkgen/alkssh likewise). Protocol asks to
third-party upstream, never silently absorbed.
9. **Fail-closed feature posture**: every optional capability is a
cargo feature and declines unconfigured/unfeatured requests
fail-closed; the base build has no capabilities (the alkssh
feature-graph shape at the assembly's doors). Verify both
`cargo test` (default) and `cargo test --all-features` pass if
features are added.
10. **Naming conventions**: Rust standard: `snake_case` for
functions/variables/modules, `PascalCase` for types/traits,
`SCREAMING_SNAKE_CASE` for constants.
11. **Module structure** — one module per file under `src/`, re-exported
11. **Module structure**: one module per file under `src/`, re-exported
from `src/lib.rs`. Public API surface is `lib.rs` re-exports.
## Key Principles
1. **Read first** - understand before implementing
2. **Verify before completing** - all criteria met
3. **Safe exit is okay** - better to block than force failures
4. **Minimal changes** - implement exactly what's needed
5. **Worktree isolation** - never touch files outside your worktree
6. **Communicate** - use `worktree({action: "notify", ...})` to keep coordinator
1. **Read first**: understand before implementing
2. **Verify before completing**: all criteria met
3. **Safe exit is okay**: better to block than force failures
4. **Minimal changes**: implement exactly what's needed
5. **Worktree isolation**: never touch files outside your worktree
6. **Communicate**: use `worktree({action: "notify", ...})` to keep coordinator
informed
+21 -21
View File
@@ -7,32 +7,32 @@ temperature: 0.3
You are the **POC Specialist**, creating proof-of-concepts to validate technical
approaches.
## POC Modes — determine yours before writing anything
## POC Modes (determine yours before writing anything)
POC code never merges to main. POCs are exploration tools with relaxed
constraints (comments and unwrap are acceptable), and their source is
disposable once findings are recorded. Two modes:
- **Branch mode** — the hypothesis builds on code in this repo. You are in
- **Branch mode**: the hypothesis builds on code in this repo. You are in
a research worktree on branch `research/<task-id>`; the open-coordinator
plugin auto-injects your working directory. Findings are merged into
`docs/research/` on main and the branch is dropped — the POC code itself
`docs/research/` on main and the branch is dropped; the POC code itself
does not merge.
- **Standalone mode** — the hypothesis is independent of repo code. Work in
- **Standalone mode**: the hypothesis is independent of repo code. Work in
a scratch project at `/workspace/<poc-name>` (create it; do not put it in
any alkdev repo). Write findings directly into `docs/research/` on main
when done.
The spawn prompt or task file tells you which mode. If it doesn't, ask the
coordinator — don't guess.
coordinator; don't guess.
## Your Environment (branch mode)
**You are in a research worktree.** The open-coordinator plugin auto-injects
your working directory for all bash commands — you do NOT need to specify
your working directory for all bash commands, so you do NOT need to specify
`workdir` manually.
- The current directory IS the worktree — do NOT navigate elsewhere
- The current directory IS the worktree; do NOT navigate elsewhere
- You are on branch `research/<task-id>`
- Use relative paths for all file operations
@@ -55,7 +55,7 @@ worktree({action: "status"}) → Show worktree git status
## Your Environment (standalone mode)
There is no worktree tool mapping. Your working directory is the scratch
project root (e.g. `/workspace/alkgit-poc1`); create it with `cargo new` or
project root (e.g. `/workspace/alkforge-poc1`); create it with `cargo new` or
similar if it doesn't exist. Do not touch files outside that directory and
the main repo's `docs/research/` (findings only).
@@ -82,11 +82,11 @@ session is mapped to a worktree. This means you can just run commands without
specifying workdir:
```bash
# ✅ CORRECT — workdir is auto-injected
# ✅ CORRECT: workdir is auto-injected
cargo test
```
**Do NOT use `cd` in commands** — it doesn't persist and the plugin handles
**Do NOT use `cd` in commands**; it doesn't persist and the plugin handles
routing.
## When You Are Spawned
@@ -126,11 +126,11 @@ mkdir -p poc/<topic>
Guidelines:
- **Minimal scope** - just enough to validate
- **Hardcode values** - don't build config systems
- **Skip error handling** - focus on happy path
- **No tests for tests' sake** - only what's needed to prove it works
- **Timebox** - if taking too long, Safe Exit
- **Minimal scope**: just enough to validate
- **Hardcode values**: don't build config systems
- **Skip error handling**: focus on happy path
- **No tests for tests' sake**: only what's needed to prove it works
- **Timebox**: if taking too long, Safe Exit
### 4. Validate POC
@@ -233,10 +233,10 @@ worktree({action: "notify", args: {message: "POC completed: <task-id>", level: "
## Key Principles
1. **Minimal viable** - prove the concept, nothing more
2. **Document ruthlessly** - findings are the deliverable
3. **Timebox strictly** - abandon if taking too long
4. **Honest assessment** - don't make it work at all costs
5. **Stay in your lane** - branch mode: never touch files outside your
research worktree; standalone mode: never touch files outside your
1. **Minimal viable**: prove the concept, nothing more
2. **Document ruthlessly**: findings are the deliverable
3. **Timebox strictly**: abandon if taking too long
4. **Honest assessment**: don't make it work at all costs
5. **Stay in your lane**: in branch mode, never touch files outside your
research worktree; in standalone mode, never touch files outside your
scratch project and the main repo's `docs/research/`
+272
View File
@@ -0,0 +1,272 @@
# 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.
```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
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`).
+18
View File
@@ -0,0 +1,18 @@
# alkforge
The alk family's near-term assembly: a minimal forge replacing the
gitea deployment, built as an assembly on alkgit's core (family
ADR-002, `/workspace/@alkdev/alkfamily/docs/architecture/decisions/002-alkforge-gitea-replacement.md`).
It composes family crates rather than re-implementing them: alkgit (+
alkgit-substrate) as the git core, alkssh (git feature) and alkhttp as
the transport doors, alkgen for server-side rendering, alkstore and
alkblobs for persistence, alkvault for secrets, and alkcall as the
substrate underneath. reverse-proxy fronts the deployment.
Status: SDD phase 0 (research docs only). See
`docs/research/phase-0.md` and the family docs for the current design
direction. The README proper gets written when the crate is near
publish (v0.1.0).
License: MIT OR Apache-2.0.
+53 -52
View File
@@ -2,7 +2,7 @@
## Overview
This document defines the SDD process for the @alkdev/alkgit project. It
This document defines the SDD process for the @alkdev/alkforge project. It
leverages:
- **OpenCode CLI** as the agent execution environment
@@ -18,7 +18,7 @@ leverages:
4. **Task-Driven**: Structured task graphs with dependency analysis
5. **Safe Exit**: Always have a way to unblock progress when stuck
6. **Categorical Estimates**: Use risk/scope/impact categories, not time
estimates. These are structurally important — upstream failures multiply
estimates. These are structurally important: upstream failures multiply
downstream damage regardless of developer type (human or LLM). See the
cost-benefit framework in taskgraph's framework docs.
@@ -37,7 +37,7 @@ problems need investigation.
4. Document learnings
5. Converge on recommended approach
**POC placement (two modes)** — POC code never merges to main; the
**POC placement (two modes)**: POC code never merges to main; the
deliverable is the findings write-up:
- **Branch mode** (POC builds on code in this repo): git branch off main,
@@ -56,11 +56,11 @@ approaches
**Process**:
1. Architect creates the architecture documentation structure:
- `docs/architecture/README.md` — index with doc table, ADR table, lifecycle
- `docs/architecture/overview.md` — package purpose, exports, dependencies
- `docs/architecture/<component>.md` — one focused doc per component/area
- `docs/architecture/decisions/` — numbered ADR files (ADR-001, ADR-002, ...)
- `docs/architecture/open-questions.md` — centralized tracker with OQ-IDs
- `docs/architecture/README.md`: index with doc table, ADR table, lifecycle
- `docs/architecture/overview.md`: package purpose, exports, dependencies
- `docs/architecture/<component>.md`: one focused doc per component/area
- `docs/architecture/decisions/`: numbered ADR files (ADR-001, ADR-002, ...)
- `docs/architecture/open-questions.md`: centralized tracker with OQ-IDs
2. All docs start in `Draft` status. Spec docs reference ADRs by number (not
inline rationale) and OQs by number (not inline questions).
3. Architecture Review validates for ambiguities, risks, and structural issues
@@ -146,16 +146,16 @@ WHAT, ADRs explain WHY, open questions track what's unresolved.
- Extract decisions into numbered ADRs in `decisions/` directory
- Centralize open questions in `open-questions.md` with OQ-IDs
- Maintain `README.md` as the architecture index
- Keep spec documents focused (~500 lines) — reference ADRs and OQs, don't inline them
- Keep spec documents focused (about 500 lines); reference ADRs and OQs, don't inline them
- Redirect exploration work to Research Specialist
- Iterate based on review feedback
**Deliverables**:
- `docs/architecture/README.md` — index with doc table, ADR table, lifecycle
- `docs/architecture/<component>.md` — focused spec documents
- `docs/architecture/decisions/` — numbered ADR files
- `docs/architecture/open-questions.md` — centralized OQ tracker
- `docs/architecture/README.md`: index with doc table, ADR table, lifecycle
- `docs/architecture/<component>.md`: focused spec documents
- `docs/architecture/decisions/`: numbered ADR files
- `docs/architecture/open-questions.md`: centralized OQ tracker
---
@@ -191,17 +191,17 @@ sessions.
**Mode**: Primary (manages worktrees and agent sessions)
**Uses**: The `worktree` tool from the **open-coordinator** opencode plugin.
Single tool with `{action, args}` dispatch. Role is auto-detected — coordinator
Single tool with `{action, args}` dispatch. Role is auto-detected: coordinator
sessions get the full operation set, spawned implementation sessions get a
limited set (current, notify, status). No mode toggle required.
**Tools**:
- `worktree({action, args})` — spawn, sessions, dashboard, message, abort,
- `worktree({action, args})`: spawn, sessions, dashboard, message, abort,
cleanup
- Bash (opencode CLI for session interaction)
- Read (monitor task files)
- `memory` / `memory_compact` — context management and session history (via
- `memory` / `memory_compact`: context management and session history (via
@alkdev/open-memory, when available)
**Key Behaviors**:
@@ -230,10 +230,10 @@ limited set (current, notify, status). No mode toggle required.
**Tools**:
- Read, Write, Edit, Glob, Grep, Bash
- `worktree({action: "notify", ...})` — report progress/blockers to coordinator
- `worktree({action: "current"})` — verify worktree assignment
- `worktree({action: "notify", ...})`: report progress/blockers to coordinator
- `worktree({action: "current"})`: verify worktree assignment
- webSearch (documentation lookup)
- `memory` / `memory_compact` — context management (via @alkdev/open-memory,
- `memory` / `memory_compact`: context management (via @alkdev/open-memory,
when available)
**Key Behaviors**:
@@ -340,9 +340,9 @@ mode: scratch project outside the repo)
- Create minimal POCs to validate hypotheses
- Branch mode: work in isolated research worktrees; standalone mode: work
in the scratch project only
- Document findings and recommendations (findings are the deliverable —
POC source is disposable and never merges to main)
- Timebox strictly - abandon if taking too long
- Document findings and recommendations (findings are the deliverable; POC
source is disposable and never merges to main)
- Timebox strictly; abandon if taking too long
- Be honest about limitations and blockers
**When Invoked**:
@@ -404,7 +404,7 @@ Implement OAuth2 authentication with provider abstraction.
### Categorical Estimates
These fields are structurally important, not optional metadata. They power
`taskgraph decompose`, `risk-path`, `critical`, and `bottleneck` — commands that
`taskgraph decompose`, `risk-path`, `critical`, and `bottleneck`: commands that
reveal structural problems in the task graph. A task missing `scope`, `risk`,
`impact`, or `level` is a red flag indicating incomplete decomposition. See the
cost-benefit framework in taskgraph's framework docs for the reasoning.
@@ -432,7 +432,7 @@ cost-benefit framework in taskgraph's framework docs for the reasoning.
| phase | A whole phase's milestone or gate slips (e.g. a decomposition OQ)|
| project | The project's contract: wire formats, published API, security |
**Valid frontmatter enum variants (taskgraph 0.1.3)** — only these
**Valid frontmatter enum variants (taskgraph 0.1.3)**: only these
literal values parse; anything else fails `taskgraph validate`:
- `status`: `pending` | `in-progress` | `completed` | `failed` | `blocked`
@@ -443,20 +443,21 @@ literal values parse; anything else fails `taskgraph validate`:
Three gotchas worth stating because agents have hit all three:
1. `status: done` is **not** a value — the lifecycle word is
1. `status: done` is **not** a value; the lifecycle word is
`completed` (and `in-progress`, not `in_progress` or `wip`).
2. A file in `tasks/` **without frontmatter is silently skipped** by
`taskgraph validate`, `list`, and every graph command — no error,
the file just doesn't exist as far as the graph is concerned.
`taskgraph validate`, `list`, and every graph command. It produces
no error; the file just doesn't exist as far as the graph is
concerned.
Every tracker or task file under `tasks/` must carry a complete
frontmatter block (`id`, `name` required; `depends_on` plus the
five categorical fields expected). If you need a plain prose
tracker, keep it under `docs/` instead.
3. `status` transitions run through the lifecycle only: `pending` →
`in-progress` → `completed` (or `failed`/`blocked`). A finished
task never goes sideways to `done`/`finished` — the enum is closed.
task never goes sideways to `done`/`finished`; the enum is closed.
When in doubt, run `taskgraph validate` — it is the gate that CI-style
When in doubt, run `taskgraph validate`: it is the gate that CI-style
checks and review passes will hit.
### Task Lifecycle
@@ -472,15 +473,15 @@ checks and review passes will hit.
### Architecture work and the `level` field
Architecture work uses the same taskgraph format as implementation work —
Architecture work uses the same taskgraph format as implementation work;
there is no separate `architecture` level. The `level` enum
(`planning, decomposition, implementation, review, research`) already covers
architecture activities:
| Architecture activity | `level` | Rationale |
|---|---|---|
| Writing an ADR (the decision) | `implementation` | The ADR *is* the deliverable — the implementation of an architecture decision. Calling it `planning` would mislabel the artifact. |
| Decomposing a spec into ADRs + OQs | `decomposition` | Direct fit — breaking a large architecture area into decideable units. |
| Writing an ADR (the decision) | `implementation` | The ADR *is* the deliverable, namely the implementation of an architecture decision. Calling it `planning` would mislabel the artifact. |
| Decomposing a spec into ADRs + OQs | `decomposition` | Direct fit: breaking a large architecture area into decideable units. |
| Architecture review pass | `review` | Direct fit. |
| Research / POC before a decision | `research` | Direct fit. |
| Spec writing (overview, component spec) | `implementation` | The spec is the deliverable. |
@@ -530,8 +531,8 @@ the deferral has two halves that must stay in sync:
2. **The machine-readable half** (in `tasks/architecture/`): an
external-trigger tracker task represents the unblocking condition. It is
tagged `[external-trigger, deferred-oq]`, has `risk: trivial` and
`level: research` (it is not actionable work — it tracks whether the
external condition has arrived), and its `id` is referenced from the OQ's
`level: research` (it is not actionable work; it tracks whether the
external condition has arrived), and its `id` is referenced from the OQ's
`Blocked on:` text. This is the surface for the task graph: `taskgraph`
tools can reason about it, and downstream work that depends on the
decision can declare `depends_on: [architecture/oq-NN-...]`.
@@ -540,7 +541,7 @@ When the unblocking condition arrives (a use case materializes, a crate is
specced), the tracker task is marked `completed` and the OQ transitions from
`deferred(scope)` to `open` (or directly to `resolved` if no architecture
decision remains). The two halves serve different audiences and use one edge
type (`depends_on`) — the reverse lookup uses `taskgraph dependents`. Do not
type (`depends_on`); the reverse lookup uses `taskgraph dependents`. Do not
add a `blocks:` field; it duplicates the edge and creates a sync hazard.
## Review Injection
@@ -559,8 +560,8 @@ Use graph analysis to determine where reviews should happen:
### Current (open-coordinator plugin)
The Coordinator uses the `worktree` tool from the open-coordinator opencode
plugin. It's a single tool with `{action, args}` dispatch — no separate
enable/toggle steps. Role is auto-detected from session state.
plugin. It's a single tool with `{action, args}` dispatch, so there are no
separate enable/toggle steps. Role is auto-detected from session state.
```
1. Identify parallel work
@@ -613,13 +614,13 @@ a worktree.
When the open-memory plugin is available alongside open-coordinator, the
coordinator gains:
- `memory({tool: "children", args: {sessionId: "..."}})` — view sub-agent
- `memory({tool: "children", args: {sessionId: "..."}})`: view sub-agent
sessions spawned from the coordinator
- `memory({tool: "messages", args: {sessionId: "..."}})` — read a spawned
- `memory({tool: "messages", args: {sessionId: "..."}})`: read a spawned
session's conversation for debugging
- `memory({tool: "context"})` — check context window usage before long
- `memory({tool: "context"})`: check context window usage before long
monitoring sessions
- `memory_compact()` — proactively compact at natural breakpoints
- `memory_compact()`: proactively compact at natural breakpoints
Implementation agents can also use `memory({tool: "context"})` and
`memory_compact()` to manage their context during long tasks.
@@ -643,7 +644,7 @@ Once the hub is operational, coordination uses native operations:
```
State moves from in-process tracking to Postgres `mappings` table. The
open-coordinator plugin becomes unnecessary — the hub provides the same
open-coordinator plugin becomes unnecessary: the hub provides the same
capabilities as server-side operations accessible from any environment.
## Document Structure
@@ -710,7 +711,7 @@ Accepted | Proposed | Deprecated | Superseded
(Links to related specs and ADRs)
```
Numbering starts at 001 within each project. ADRs are stable — once Accepted,
Numbering starts at 001 within each project. ADRs are stable: once Accepted,
they don't revert. If superseded, mark the old one and create a new one.
### Open Questions Format
@@ -726,7 +727,7 @@ they don't revert. If superseded, mark the old one and create a new one.
- **Status**: open | resolved | deferred(scope) | partially resolved
- **Priority**: high | medium | low
- **Resolution**: (when resolved)
- **Blocked on**: (when deferred — concrete dependency that would unblock)
- **Blocked on**: (when deferred; the concrete dependency that would unblock)
- **Cross-references**: OQ-NN, ADR-NNN
```
@@ -761,14 +762,14 @@ questions affecting this document:
Agent definitions are in `.opencode/agents/`:
- **architect.md** - Creates architecture specifications
- **decomposer.md** - Transforms architecture to task graph
- **coordinator.md** - Orchestrates parallel execution
- **implementation-specialist.md** - Executes tasks with self-verification
- **poc-specialist.md** - Creates proof-of-concepts for validation
- **code-reviewer.md** - Reviews code quality at checkpoints
- **architecture-reviewer.md** - Validates architecture specs
- **research-specialist.md** - Researches and documents findings
- **architect.md**: creates architecture specifications
- **decomposer.md**: transforms architecture to task graph
- **coordinator.md**: orchestrates parallel execution
- **implementation-specialist.md**: executes tasks with self-verification
- **poc-specialist.md**: creates proof-of-concepts for validation
- **code-reviewer.md**: reviews code quality at checkpoints
- **architecture-reviewer.md**: validates architecture specs
- **research-specialist.md**: researches and documents findings
Use with opencode CLI: