@openthink/team 0.0.11 → 0.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -31
- package/dist/assign-ticket.md +25 -21
- package/dist/implement-project.md +160 -0
- package/dist/index.js +901 -368
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# open-team
|
|
2
2
|
|
|
3
|
-
Source-agnostic
|
|
3
|
+
Source-agnostic workspace-driven role pipeline for spawning Claude agents against tickets. Lifts the "Assign to agent" + role-pipeline flow out of agentic-desktop's Swift code into a standalone npm CLI.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
@@ -42,7 +42,7 @@ Flags:
|
|
|
42
42
|
|
|
43
43
|
## Workspace setup
|
|
44
44
|
|
|
45
|
-
`open-team` reads from a workspace directory (
|
|
45
|
+
`open-team` reads from a workspace directory (no Obsidian required). Layout:
|
|
46
46
|
|
|
47
47
|
```
|
|
48
48
|
openteam/
|
|
@@ -56,7 +56,7 @@ openteam/
|
|
|
56
56
|
|
|
57
57
|
A ticket's `state:` frontmatter must always match its containing folder under `tickets/`.
|
|
58
58
|
|
|
59
|
-
For the simplest single-workspace setup, run `oteam init` (creates and registers `~/openteam/`). To use an existing tree, register it via `oteam config
|
|
59
|
+
For the simplest single-workspace setup, run `oteam init` (creates and registers `~/openteam/`). To use an existing tree, register it via `oteam config workspace add <path>` or set `PRODUCT_VAULT_PATH`. For multiple workspaces (personal + work, etc.) see [Config & multiple workspaces](#config--multiple-workspaces).
|
|
60
60
|
|
|
61
61
|
## Subcommands
|
|
62
62
|
|
|
@@ -65,12 +65,11 @@ oteam pull <source> <ref> # ingest external item → tickets/triage/
|
|
|
65
65
|
oteam pull --project <name> ... # tag the new ticket with a project
|
|
66
66
|
oteam assign <ticket-or-id> # drive role pipeline (full path or AGT-NNN)
|
|
67
67
|
oteam assign --inline <path> # … or run inline in current terminal
|
|
68
|
-
oteam assign --no-stamp <id> # one-shot override of stamp.enforce (clones from GitHub)
|
|
69
68
|
oteam list [--state <state>] # list active tickets
|
|
70
69
|
oteam list --project <name> # filter by project frontmatter
|
|
71
|
-
oteam archive <ticket-id> # move done ticket to archive/YYYY-MM/
|
|
72
|
-
oteam config
|
|
73
|
-
oteam config
|
|
70
|
+
oteam archive <ticket-id> # move done ticket to archive/YYYY-MM/ + reap workspace
|
|
71
|
+
oteam config workspace add <path> # register a workspace under a name
|
|
72
|
+
oteam config workspace list # show registered workspaces + default
|
|
74
73
|
oteam config stamp set --host <url> # configure stamp host post-init
|
|
75
74
|
oteam config stamp set --enforce on # require repos be stamp-registered
|
|
76
75
|
oteam config stamp clear # remove the stamp block
|
|
@@ -84,7 +83,7 @@ oteam telemetry summary [--days N] [--phase X] [--model Y]
|
|
|
84
83
|
oteam telemetry tail [-n 20] # last N telemetry lines (raw JSONL)
|
|
85
84
|
```
|
|
86
85
|
|
|
87
|
-
Most commands accept `--
|
|
86
|
+
Most commands accept `--workspace <name-or-path>` (or the back-compat alias `--vault`) to operate on a specific workspace.
|
|
88
87
|
|
|
89
88
|
### Tagging tickets by project
|
|
90
89
|
|
|
@@ -141,7 +140,7 @@ Where the clone comes from is governed by oteam config (`~/.open-team/config.jso
|
|
|
141
140
|
|
|
142
141
|
| `stamp` config | Mode | Clone source | Behaviour |
|
|
143
142
|
|------------------------------------------------|-----------|--------------------------------------------------------|-------------------------------------------------------------------------------------------------|
|
|
144
|
-
| absent / `null` |
|
|
143
|
+
| absent / `null` | plain | `git@github.com:<repo>.git` | Default. No stamp config files are read. `oteam` works against any git repo. |
|
|
145
144
|
| `{ host, enforce: false }` | soft | `git@github.com:<repo>.git` | Stamp host is recorded for tooling that asks for it; `oteam assign` does not gate. |
|
|
146
145
|
| `{ host, enforce: true }` | enforce | `<host>/srv/git/<basename>.git` (the stamp server) | The clone IS the gate: clone failure exits non-zero before any spawn. AGT-050 behaviour. |
|
|
147
146
|
|
|
@@ -157,11 +156,7 @@ oteam config stamp set --enforce off # … or back off
|
|
|
157
156
|
oteam config stamp clear # remove the stamp block entirely
|
|
158
157
|
```
|
|
159
158
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
> **Migration note.** Earlier `oteam` builds read `~/.stamp/server.yml` directly. This version does not — to keep the AGT-050 stamp gate in place after upgrade, run `oteam init` and paste the host (or `oteam config stamp set --host <url> --enforce on`).
|
|
163
|
-
|
|
164
|
-
Stale workspaces from prior assigns are GC'd at spawn time: any `/tmp/open-team-issues/agt-N/` directory whose ticket id has no matching ticket in the active vault is `rm -rf`'d before the new clone. The current run's workspace is also `rm -rf`'d before its clone, so re-assigns are hermetic.
|
|
159
|
+
Stale workspaces from prior assigns are GC'd at spawn time: any `/tmp/open-team-issues/agt-N/` directory whose ticket id has no matching ticket in the active workspace is `rm -rf`'d before the new clone. The current run's workspace is also `rm -rf`'d before its clone, so re-assigns are hermetic.
|
|
165
160
|
|
|
166
161
|
## Per-phase model selection
|
|
167
162
|
|
|
@@ -225,33 +220,61 @@ Knobs:
|
|
|
225
220
|
- `oteam config telemetry set off` opts out — no JSONL writes occur. Re-enable with `oteam config telemetry set on`. Default is on.
|
|
226
221
|
- Telemetry is best-effort: a write failure (read-only dir, missing session file, malformed log) writes one line to stderr and does not fail the role-pipeline phase. Token fields the agent SDK doesn't expose are omitted from the line rather than recorded as zero.
|
|
227
222
|
|
|
228
|
-
## Config & multiple
|
|
223
|
+
## Config & multiple workspaces
|
|
229
224
|
|
|
230
|
-
`open-team` supports any number of named
|
|
225
|
+
`open-team` supports any number of named workspaces via `~/.open-team/config.json`. The on-disk key is `vaults` for back-compat with earlier builds; conceptually these are workspaces. Register them with:
|
|
231
226
|
|
|
232
227
|
```sh
|
|
233
|
-
oteam config
|
|
234
|
-
oteam config
|
|
235
|
-
oteam config
|
|
236
|
-
oteam config
|
|
237
|
-
oteam config
|
|
228
|
+
oteam config workspace add ~/Documents/my-workspace # auto-name "my-workspace"; first add becomes default
|
|
229
|
+
oteam config workspace add ~/Documents/work-workspace --name work
|
|
230
|
+
oteam config workspace list
|
|
231
|
+
oteam config workspace default --set work
|
|
232
|
+
oteam config workspace remove work # clears default if it pointed here
|
|
238
233
|
```
|
|
239
234
|
|
|
240
|
-
|
|
235
|
+
The `oteam config vault ...` form also works as a silent back-compat alias (`oteam config vault add` and `oteam config workspace add` register the same thing).
|
|
236
|
+
|
|
237
|
+
Paths are resolved to absolute at `add` time, so the registration survives `cd`. Removing the default workspace clears `default` and forces an explicit `--workspace` on every subsequent command until you set a new one — there is no silent promotion.
|
|
241
238
|
|
|
242
239
|
### Resolution precedence (most-specific wins)
|
|
243
240
|
|
|
244
|
-
| # | Source
|
|
245
|
-
|
|
246
|
-
| 1 | `--
|
|
247
|
-
| 2 | `PRODUCT_VAULT_PATH` env var
|
|
248
|
-
| 3 | `default` in `~/.open-team/config.json`
|
|
249
|
-
| 4 | `~/Documents/product-vault`
|
|
241
|
+
| # | Source | Notes |
|
|
242
|
+
|---|-----------------------------------------------------------|---------------------------------------------------------------|
|
|
243
|
+
| 1 | `--workspace <name-or-path>` flag (or `--vault` alias) | Per-command override |
|
|
244
|
+
| 2 | `PRODUCT_VAULT_PATH` env var | One-off shell override; also propagated to spawns |
|
|
245
|
+
| 3 | `default` in `~/.open-team/config.json` | Set via `oteam config workspace default --set <name>` |
|
|
246
|
+
| 4 | `~/Documents/product-vault` | Implicit fallback if no config exists |
|
|
250
247
|
|
|
251
248
|
`oteam assign` adds two niceties on top:
|
|
252
249
|
|
|
253
|
-
- **AGT-NNN shorthand**: `oteam assign AGT-001` walks `<
|
|
254
|
-
- **
|
|
250
|
+
- **AGT-NNN shorthand**: `oteam assign AGT-001` walks `<workspace>/tickets/<state>/` for a file whose basename starts with `AGT-001-`.
|
|
251
|
+
- **Workspace auto-detection from path**: passing a full path that lives inside a registered workspace root makes that workspace the active one for the run, even if it's not the default. The spawned `_role-run` then inherits `PRODUCT_VAULT_PATH=<that-workspace>` so any follow-up `oteam pull/list/...` from the agent lands in the same workspace.
|
|
252
|
+
|
|
253
|
+
## Claim-on-assign (preventing double-pickup)
|
|
254
|
+
|
|
255
|
+
When multiple operators or agents work the same workspace, two of them can race on the same ticket — both run `oteam assign` and both spin up role pipelines against the same GitHub issue. To prevent that, `oteam assign` can claim the underlying GH issue (sets `assignees`) before driving the pipeline:
|
|
256
|
+
|
|
257
|
+
```sh
|
|
258
|
+
oteam config bot-identity set <github-login> # e.g. your own login, or a dedicated bot account
|
|
259
|
+
oteam config bot-identity show
|
|
260
|
+
oteam config bot-identity clear # disable claim-on-assign
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
When `botIdentity` is set, every `oteam assign` run on a github-sourced ticket does the following pre-flight before any expensive setup:
|
|
264
|
+
|
|
265
|
+
1. GET the issue from `source.url`.
|
|
266
|
+
2. If state is `closed` → exit 1 (refuses to drive the pipeline on resolved work).
|
|
267
|
+
3. If already assigned to someone other than `botIdentity` → exit 1 (someone else has it).
|
|
268
|
+
4. PATCH `assignees: [botIdentity]`. Re-read the response.
|
|
269
|
+
5. If assignees came back empty, the operator's `gh` token has no push access on the repo (GitHub silently drops assignee changes without it) → exit 1 with a hint.
|
|
270
|
+
6. If assignees != `[botIdentity]` (race with another writer) → exit 1.
|
|
271
|
+
7. Otherwise the claim is held; proceed.
|
|
272
|
+
|
|
273
|
+
When `botIdentity` is empty (the default), the pre-flight is skipped — backwards-compatible with configs that predate the field.
|
|
274
|
+
|
|
275
|
+
Per-invocation override: `OTEAM_BOT_IDENTITY=<login> oteam assign …`. Useful when one operator runs occasionally under a different identity (e.g. testing with a personal account) without rewriting the persisted config.
|
|
276
|
+
|
|
277
|
+
Manual tickets (`source.type: manual`) and tickets with no parseable GH URL skip the claim entirely — there's nothing to claim.
|
|
255
278
|
|
|
256
279
|
## Migration from agentic-desktop
|
|
257
280
|
|
|
@@ -260,7 +283,7 @@ agentic-desktop now keeps only the PR-side modules (`GitHubPRs`, `AIReview*`, `C
|
|
|
260
283
|
Migration steps:
|
|
261
284
|
|
|
262
285
|
1. `npm install -g @openthink/team` (or `npm link` from a local clone).
|
|
263
|
-
2. Either set `PRODUCT_VAULT_PATH` if your
|
|
286
|
+
2. Either set `PRODUCT_VAULT_PATH` if your workspace isn't at `~/Documents/product-vault`, or register it via `oteam config workspace add <path>` (see [Config & multiple workspaces](#config--multiple-workspaces)).
|
|
264
287
|
3. Optionally set `OTEAM_MONITORED_ORGS=Org1,Org2` to route those repos' tickets to the "work" kitty socket (preserves the personal/work split agentic-desktop had).
|
|
265
288
|
4. Delete `~/Library/Application Support/AgenticDesktop/vault-assignments.json` (panel-indicator state, no longer used).
|
|
266
289
|
5. Use `oteam pull github <ref>` instead of clicking "Assign to agent" on the Issues panel.
|
package/dist/assign-ticket.md
CHANGED
|
@@ -13,7 +13,7 @@ You are working a `product-vault` ticket. The user invoked `oteam assign <path>`
|
|
|
13
13
|
3. **No commits, no PRs, no Linear.** Vault tickets do not necessarily map to a code repo. Only act on code if the ticket's `repo:` field is set AND the work demands it.
|
|
14
14
|
4. **STOP at every role-handoff boundary.** When your role is done, write a STOP marker (visual banner per Output discipline below) and let the human decide whether to continue.
|
|
15
15
|
5. **3-attempt cap on any failing operation.** If a step fails (e.g., file mv fails, frontmatter parse fails, build/test fails), you get 3 tries before STOPPing.
|
|
16
|
-
6. **Never read or write inside `$HOME/Development/<repo>`.** That tree may have uncommitted in-flight work; entangling with it is a sterile-field violation. For repo-bound tickets, the `oteam` runner has already prepared an isolated agent worktree at `/tmp/open-team-issues/<ticket-id-lowercased>/repo` and spawned you cd'd into it — that's your only valid working directory. The runner clones from the
|
|
16
|
+
6. **Never read or write inside `$HOME/Development/<repo>`.** That tree may have uncommitted in-flight work; entangling with it is a sterile-field violation. For repo-bound tickets, the `oteam` runner has already prepared an isolated agent worktree at `/tmp/open-team-issues/<ticket-id-lowercased>/repo` and spawned you cd'd into it — that's your only valid working directory. The runner clones from the URI recorded for the repo in `~/.open-team/config.json` and sets your cwd to it; the worktree is isolated from your primary, so AC-shaped requirements like "primary's `git remote -v` is byte-equal before/after a spawn" are satisfied by construction. If your `$PWD` is not the prepared workspace (e.g. you invoked the slash command by hand outside of `oteam assign`), set up the workspace yourself before reading any repo file — see Phase 3 Step 0.
|
|
17
17
|
|
|
18
18
|
## Phase 0 — Read the ticket
|
|
19
19
|
|
|
@@ -65,9 +65,9 @@ If your appended system context flags the **AGT-107 haiku-downshift heuristic**
|
|
|
65
65
|
|
|
66
66
|
## Phase 3 — Engineering agent (state: refined → spike phase)
|
|
67
67
|
|
|
68
|
-
**Step 0 — Workspace is already prepared.** When `oteam assign` spawned you against a repo-bound ticket, it already cloned `/tmp/open-team-issues/<ticket-id-lowercased>/repo`
|
|
68
|
+
**Step 0 — Workspace is already prepared.** When `oteam assign` spawned you against a repo-bound ticket, it already cloned `/tmp/open-team-issues/<ticket-id-lowercased>/repo` from the URI recorded for the repo in `~/.open-team/config.json` and set your cwd to it. Confirm with `pwd` and `git remote -v`; you should see exactly one remote, `origin`, pointing at the URI recorded for this repo.
|
|
69
69
|
|
|
70
|
-
Cost trade: a fresh
|
|
70
|
+
Cost trade: a fresh clone per assign adds a few seconds vs. the older `git worktree add` fast path. That's an intentional trade for AC-grade isolation — the agent worktree shares no `.git/objects` and no remotes with your primary, and removing or renaming any remote inside the worktree cannot leak back to your daily flow.
|
|
71
71
|
|
|
72
72
|
If you invoked `/assign-ticket` by hand (no `oteam assign` wrapper) and the workspace doesn't exist yet, set it up the same way the runner would:
|
|
73
73
|
|
|
@@ -77,19 +77,19 @@ WORKSPACE="/tmp/open-team-issues/$TICKET_ID_LC"
|
|
|
77
77
|
# REPO_SLUG is "<owner>/<name>" from `repo:` if set, else inferred from the
|
|
78
78
|
# ticket. When inferring, name it explicitly in your spike notes so the human
|
|
79
79
|
# can correct you.
|
|
80
|
-
REPO_BASE=$(basename "$REPO_SLUG")
|
|
81
80
|
mkdir -p "$WORKSPACE"
|
|
82
81
|
cd "$WORKSPACE"
|
|
83
82
|
rm -rf repo
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
#
|
|
91
|
-
|
|
83
|
+
# Look up the recorded clone URI. `oteam config repo show` prints two lines
|
|
84
|
+
# (clone-uri: ..., added: ...) on success, or "(no entry for ...)" on miss.
|
|
85
|
+
CLONE_URI=$(oteam config repo show "$REPO_SLUG" 2>/dev/null | awk '/^clone-uri:/ { print $2 }')
|
|
86
|
+
if [ -z "$CLONE_URI" ]; then
|
|
87
|
+
# No recorded URI yet — fall back to the GitHub HTTPS default. Running
|
|
88
|
+
# `oteam assign` interactively will prompt once and record it; hand-running
|
|
89
|
+
# this slash command skips the prompt and uses the public default.
|
|
90
|
+
CLONE_URI="git@github.com:${REPO_SLUG}.git"
|
|
92
91
|
fi
|
|
92
|
+
git clone -- "$CLONE_URI" repo
|
|
93
93
|
cd repo
|
|
94
94
|
```
|
|
95
95
|
|
|
@@ -175,8 +175,8 @@ Read `CLAUDE.md`, `AGENTS.md`, `README.md` at the repo root if present.
|
|
|
175
175
|
|
|
176
176
|
**2. Verify the worktree shape and compute the merge mode.** Run `git remote -v` and check whether `.stamp/` exists in the worktree. Three shapes are valid:
|
|
177
177
|
|
|
178
|
-
- **Stamp-governed
|
|
179
|
-
- **Local-stamp.** `origin` points at `git@github.com:<owner>/<repo>.git` AND the worktree contains a `.stamp/` directory. The repo carries stamp config but
|
|
178
|
+
- **Stamp-governed.** Exactly one remote, `origin`, pointing at a non-GitHub URI (the stamp server). `MODE` will resolve to `stamp` and routing goes through the stamp-protected branch (5a) at the end of Step 5.
|
|
179
|
+
- **Local-stamp.** `origin` points at `git@github.com:<owner>/<repo>.git` AND the worktree contains a `.stamp/` directory. The repo carries stamp config but `origin` is GitHub, not a stamp server. `MODE` will resolve to `local-stamp` and routing goes through 5c — `stamp review` + `stamp merge` produce a signed merge commit locally, which is then pushed to GitHub as the PR head for human review. `stamp push` is intentionally not invoked (no stamp server); `stamp verify <merge-sha>` still works against the PR head from any clone with the trusted public keys.
|
|
180
180
|
- **Plain GitHub.** `origin` points at `git@github.com:<owner>/<repo>.git` and there is no `.stamp/` directory. `MODE` will resolve to `plain` and routing goes through 5b — `git push origin <feature>` + `gh pr create`.
|
|
181
181
|
|
|
182
182
|
Compute `MODE` once, here, from the worktree's actual state — every subsequent step branches on this single variable:
|
|
@@ -241,13 +241,9 @@ When tests pass:
|
|
|
241
241
|
```sh
|
|
242
242
|
git add -A
|
|
243
243
|
COMMIT_BODY="Refs <ticket-id>"
|
|
244
|
-
# GitHub
|
|
245
|
-
#
|
|
246
|
-
|
|
247
|
-
# remote, the merge gets pushed to the stamp server, and GH never sees a
|
|
248
|
-
# commit that would trigger auto-close. QA Phase 5 closes the GH issue
|
|
249
|
-
# explicitly via `gh issue close`, so behaviour is preserved either way.
|
|
250
|
-
if [ "<source.type>" = "github" ] && [ "$MODE" != "stamp" ]; then
|
|
244
|
+
# When the source is a GitHub issue, append a `Fixes <gh-issue-url>` trailer
|
|
245
|
+
# so the PR merge (or stamp push that mirrors to GitHub) auto-closes the issue.
|
|
246
|
+
if [ "<source.type>" = "github" ]; then
|
|
251
247
|
COMMIT_BODY="$COMMIT_BODY"$'\n'"Fixes <linked-github URL>"
|
|
252
248
|
fi
|
|
253
249
|
git commit -m "<one-line summary>
|
|
@@ -260,6 +256,14 @@ Never use `--no-verify`. Fix hook failures at the root cause.
|
|
|
260
256
|
|
|
261
257
|
**5. Route by `$MODE`** (set in Step 2): `stamp` → 5a, `plain` → 5b, `local-stamp` → 5c.
|
|
262
258
|
|
|
259
|
+
**Push gate (AGT-099).** If your appended system context includes a `# Push step: disabled by oteam config` block, the operator has set `push: off` in `~/.open-team/config.json`. Run every step in 5a/5b/5c up to (but not including) the outbound push command, then print:
|
|
260
|
+
|
|
261
|
+
```
|
|
262
|
+
push disabled by oteam config; merge commit is local at <sha>; run 'git push origin' manually when ready
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
substituting `<sha>` with `git rev-parse HEAD` after the merge (5a/5c) or after the last feature commit (5b). In 5b/5c, also skip `gh pr create` and leave `linked-pr:` empty — there is no pushed branch for the PR to reference. Note the held push in the wrap-up comment. Step 6 (stamp retro routing) still runs because it does not depend on the push.
|
|
266
|
+
|
|
263
267
|
#### 5a — Stamp-protected repo
|
|
264
268
|
|
|
265
269
|
Run review and merge. Capture the review's combined output (stdout + stderr) to a known tempfile so Step 6 can route any `STAMP-RETRO` candidates the reviewers emit. Re-run the entire `tee` block on every round of the 5-round iteration — `$STAMP_REVIEW_OUT` is reassigned to a fresh `mktemp` each round, so Step 6 reads only the last (gate-opening) run; prior tempfiles are left behind for the OS to reap.
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Drive every ticket in a vault project through the role-pipeline to done with minimal human intervention. Argument is a project id (e.g. `think-cli-v2`). Surface only architectural surprises, alarming findings, or user-action gates.
|
|
3
|
+
argument-hint: <project-id>
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
You are running an **autonomous-orchestration loop** against a single project in the vault. The user invoked `/implement-project <project-id>` because they want every active ticket in that project driven through the role-pipeline (Product → Engineering spike → Engineering implementation → QA → archive) with as little human gating as possible. Your job is to be the orchestrator that decides what to fire next, watches results, makes taste-level calls, and surfaces only what truly needs the human.
|
|
7
|
+
|
|
8
|
+
**Argument**: `$ARGUMENTS` — a single project id matching a folder under `<vault>/projects/<id>/` (e.g. `think-cli-v2`). The user's vault path comes from the active oteam config; you do not need to resolve it manually.
|
|
9
|
+
|
|
10
|
+
## Hard rules — read first
|
|
11
|
+
|
|
12
|
+
1. **Stop conditions are tight. Everything else is yours.** Surface to the human only on:
|
|
13
|
+
- **Architectural decisions surfaced by unknown findings during build** — implementation reveals something that meaningfully changes the design (e.g. "the API doesn't actually support what we assumed").
|
|
14
|
+
- **Something so off or confusing it's alarming.** Trust the gut. Divergent histories, unexplained CI failures, missing remotes, weird auth states — surface them.
|
|
15
|
+
- **User-action gates** — tickets where the work is for the operator (e.g. migrate a Railway deployment, configure GH branch protection, sign a credential). You can't proxy these. Surface and wait.
|
|
16
|
+
- **Spike-time questions that require the user's roadmap or values** — not "default 100 vs 1000," but "single-tenant vs multi-tenant" if the design doc didn't already decide.
|
|
17
|
+
|
|
18
|
+
Do NOT stop for: file naming, test fixture shape, comment voice, choosing between equally-good library options, small refactors, ordering of independent sub-steps. Exercise judgment.
|
|
19
|
+
|
|
20
|
+
2. **Push to main is self-authorized when stamp's three reviewers approve.** Do not ping the user before pushing. The stamp gate is the approval mechanism. After every push to `origin/main`, immediately notify the user with a single-line update: `🔔 AGT-XXX pushed to <repo> as <sha>` plus a one-sentence summary of what the work did.
|
|
21
|
+
|
|
22
|
+
3. **Never push to GitHub directly from an agent worktree.** Work goes through stamp; stamp mirrors to GitHub. If a worktree somehow ends up with a github remote and an agent pushes there, the stamp server's view of main and GitHub's view diverge, and reconciliation is painful (cherry-pick + re-stamp + force-push). Insist on stamp-only push paths.
|
|
23
|
+
|
|
24
|
+
4. **Background long runs, inline short ones.**
|
|
25
|
+
- **Inline:** Product passes, QA passes, anything < ~3 minutes.
|
|
26
|
+
- **Background** (`run_in_background: true` on the Bash tool): Engineering spikes, implementations, anything that involves real LLM thinking + code edits. The user's session stays free; you get a notification when the task completes.
|
|
27
|
+
|
|
28
|
+
5. **Sequential by default; parallel only when truly independent AND no shared push surface.** Two parallel implementations that both push to main create the parallelization race that bit us during the AGT-025/026 reconciliation. Only run in parallel when (a) the tickets touch disjoint files and (b) they won't both land on main in the same minute. When in doubt, sequential.
|
|
29
|
+
|
|
30
|
+
6. **3-attempt cap on any failing operation.** Three failures → STOP and surface the error.
|
|
31
|
+
|
|
32
|
+
## Phase 0 — Pre-flight
|
|
33
|
+
|
|
34
|
+
Resolve the project. Run:
|
|
35
|
+
|
|
36
|
+
```sh
|
|
37
|
+
oteam project show <project-id> --tickets
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
If that errors, STOP — print `🛑 BLOCKED — project <id> not found in vault` and surface the candidates from `oteam project list`.
|
|
41
|
+
|
|
42
|
+
Read the project README and every sibling design doc:
|
|
43
|
+
|
|
44
|
+
```sh
|
|
45
|
+
PROJECT_DIR="$(oteam project show <project-id> | grep '^ readme:' | awk '{print $2}' | xargs dirname)"
|
|
46
|
+
cat "$PROJECT_DIR/README.md"
|
|
47
|
+
ls "$PROJECT_DIR/"
|
|
48
|
+
# read each sibling .md as needed for context
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Build the dependency graph from ticket comments (each ticket usually names "Sequencing: blocked by AGT-XXX" or "depends on AGT-YYY"). Identify:
|
|
52
|
+
|
|
53
|
+
- Active tickets (state ≠ done): the work to do.
|
|
54
|
+
- Done tickets: assume their content is in main.
|
|
55
|
+
- User-action tickets (the kind where engineering can't proceed without the operator doing something offline): mark these as gates.
|
|
56
|
+
- Deferred / parking-lot tickets (their own comment usually says so): exclude from the chain unless the user says otherwise.
|
|
57
|
+
|
|
58
|
+
## Phase 1 — Confirm the chain with the user
|
|
59
|
+
|
|
60
|
+
Print a short plan to the user (no walls of text):
|
|
61
|
+
|
|
62
|
+
- The active ticket list, in proposed execution order.
|
|
63
|
+
- Sequencing notes (which depend on which; which can run in parallel; which are user-gates).
|
|
64
|
+
- Anything explicitly excluded (deferred tickets) and why.
|
|
65
|
+
|
|
66
|
+
Ask one question: "Run this chain with the standard autonomous-orchestration rules? Or override anything?"
|
|
67
|
+
|
|
68
|
+
If the user says go (or equivalent), proceed. If they push back on the plan, adjust and re-confirm. Do not start firing without explicit go-ahead on the chain shape.
|
|
69
|
+
|
|
70
|
+
## Phase 2 — Drive each ticket
|
|
71
|
+
|
|
72
|
+
For each non-user-gate ticket, in dependency order:
|
|
73
|
+
|
|
74
|
+
### 2.0 — Brief on prior retros for this ticket's repo
|
|
75
|
+
|
|
76
|
+
**Gated on `repo:` being non-empty.** Read the ticket's `repo:` frontmatter field. If it is empty or absent, skip this step silently — vault-internal tickets have no cortex to brief from.
|
|
77
|
+
|
|
78
|
+
Derive the cortex name from the `repo:` value using the **same rule pinned in AGT-173/174**: take the path component after the slash, lowercased. Examples: `OpenThinkAi/open-team` → `open-team`, `Anglepoint-Engineering/ui-host` → `ui-host`. The cortex name is sourced from validated frontmatter, so it is safe as a literal in shell.
|
|
79
|
+
|
|
80
|
+
Run `think brief` and capture stdout. **Do not gate on exit code or empty output** — every failure mode (missing binary, cortex not found, non-zero exit, empty cortex) is non-fatal; the orchestrator proceeds without the brief:
|
|
81
|
+
|
|
82
|
+
```sh
|
|
83
|
+
think brief --cortex <derived-cortex-name> 2>&1 || true
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Treat the captured output as a clearly-labelled background section in your working context: `## Prior context for ticket AGT-XXX (<repo>)`. It is background, not actionable directives — lessons to weigh when deciding how to drive this ticket, not a re-litigation of its spike. If `think` is missing, exits non-zero, or the cortex has no promoted retros yet, note `no prior retros yet for <repo>` and proceed normally.
|
|
87
|
+
|
|
88
|
+
**Fetch at most once per ticket per orchestrator run.** This step runs here at the 2.0 entry point; do not re-fetch in 2b, 2c, or 2d for the same ticket. The spawned `oteam assign` agent runs its own `think brief` via AGT-174 — do not forward this orchestrator's brief output into the spawn; that would double-fetch.
|
|
89
|
+
|
|
90
|
+
### 2a. Product pass (inline, short)
|
|
91
|
+
|
|
92
|
+
```sh
|
|
93
|
+
oteam assign --inline AGT-XXX
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
If Product returns `✅ DONE — Refined; ready for Engineering spike`, proceed. If `⏸️ PAUSED — Ticket needs answers before Product can refine`, surface to the user — Product can't proceed without their input.
|
|
97
|
+
|
|
98
|
+
### 2b. Engineering spike (background)
|
|
99
|
+
|
|
100
|
+
```sh
|
|
101
|
+
oteam assign --inline AGT-XXX # with run_in_background: true on the Bash tool
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
When the background task notification arrives, read the task's output file. Three possible outcomes:
|
|
105
|
+
|
|
106
|
+
- **Spike auto-proceeded to implementation (S/H rated, no plan review needed):** continue to 2d.
|
|
107
|
+
- **Spike paused for plan review (M+ scope or has gaps):** read the spike. Decide on each open question:
|
|
108
|
+
- If it's taste-level (file names, fixture shape, default values, sequencing of independent substeps): make the call yourself. Append a `### YYYY-MM-DD — Plan approved` comment to the ticket noting your decisions and the rationale. Advance the ticket frontmatter to `state: in-progress`, move the file to `tickets/in-progress/`, then re-fire `oteam assign --inline AGT-XXX` (background).
|
|
109
|
+
- If it's an architectural decision the design doc doesn't answer: surface to the user. Quote the spike's question. Wait.
|
|
110
|
+
- **Spike failed (`STOP:` error of some kind):** surface to the user with the error.
|
|
111
|
+
|
|
112
|
+
### 2c. Implementation (background)
|
|
113
|
+
|
|
114
|
+
The agent does the work, runs stamp review, stamp merge, push. When the background task notification arrives:
|
|
115
|
+
|
|
116
|
+
- Read the task output.
|
|
117
|
+
- If it pushed to `origin/main`: notify the user immediately with a one-line `🔔 AGT-XXX pushed to <repo> as <sha>` plus a one-sentence summary.
|
|
118
|
+
- If it stamp-merged but the mirror push to GitHub was rejected (the AGT-026 case): SURFACE — divergence is alarming, never auto-reconcile.
|
|
119
|
+
- If stamp gate stayed closed (review never converged): surface the last review's `changes_requested` reasoning to the user.
|
|
120
|
+
- If implementation completed but didn't push (no main push needed, e.g. test-only changes that the agent decided to leave on a branch): note in the user-facing summary.
|
|
121
|
+
|
|
122
|
+
### 2d. QA pass (inline, short)
|
|
123
|
+
|
|
124
|
+
```sh
|
|
125
|
+
oteam assign --inline AGT-XXX
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
QA verifies the AC against the merged code and archives the ticket. If QA returns `✅ DONE — QA approved; archived`, mark the ticket complete in your internal chain and move to the next ticket. If QA bounces back with `changes_requested`, fire the engineering implementation again (background) with the QA feedback noted in a comment.
|
|
129
|
+
|
|
130
|
+
### 2e. User-action tickets
|
|
131
|
+
|
|
132
|
+
When the chain reaches a user-action ticket:
|
|
133
|
+
|
|
134
|
+
- Stop driving.
|
|
135
|
+
- Surface the ticket to the user with: title, what they need to do (concrete steps, ideally copy-pasteable commands), and why it's blocking.
|
|
136
|
+
- Wait. Do not proceed past it until the user signs off (either by archiving it or telling you "done, move on").
|
|
137
|
+
|
|
138
|
+
## Phase 3 — Post-completion
|
|
139
|
+
|
|
140
|
+
When every active ticket in the chain is archived:
|
|
141
|
+
|
|
142
|
+
- Print a summary: tickets shipped (with SHAs), elapsed time, anything noteworthy that happened during the run.
|
|
143
|
+
- Offer to bump the project's `status:` frontmatter to `shipped` in `<project>/README.md`, if every active ticket is archived and the user hasn't otherwise indicated more work is incoming.
|
|
144
|
+
- Ask if there are any follow-up tickets to file (issues that surfaced during the run that didn't get filed yet).
|
|
145
|
+
|
|
146
|
+
Then stop. Do not chain into the next project automatically; the user picks what to do next.
|
|
147
|
+
|
|
148
|
+
## Output discipline
|
|
149
|
+
|
|
150
|
+
- One status update per state transition. Don't narrate every Bash call.
|
|
151
|
+
- Always notify on push to `origin/main` (single line, immediate).
|
|
152
|
+
- When surfacing a stop condition: name the ticket, name the issue, propose the resolution paths, ask one question. No walls of text.
|
|
153
|
+
- Use the user's preferred voice from feedback memory: terse, technical, no marketing.
|
|
154
|
+
|
|
155
|
+
## Things that are explicitly NOT your job
|
|
156
|
+
|
|
157
|
+
- Filing new tickets unsolicited (use `oteam ticket new` if a clear bug surfaces during the run, but only file when the issue is concrete and unrelated to the chain you're driving — don't bloat the chain).
|
|
158
|
+
- Reconciling stamp/GitHub divergence — surface immediately, do not auto-merge or auto-force-push.
|
|
159
|
+
- Rewriting design docs mid-run — if the design doc is wrong, surface that and wait.
|
|
160
|
+
- Pushing to feature branches that haven't been stamp-reviewed — stamp is the gate.
|