From 95399b46a299c989deb989720c1e4a613d23246f Mon Sep 17 00:00:00 2001 From: "glm-5.3-flash" Date: Sat, 7 Nov 2026 06:39:44 +0000 Subject: [PATCH] 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/ --- .opencode/agents/architect.md | 101 +++---- .opencode/agents/architecture-reviewer.md | 32 +-- .opencode/agents/code-reviewer.md | 8 +- .opencode/agents/coordinator.md | 64 +++-- .opencode/agents/implementation-specialist.md | 84 +++--- .opencode/agents/poc-specialist.md | 42 +-- AGENTS.md | 272 ++++++++++++++++++ README.md | 18 ++ docs/sdd_process.md | 105 +++---- 9 files changed, 513 insertions(+), 213 deletions(-) create mode 100644 AGENTS.md create mode 100644 README.md diff --git a/.opencode/agents/architect.md b/.opencode/agents/architect.md index 78c2e27..1d39735 100644 --- a/.opencode/agents/architect.md +++ b/.opencode/agents/architect.md @@ -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**: - **Priority**: -- **Impacts**: +- **Impacts**: - **Blocked on**: (for deferred(scope)) **Investigation**: (for deferred(unclear)) -- **Resolution**: Not yet decidable. - **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/ # required 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 diff --git a/.opencode/agents/architecture-reviewer.md b/.opencode/agents/architecture-reviewer.md index 093104a..b4ff15a 100644 --- a/.opencode/agents/architecture-reviewer.md +++ b/.opencode/agents/architecture-reviewer.md @@ -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. diff --git a/.opencode/agents/code-reviewer.md b/.opencode/agents/code-reviewer.md index 435b753..f93bca7 100644 --- a/.opencode/agents/code-reviewer.md +++ b/.opencode/agents/code-reviewer.md @@ -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`) diff --git a/.opencode/agents/coordinator.md b/.opencode/agents/coordinator.md index d1d18e1..bd68924 100644 --- a/.opencode/agents/coordinator.md +++ b/.opencode/agents/coordinator.md @@ -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/", 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}}. ", 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/", 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. diff --git a/.opencode/agents/implementation-specialist.md b/.opencode/agents/implementation-specialist.md index 4e3dcaf..42e7197 100644 --- a/.opencode/agents/implementation-specialist.md +++ b/.opencode/agents/implementation-specialist.md @@ -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(): " 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 : ", 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 diff --git a/.opencode/agents/poc-specialist.md b/.opencode/agents/poc-specialist.md index 3990f9d..83c2fcc 100644 --- a/.opencode/agents/poc-specialist.md +++ b/.opencode/agents/poc-specialist.md @@ -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/`; 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/` (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/` - 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/ 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: ", 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/` diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..d0944f8 --- /dev/null +++ b/AGENTS.md @@ -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: + `(): `; 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 `). +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/`). 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`), + `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`). diff --git a/README.md b/README.md new file mode 100644 index 0000000..77fec1c --- /dev/null +++ b/README.md @@ -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. diff --git a/docs/sdd_process.md b/docs/sdd_process.md index 5a8a720..1947057 100644 --- a/docs/sdd_process.md +++ b/docs/sdd_process.md @@ -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/.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/.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/.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/.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: