Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 10 additions & 6 deletions .github/workflows/ci-typecheck.yml
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
# ============================================================================
# 🔍 CI · Typecheck
# ----------------------------------------------------------------------------
# Purpose : TypeScript type checking across all packages (bun typecheck)
# plus oxlint warning ratchet (bun run lint, --max-warnings gate)
# Purpose : TypeScript type checking across all packages (bun typecheck),
# oxlint warning ratchet, and the DAG core behavior/coverage gate
# Trigger : Push to `main`/`dev`, PRs targeting `main`/`dev`, manual dispatch
# Jobs : typecheck — single Linux runner, `bun run lint` + `bun typecheck`
# Gate : Required status check on BOTH `dev` and `main` rulesets — it is
# the fast gate for feat/fix → dev PRs (full test suite only gates
# dev → main, see ci-test.yml). Lint lives inside this job so it
# blocks merges without editing the rulesets' required checks.
# Gate : Required status check on BOTH `dev` and `main` rulesets. The DAG
# core gate protects state-machine and persistence changes before
# they merge to dev; the full suite still gates dev → main.
# Notes : No push trigger on feat/* or fix/* (frequent changes); PRs cover
# them.
# ============================================================================
Expand Down Expand Up @@ -42,3 +41,8 @@ jobs:

- name: Run typecheck
run: bun typecheck

