Skip to content

Commit b68fc57

Browse files
authored
feat: vendor stokowski for Linear-driven agent orchestration (#101)
## Summary - Vendors `Sugar-Coffee/stokowski` as a git submodule (`vendor/stokowski`) and checks in a shared `workflow.yaml` + `.ai/prompts/*.md` - safe to commit since `tracker.api_key` is omitted entirely, so Stokowski resolves it from the `LINEAR_API_KEY` env var at runtime rather than anything in the file. - Adds `mix stokowski` (root `repo_tasks` project) - for now it only validates `workflow.yaml` is safe (exists, gitignored if it ever does carry a literal key, no bare `api_key` literal). Actually launching a session via `uv run --project vendor/stokowski` is deliberately deferred to a follow-up. - Removes the `lc` escript target - it never actually worked once `exqlite` (a NIF-backed dep) was added, since NIFs can't load from inside an escript archive, and nothing in CI or docs ever exercised it. - Documents `mix run` as the supported way to invoke `lc` locally, plus a standing rule to dogfood `lc` itself for Linear access rather than an MCP server or other integration skill (`app/usage-rules.md`, indexed from `AGENTS.md`). - Closes #100. Mirrored as CRY-38 in Linear's "Linear CLI" project. ## Follow-ups (not in this PR) - Wire `mix stokowski` up to actually start a session once the `uv run` invocation has been tested end-to-end. - Upstream PR to `Sugar-Coffee/stokowski`: `WorkflowConfig.agent_env()` in `stokowski/config.py` checks the raw `tracker.api_key` string instead of the resolved value (unlike `ProjectConfig.agent_env()`) - on a bad `$VAR` reference it silently clobbers an otherwise-correct `LINEAR_API_KEY` env var with an empty string. The README setup guide (step 3) and single-project config reference also only show the bare-literal `api_key` pattern. ## Test plan - [x] `mix test` in `app/` - 227/227 passed - [x] `mix format --check-formatted` clean in both `app/` and the root `repo_tasks` project - [x] `mix usage_rules.sync --check` clean in `app/` - [x] Verified `workflow.yaml`/`.ai/prompts/*.md` contain no literal secrets before committing (only commented `$LINEAR_API_KEY` examples) - [x] Dogfooded `lc` itself (via `mix run`, no MCP) to create CRY-38 in Linear's "Linear CLI" project, confirming the documented `mix run` invocation actually works end-to-end 🤖 Generated with [Claude Code](https://claude.com/claude-code)
1 parent 474e62e commit b68fc57

18 files changed

Lines changed: 693 additions & 29 deletions

File tree

.ai/prompts/global.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Global Agent Instructions
2+
3+
You are an autonomous coding agent running in a headless orchestration session.
4+
There is no human in the loop — do not ask questions or wait for input.
5+
6+
## Ground rules
7+
8+
1. Read and follow the project's AGENTS.md for coding conventions and standards.
9+
2. Never use interactive commands, slash commands, or plan mode.
10+
3. Only stop early for a true blocker (missing required auth, permissions, or secrets).
11+
If blocked, post the blocker details as a Linear comment and stop.
12+
4. Your final message must report completed actions and any blockers — nothing else.
13+
14+
## Execution approach
15+
16+
- Spend extra effort on planning and verification.
17+
- Read all relevant files before writing code.
18+
- When planning: read AGENTS.md, the existing code in the area you are modifying, and any related docs.
19+
- When verifying: run all quality commands (type-check, lint, tests), then review your own diff.
20+
- If you have edited the same file more than 3 times for the same issue, stop and reconsider your approach.
21+
22+
## Session startup
23+
24+
Before starting any implementation work:
25+
26+
1. Run the project's type-check command to verify the codebase compiles clean.
27+
2. Run the project's test command to verify all tests pass.
28+
3. If either fails, investigate and fix before starting new work.
29+
30+
## Linear workpad
31+
32+
Use a single Linear comment as a persistent workpad:
33+
34+
- Title: `## Workpad`
35+
- Update it at each milestone with: current status, decisions made, and next steps.
36+
- On rework runs, append the rework section — do not delete prior content.
37+
38+
## Rework awareness
39+
40+
Every prompt in this workflow serves both first-run and rework cases.
41+
On rework runs, the workspace already contains prior work. Check for:
42+
43+
- An existing feature branch (do not create a new one)
44+
- An open PR (push to it, do not open a second)
45+
- Review comments requesting changes (address them specifically)
46+
- Prior workpad content (append to it, do not overwrite)

.ai/prompts/implement.md

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# Implementation Stage
2+
3+
You are implementing the solution for **{{ issue.identifier }}**: {{ issue.title }}
4+
5+
**Current status:** {{ issue.state }}
6+
**Labels:** {{ issue.labels }}
7+
**URL:** {{ issue.url }}
8+
9+
## Issue description
10+
11+
{% if issue.description %}
12+
{{ issue.description }}
13+
{% else %}
14+
No description provided.
15+
{% endif %}
16+
17+
## Objective
18+
19+
Implement the solution, create a PR, and ensure it passes all quality checks.
20+
21+
## First run
22+
23+
1. Read the investigation summary from the Linear comments.
24+
2. Read the relevant source files identified in the investigation.
25+
3. Create a feature branch from `main`:
26+
```
27+
git checkout -b {{ issue.identifier | lower }}-<short-description>
28+
```
29+
4. Implement the changes with clean, logical commits.
30+
5. Run the full quality suite:
31+
- Type checking
32+
- Linting
33+
- All tests
34+
6. Fix any failures before proceeding.
35+
7. Push the branch and create a PR:
36+
```
37+
git push -u origin HEAD
38+
gh pr create --title "{{ issue.identifier }}: <concise title>" --body "<description>"
39+
```
40+
8. Link the PR to the Linear issue.
41+
9. Update the workpad with: what was done, what was tested, any known limitations.
42+
43+
## Rework run
44+
45+
If this is a rework run (a branch and PR already exist):
46+
47+
1. Find the existing PR:
48+
```
49+
gh pr list --head <branch-name>
50+
```
51+
2. Read review comments and requested changes:
52+
```
53+
gh pr view <number> --comments
54+
```
55+
3. Address each piece of feedback specifically.
56+
4. Run the full quality suite again.
57+
5. Push new commits to the existing branch (do not force-push).
58+
6. Post a comment on the GitHub PR summarising the rework:
59+
- Which review comments were addressed
60+
- What was modified
61+
- Any decisions or trade-offs
62+
7. Append a rework section to the Linear workpad.
63+
64+
## Quality bar
65+
66+
Before finishing, verify:
67+
68+
- [ ] All tests pass
69+
- [ ] No type errors
70+
- [ ] No lint errors
71+
- [ ] All acceptance criteria from the ticket description met
72+
- [ ] PR created (or updated) and linked to Linear issue
73+
- [ ] Workpad updated with completion summary

.ai/prompts/investigate.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# Investigation Stage
2+
3+
You are investigating issue **{{ issue.identifier }}**: {{ issue.title }}
4+
5+
**Current status:** {{ issue.state }}
6+
**Labels:** {{ issue.labels }}
7+
**URL:** {{ issue.url }}
8+
9+
## Issue description
10+
11+
{% if issue.description %}
12+
{{ issue.description }}
13+
{% else %}
14+
No description provided.
15+
{% endif %}
16+
17+
## Objective
18+
19+
Understand the problem thoroughly before any code is written. Your output is
20+
an investigation summary posted as a Linear comment — not code changes.
21+
22+
## First run
23+
24+
1. Read the issue description and any existing Linear comments.
25+
2. Identify the relevant source files — read them, understand the architecture.
26+
3. If the issue is a bug: reproduce it first (run the failing test or repro steps).
27+
4. If the issue is a feature: map out which files/modules need changes.
28+
5. Write a structured investigation summary:
29+
- **Root cause** or **Requirements** (depending on issue type)
30+
- **Affected files** with brief explanation of needed changes
31+
- **Risks or open questions**
32+
- **Proposed approach** (high-level, 3-5 bullet points)
33+
6. Post the summary as a Linear comment titled `## Investigation`.
34+
7. Update the workpad with investigation status.
35+
36+
## Rework run
37+
38+
If this is a rework run (the workspace already has investigation content):
39+
40+
1. Read the review feedback from Linear comments.
41+
2. Read your prior investigation summary.
42+
3. Address the specific feedback — expand analysis, correct mistakes, or
43+
investigate additional areas as requested.
44+
4. Update the `## Investigation` comment with revised findings.
45+
5. Append a rework note to the workpad.
46+
47+
## Do NOT
48+
49+
- Write implementation code.
50+
- Create branches or PRs.
51+
- Modify source files (reading is fine).

.ai/prompts/merge.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Merge Stage
2+
3+
You are merging the approved PR for **{{ issue.identifier }}**: {{ issue.title }}
4+
5+
**URL:** {{ issue.url }}
6+
7+
## Objective
8+
9+
Merge the PR and move the issue to its terminal state. This is a short,
10+
mechanical stage — no new code changes.
11+
12+
## Process
13+
14+
1. Find the open PR for this issue:
15+
```
16+
gh pr list --head <branch-name>
17+
```
18+
2. Verify the PR is approved and CI is passing:
19+
```
20+
gh pr view <number> --json reviewDecision,statusCheckRollup
21+
```
22+
3. If CI is failing, investigate briefly. If it is a flaky test or transient
23+
failure, re-run the checks. If it is a real failure, post a comment on the
24+
Linear issue and stop.
25+
4. Merge the PR using squash merge:
26+
```
27+
gh pr merge <number> --squash --delete-branch
28+
```
29+
5. Update the Linear workpad with the merge confirmation.
30+
6. Move the Linear issue to `Done`.
31+
32+
## Rework run
33+
34+
If this is a rework run (merge was attempted before but failed):
35+
36+
1. Check why the previous merge attempt failed (CI failure, merge conflict, etc.).
37+
2. If there is a merge conflict:
38+
- Rebase the branch onto `main` and resolve conflicts.
39+
- Push the updated branch.
40+
- Wait for CI to pass, then merge.
41+
3. If CI failed:
42+
- Read the failure logs.
43+
- If it is a test failure caused by the PR's changes, post details to
44+
Linear and stop (this needs to go back to implementation).
45+
- If it is a flaky or infrastructure issue, re-run and retry the merge.
46+
4. Update the workpad with what happened.
47+
48+
## Do NOT
49+
50+
- Make code changes beyond conflict resolution.
51+
- Open new PRs.
52+
- Skip CI checks.

.ai/prompts/review.md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# Code Review Stage
2+
3+
You are an independent code reviewer with NO prior context about this issue.
4+
Review the changes on the current branch compared to `main`.
5+
6+
**Issue:** {{ issue.identifier }} — {{ issue.title }}
7+
**URL:** {{ issue.url }}
8+
9+
## Issue description
10+
11+
{% if issue.description %}
12+
{{ issue.description }}
13+
{% else %}
14+
No description provided.
15+
{% endif %}
16+
17+
## Objective
18+
19+
Perform a thorough, adversarial code review. Your job is to find problems
20+
the implementer missed — not to rubber-stamp the PR.
21+
22+
## Review process
23+
24+
1. Read the full diff:
25+
```
26+
git diff main...HEAD
27+
```
28+
2. Read the issue description and any acceptance criteria.
29+
3. For each changed file, read the surrounding code (not just the diff) to
30+
understand the full context.
31+
4. Evaluate:
32+
- **Correctness** — Does the code do what the ticket asks? Edge cases?
33+
- **Quality** — Clean code, no duplication, follows project conventions?
34+
- **Safety** — Error handling, input validation, no security issues?
35+
- **Tests** — Adequate coverage? Do tests actually test the right thing?
36+
- **Performance** — Any obvious regressions or inefficiencies?
37+
5. Run the quality suite yourself to confirm everything passes:
38+
- Type checking
39+
- Linting
40+
- Tests
41+
6. Post your review as a Linear comment titled `## Code Review`:
42+
- List issues found (critical, major, minor)
43+
- Note anything that looks good
44+
- Give an overall assessment: approve, request changes, or flag concerns
45+
46+
## Rework run
47+
48+
If this is a rework run (the review stage is being re-run after changes):
49+
50+
1. Read your prior review from the Linear comments.
51+
2. Read the new commits since your last review:
52+
```
53+
git log --oneline main..HEAD
54+
```
55+
3. Verify that previously raised issues have been addressed.
56+
4. Check for any new issues introduced by the rework.
57+
5. Post an updated `## Code Review` comment with your revised assessment.
58+
59+
## Guidelines
60+
61+
- Be specific: reference file names and line numbers.
62+
- Be constructive: suggest fixes, not just problems.
63+
- Do NOT make code changes yourself — this is a review-only stage.
64+
- Do NOT create or modify branches or PRs.

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,6 @@
2828
[submodule "vendor/rubyists-homebrew-tap"]
2929
path = vendor/rubyists-homebrew-tap
3030
url = git@github.com:rubyists/homebrew-tap
31+
[submodule "vendor/stokowski"]
32+
path = vendor/stokowski
33+
url = https://github.com/Sugar-Coffee/stokowski

AGENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ and best practices for agents to follow.
1717
- Conventional Commits: app/usage-rules.md — enforced by the `commit-msg`
1818
and `pre-push` hooks at `githooks/` (run `mix setup` once per clone to
1919
activate them).
20+
- Dogfooding & running `lc` locally (no MCP, no escript): app/usage-rules.md
2021
- Accessibility: app/usage-rules.md — the actual reason this project
2122
exists; see documents/motivation.adoc for the full why.
2223

app/.gitignore

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,6 @@ erl_crash.dump
2222
# Ignore package tarball (built via "mix hex.build").
2323
app-*.tar
2424

25-
# The escript built via "mix escript.build" (local dev convenience only -
26-
# the real distribution target is the Burrito release, see Phase 8).
27-
/lc
28-
2925
# Burrito's per-target output binaries (mix release, see documents/phase-8-plan.adoc).
3026
/burrito_out/
3127

app/lib/linear_cli/application.ex

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ defmodule LinearCli.Application do
1414

1515
# Only the daemon run mode (LINEAR_CLI_DAEMON=true, set by the mix
1616
# release's daemon startup) starts the repo + Oban and stays alive.
17-
# Confirmed empirically that the escript boots this whole application on
18-
# every invocation - without this gate, every interactive command would
19-
# also open a database connection and boot Oban's full supervision tree.
20-
# See documents/phase-7-plan.adoc.
17+
# Confirmed empirically that every run mode (Burrito release, `mix run`)
18+
# boots this whole application on every invocation - without this gate,
19+
# every interactive command would also open a database connection and
20+
# boot Oban's full supervision tree. See documents/phase-7-plan.adoc.
2121
#
2222
# Which repo/engine actually starts is resolved fresh on every boot via
2323
# LinearCli.ObanRepo.{repo,oban_engine}/0, not baked in at compile time -
@@ -35,19 +35,18 @@ defmodule LinearCli.Application do
3535
Supervisor.start_link([repo, {Oban, oban_opts}], opts)
3636
end
3737

38-
# `mix escript.build`'s `main_module: LinearCli.CLI` makes the escript
39-
# runtime call `LinearCli.CLI.main/1` itself once boot finishes here - so
40-
# this must NOT also call it, or every interactive command would run
41-
# twice. A Burrito-wrapped release has no such runtime: it boots via
42-
# `-s elixir start_cli`, which only recognizes Elixir's own CLI flags
43-
# (`--help`/`--version`) and otherwise tries to run the first arg as a
44-
# script file (see documents/phase-8-plan.adoc's Burrito verification -
45-
# it only exercised the daemon boot-and-stay-alive path, not this one).
46-
# `LinearCli.CLI.main/2` never reaches this call site as a Burrito
47-
# release, so it has to happen here instead, per Burrito's own
48-
# "Application Entry Point" README section. `running_standalone?/0`
49-
# (checks the `__BURRITO` env var the Zig wrapper sets) is what
50-
# distinguishes that case from escript/`mix run`.
38+
# A Burrito-wrapped release boots via `-s elixir start_cli`, which only
39+
# recognizes Elixir's own CLI flags (`--help`/`--version`) and otherwise
40+
# tries to run the first arg as a script file (see
41+
# documents/phase-8-plan.adoc's Burrito verification - it only exercised
42+
# the daemon boot-and-stay-alive path, not this one). `LinearCli.CLI.
43+
# main/2` never reaches this call site as a Burrito release, so it has
44+
# to happen here instead, per Burrito's own "Application Entry Point"
45+
# README section. `running_standalone?/0` (checks the `__BURRITO` env
46+
# var the Zig wrapper sets) is what distinguishes that case from `mix
47+
# run`, where the caller (a test, an `-e` script, IEx) invokes
48+
# `LinearCli.CLI.main/1` itself - calling it again here would run every
49+
# interactive command twice.
5150
defp start_interactive do
5251
if Burrito.Util.running_standalone?() do
5352
LinearCli.CLI.main(Burrito.Util.Args.argv())

app/lib/linear_cli/oban_repo.ex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ defmodule LinearCli.ObanRepo do
33
Resolves which Ecto repo (and matching Oban engine) actually backs Oban's
44
job table, at *runtime* - `LINEAR_CLI_DB_ADAPTER=sqlite|postgres` (default
55
`sqlite`), read fresh every time the daemon boots (see
6-
`LinearCli.Application`). This is what lets one compiled escript/release
7-
run against either backend on whatever machine it's deployed to, with no
6+
`LinearCli.Application`). This is what lets one compiled release run
7+
against either backend on whatever machine it's deployed to, with no
88
rebuild.
99
1010
The *adapter* itself is still necessarily fixed per `Ecto.Repo` module -

0 commit comments

Comments
 (0)