- name: Run DAG core behavior and coverage gate
working-directory: packages/opencode
run: bun run test:dag-core
timeout-minutes: 10
14 changes: 14 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -214,3 +214,17 @@ Invariants for extending the SolidJS/opentui TUI. The DAG inspector (`src/featur
- Keep delivery vocabulary explicit. Prompts steer by default and promote at the next safe provider-turn boundary while the current drain requires continuation. An explicit `queue` input remains pending until the Session would otherwise become idle; promote one queued input at that boundary, then reevaluate continuation before promoting another. Promoting any new user input resets the selected agent's provider-turn allowance; a batch of steers resets it once.
- Keep EventV2 replay owner claims separate from clustered Session execution ownership.
- Keep the System Context algebra, registry, and built-ins in `src/system-context`; keep Context Source producers with their observed domains, and keep Session History selection plus Context Epoch persistence Session-owned.

## Agent skills

### Issue tracker

Issues and PRDs are tracked in this repository's GitHub Issues through the `gh` CLI. See `docs/agents/issue-tracker.md`.

### Triage labels

Triage uses the five canonical labels `needs-triage`, `needs-info`, `ready-for-agent`, `ready-for-human`, and `wontfix`. See `docs/agents/triage-labels.md`.

### Domain docs

This repository uses a multi-context domain-document layout rooted at `CONTEXT-MAP.md`. See `docs/agents/domain.md`.
11 changes: 11 additions & 0 deletions CONTEXT-MAP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Context Map

Read the context documents relevant to the code or decision under review. Do not load unrelated contexts by default.

| Context | Domain document | Primary areas |
| --- | --- | --- |
| Session Runtime and Client Contract | [`CONTEXT.md`](CONTEXT.md) | `packages/opencode/src/session`, `packages/opencode/src/system-context`, `packages/protocol`, `packages/client`, `packages/sdk` |

## Contexts created lazily

DAG orchestration does not yet have a dedicated `CONTEXT.md`. The full DAG review must establish terminology from implementation, tests, existing specifications, and accepted decisions before `/domain-modeling` creates one. Add future contexts to this map only when they have a stable document to reference.
1 change: 1 addition & 0 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

37 changes: 37 additions & 0 deletions docs/agents/domain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Domain Docs

How engineering skills consume this repository's domain documentation while exploring the codebase.

## Selected layout

This repository uses a **multi-context** layout. `CONTEXT-MAP.md` is the entry point and points to the domain documents relevant to each bounded context.

## Before exploring

1. Read `CONTEXT-MAP.md` at the repository root.
2. Read each linked `CONTEXT.md` relevant to the work.
3. Read system-wide ADRs under `docs/adr/` and context-scoped ADRs linked by the map.

If a referenced directory or document does not exist, proceed silently. Do not create speculative terminology or ADRs merely to fill the layout. `/domain-modeling`, reached through `/grill-with-docs` or `/improve-codebase-architecture`, creates them when terms or decisions are actually resolved.

## File structure

```text
/
├── CONTEXT-MAP.md # context index
├── CONTEXT.md # existing Session Runtime context
├── docs/adr/ # system-wide decisions, created lazily
└── packages/<context>/
├── CONTEXT.md # context vocabulary, created lazily
└── docs/adr/ # context decisions, created lazily
```

## Use the glossary vocabulary

When an issue title, refactor proposal, hypothesis, or test names a domain concept, use the term defined in the relevant `CONTEXT.md`. Do not replace a defined term with a synonym that the glossary explicitly avoids.

If the required concept is absent, reconsider whether the project already uses another term. If the gap is real, record it for `/domain-modeling`.

## Flag ADR conflicts

If proposed work contradicts an existing ADR, state the conflict explicitly rather than silently overriding it.
39 changes: 39 additions & 0 deletions docs/agents/issue-tracker.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Issue tracker: GitHub

Issues and PRDs for this repo live as GitHub issues. Use the `gh` CLI for all operations.

## Conventions

- **Create an issue**: `gh issue create --title "..." --body "..."`. Use a heredoc for multi-line bodies.
- **Read an issue**: `gh issue view <number> --comments`, filtering comments by `jq` and also fetching labels.
- **List issues**: `gh issue list --state open --json number,title,body,labels,comments --jq '[.[] | {number, title, body, labels: [.labels[].name], comments: [.comments[].body]}]'` with appropriate `--label` and `--state` filters.
- **Comment on an issue**: `gh issue comment <number> --body "..."`
- **Apply or remove labels**: `gh issue edit <number> --add-label "..."` or `--remove-label "..."`
- **Close**: `gh issue close <number> --comment "..."`

Infer the repository from `git remote -v`; `gh` does this automatically when run inside this clone.

## Pull requests as a triage surface

**PRs as a request surface: no.**

Pull requests are delivery artifacts, not incoming requests. `/triage` does not include them in its queue.

GitHub shares one number space across issues and PRs. Resolve an ambiguous `#42` with `gh pr view 42`, then fall back to `gh issue view 42`.

## Skill operations

- When a skill says **publish to the issue tracker**, create a GitHub issue.
- When a skill says **fetch the relevant ticket**, run `gh issue view <number> --comments`.
- Use GitHub's native blocking relationships when available. If unavailable, put `Blocked by: #<n>` at the top of the issue body.

## Wayfinding operations

Used by `/wayfinder`. The map is one issue with child issues as tickets.

- **Map**: an issue labelled `wayfinder:map`, holding Notes, Decisions-so-far, and Fog.
- **Child ticket**: a GitHub sub-issue labelled `wayfinder:<type>` where type is `research`, `prototype`, `grilling`, or `task`. If sub-issues are unavailable, link it from a task list in the map and put `Part of #<map>` at the top of the child body.
- **Blocking**: prefer GitHub native issue dependencies. Use the blocker's numeric database ID with the dependencies API, not its issue number or node ID. Fall back to a `Blocked by:` line only when native dependencies are unavailable.
- **Frontier query**: select the first open, unassigned child in map order whose blockers are all closed.
- **Claim**: `gh issue edit <n> --add-assignee @me` is the working session's first write.
- **Resolve**: comment with the decision, close the child, and add its context pointer to the map's Decisions-so-far.
13 changes: 13 additions & 0 deletions docs/agents/triage-labels.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Triage Labels

The engineering skills use five canonical triage roles. This table maps each role to the label used in this repository's GitHub Issues.

| Canonical role | GitHub label | Meaning |
| --- | --- | --- |
| `needs-triage` | `needs-triage` | A maintainer needs to evaluate the issue |
| `needs-info` | `needs-info` | Waiting for more information from the reporter |
| `ready-for-agent` | `ready-for-agent` | Fully specified and ready for an agent |
| `ready-for-human` | `ready-for-human` | Requires human implementation or judgment |
| `wontfix` | `wontfix` | Will not be actioned |

When a skill mentions a triage role, use the corresponding GitHub label from this table.
43 changes: 43 additions & 0 deletions docs/dag-core-tdd-ci-matrix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# DAG core TDD / CI coverage matrix

Baseline: `origin/dev` at `d482e4bb6` (2026-08-09). This matrix treats public behavior as the unit of coverage. It does not use private-helper tests or a repository-wide percentage as a proxy for correctness.

## Confirmed seams

| Seam | Core invariant | Existing behavior tests | Current CI enforcement | Finding |
| ------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Workflow tool / `Dag.Service` | Start, extend, replan and controls publish only legal durable transitions; invalid input has no side effects | `workflow-tool.test.ts`, `dag-create-validation.test.ts`, `dag-step-semantics.test.ts`, `dag-node-started-guard.test.ts`, `dag-dynamic-correctness.test.ts` | New DAG gate runs on PRs to `dev` and `main` | **Closed here:** one public-seam test now drives pause/resume/cancel/complete/step through `WorkflowTool.execute` and checks the durable event type. |
| Durable store → replay → recovery | An acknowledged wake is atomic; replay rebuilds the same read model; a restart neither duplicates work nor leaves invented running ownership | `dag-store-wake.test.ts`, `dag-replay-idempotency.test.ts`, `dag-deadline-extended.test.ts`, `dag-recovery.test.ts`, `dag-loop-recovery-integration.test.ts`, `dag-orphan-pending-recovery.test.ts` | New DAG gate runs the behavior suites and enforces critical store/projector floors | Behavior is strong; the missing PR gate and coverage floor are closed here. Store-defect retry semantics remain intentionally undecided, so they are not a missing test for an accepted behavior yet. |
| Runtime state machine | Wake batches are stable and retryable; spawn owns at most one attempt; abort settles live children; lock timeout releases; escalation is delivered before adjudication; every terminal path clears pending escalation | `dag-wake-integration.test.ts`, `spawn-completion.test.ts`, `dag-loop-guards.test.ts`, `dag-workflow-lock.test.ts`, `dag-timeout-escalation*.test.ts`, `dag-escalation-clear-flag.test.ts`, race regressions | New DAG gate runs the complete OpenCode `test/dag` directory | Core P0/P1 behavior is covered. The prior gap was enforcement and measured floors, not another broad behavior suite. |
| Durable event / SDK / TUI projection | Event folds are replay-safe; generated SDK stays fresh; summary events replace the server-derived view and recover missed SSE updates by refetching | `dag-projector-drift.test.ts`, `dag-summary-publisher*.test.ts`, `sync-dag.test.tsx`, `dag-inspector*.test.ts`; SDK `check:generated` | New DAG gate runs Schema, SDK freshness and TUI contract suites on both PR targets | **Closed here:** every DAG durable definition is now checked at its versioned manifest key. The TUI behavior itself was already covered. |
| CI quality gate | A DAG-critical regression must fail before merge to `dev`; critical public modules may not silently lose tested lines/functions | `bun turbo test` covers all package tests; Linux unit + Linux/Windows app E2E; typecheck/lint on `dev` and `main` PRs | `ci-typecheck.yml` now runs `bun run test:dag-core` on PRs to `dev` and `main` | **Closed here:** package-scoped behavior suites plus fixed per-module line/function floors. Bun 1.3.14 LCOV contains no branch records, so transition-matrix tests remain the branch-equivalent guard. |

## Measured baseline

| Surface | Tests | Relevant measured coverage |
| -------------------------------------------------- | ----: | --------------------------------------------------------------------------------------------------------------- |
| Core graph / scheduling / transition / store suite | 90 | scheduling 94.56% lines; transitions 96.00%; types 94.78%; store 78.34% in the Core-only slice |
| OpenCode DAG public/runtime suite | 397 | `dag.ts` 99.55%; loop 93.73%; recovery 99.30%; spawn 94.58%; projector 100%; store 87.43%; workflow tool 90.80% |
| TUI DAG projection / inspector suite | 50 | inspector utils 100%; inspector 91.69%; sync DAG reducer/bootstrap/reconnect has five named behavior tests |

The Core-only projector number (19.23%) is not a defect: projector behavior lives primarily in the OpenCode integration slice, where the same public projector reaches 100% lines. Coverage must therefore be evaluated per agreed suite, not by averaging unrelated package imports.

## Enforced gate policy

`packages/opencode/script/dag-core-coverage.ts` now:

1. run the Core, OpenCode, Schema and TUI DAG contract suites from their package directories;
2. run generated SDK freshness validation;
3. parse LCOV for explicitly named critical files;
4. fail below conservative line/function floors with enough headroom for harmless refactors;
5. keep fault-injection/retry policy out until its shutdown and backoff contract is designed.

The floors are fixed in source and intentionally sit below the measured baseline: Core critical modules range from 70–94% lines and 60–95% functions; OpenCode critical modules range from 85–98% lines and 70–95% functions; TUI critical modules range from 90–98% lines and 88–95% functions. A future change may raise them, but lowering them requires an explicit code review diff.

## Red → green evidence

1. Coverage evaluator: `bun test test/dag/dag-core-coverage-gate.test.ts` first failed with `Cannot find module '../../script/dag-core-coverage'`; after the minimal parser/assertion implementation it passed `1 pass, 0 fail`.
2. Public control dispatch: with the cancel branch deliberately routed to pause, `bun test test/dag/workflow-tool.test.ts --test-name-pattern 'dispatches every public control operation'` failed with `Expected: "dag.workflow.cancelled"; Received: "dag.workflow.paused"`; after restoring the correct public behavior it passed `1 pass, 0 fail`.
3. Durable manifest membership: with the DAG inventory deliberately omitted, `bun test test/event-manifest.test.ts --test-name-pattern 'registers every DAG durable event'` failed with `Received: undefined`; after restoring the inventory it passed `1 pass, 20 expect() calls, 0 fail`.
4. CI failure proof: with only the loop line floor temporarily raised from 90% to 99%, `bun run test:dag-core` ran `399 pass, 0 fail` and still exited 1 with `loop.ts: lines 93.73% < 99.00%`. The committed floor is restored to 90%; this proves coverage loss fails the same command invoked by CI.
5. Final gate: `bun run test:dag-core` passed Core `90`, OpenCode `399`, Schema `3`, and TUI `50` tests; SDK regeneration produced no diff and every critical-file floor passed.
2 changes: 2 additions & 0 deletions packages/opencode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"scripts": {
"typecheck": "tsgo --noEmit",
"test": "bun test --timeout 30000 --only-failures",
"test:dag-core": "bun run script/dag-core-coverage.ts",
"test:httpapi": "bun run script/httpapi-exercise.ts --mode coverage --fail-on-missing --fail-on-skip && bun run script/httpapi-exercise.ts --mode auth --fail-on-missing --fail-on-skip && bun run script/httpapi-exercise.ts --mode effect --fail-on-missing --fail-on-skip",
"test:httpapi:ci": "bun run script/httpapi-exercise.ts --mode coverage --fail-on-missing --fail-on-skip && bun run script/httpapi-exercise.ts --mode auth --fail-on-missing --fail-on-skip && bun run script/httpapi-exercise.ts --mode effect --fail-on-missing --fail-on-skip --progress --trace",
"bench:test": "bun run script/bench-test-suite.ts",
Expand Down Expand Up @@ -149,6 +150,7 @@
"web-tree-sitter": "0.25.10",
"ws": "8.21.0",
"xdg-basedir": "5.1.0",
"yaml": "2.9.0",
"yargs": "18.0.0",
"zod": "catalog:"
},
Expand Down
Loading
Loading