@openthink/team 0.0.4 → 0.0.5

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.
@@ -159,10 +159,27 @@ If you skipped Phase 3 Step 0 (vault-only spike that turned out to need code cha
159
159
 
160
160
  Read `CLAUDE.md`, `AGENTS.md`, `README.md` at the repo root if present.
161
161
 
162
- **2. Verify the worktree shape.** Run `git remote -v` and confirm one of two shapes:
162
+ **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:
163
163
 
164
- - **Stamp-governed (default).** Exactly one remote, `origin`, pointing at `ssh://git@<stamp-host>:<port>/srv/git/<basename>.git`. The runner clones with that shape on purpose; no rename / re-add is required, and adding a `github` remote here would defeat the AGT-050 invariant. Continue to Step 3 and use the stamp-protected branch (5a) at the end of Step 5.
165
- - **`--no-stamp` run.** Exactly one remote, `origin`, pointing at `git@github.com:<owner>/<repo>.git`. Continue to Step 3 and route through the plain-GitHub branch (5b) at the end of Step 5 `git push origin <feature>` + `gh pr create`. The stamp commands (`stamp review`, `stamp merge`, `stamp push`) must not be invoked in this branch even if `.stamp/` exists in the worktree, because there's nowhere to push the stamp-signed merge.
164
+ - **Stamp-governed (default).** Exactly one remote, `origin`, pointing at `ssh://git@<stamp-host>:<port>/srv/git/<basename>.git`. The runner clones with that shape on purpose; no rename / re-add is required, and adding a `github` remote here would defeat the AGT-050 invariant. `MODE` will resolve to `stamp` and routing goes through the stamp-protected branch (5a) at the end of Step 5.
165
+ - **Local-stamp.** `origin` points at `git@github.com:<owner>/<repo>.git` AND the worktree contains a `.stamp/` directory. The repo carries stamp config but no stamp server is in use (typically a `--no-stamp` run against a stamp-aware repo). `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.
166
+ - **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`.
167
+
168
+ Compute `MODE` once, here, from the worktree's actual state — every subsequent step branches on this single variable:
169
+
170
+ ```sh
171
+ ORIGIN_URL=$(git remote get-url origin)
172
+ if [ -d .stamp ]; then
173
+ case "$ORIGIN_URL" in
174
+ *github.com*) MODE=local-stamp ;;
175
+ *) MODE=stamp ;;
176
+ esac
177
+ else
178
+ MODE=plain
179
+ fi
180
+ ```
181
+
182
+ If `MODE` doesn't match what you expected from `git remote -v` and the visible `.stamp/` state, stop and surface — the worktree was cloned from an unexpected remote or `.stamp/` was added/removed mid-flight.
166
183
 
167
184
  **3. Determine base branch + cut feature branch.**
168
185
 
@@ -177,6 +194,17 @@ git fetch origin "$BASE_BRANCH":"$BASE_BRANCH" 2>/dev/null || git fetch origin "
177
194
  git checkout "$BASE_BRANCH"
178
195
  FEATURE_BRANCH="agt/$(echo "$TICKET_ID" | tr '[:upper:]' '[:lower:]')"
179
196
  git checkout -b "$FEATURE_BRANCH"
197
+
198
+ # Local-stamp only: cut a work branch off the feature branch so commits in
199
+ # Step 4 land on $WORK_BRANCH and Step 5c can stamp-merge $WORK_BRANCH into
200
+ # $FEATURE_BRANCH locally — the resulting signed merge commit becomes the
201
+ # PR head. In other modes WORK_BRANCH == FEATURE_BRANCH (no extra checkout).
202
+ if [ "$MODE" = "local-stamp" ]; then
203
+ WORK_BRANCH="${FEATURE_BRANCH}-work"
204
+ git checkout -b "$WORK_BRANCH"
205
+ else
206
+ WORK_BRANCH="$FEATURE_BRANCH"
207
+ fi
180
208
  ```
181
209
 
182
210
  Branch name: `agt/<ticket-id-lowercased>` (e.g. `agt/agt-003`). Vault ticket IDs are the canonical key — Linear identifiers are no longer minted in this flow (Linear-as-publish-target is a separate downstream sync, filed as its own follow-up ticket).
@@ -199,13 +227,13 @@ When tests pass:
199
227
  ```sh
200
228
  git add -A
201
229
  COMMIT_BODY="Refs <ticket-id>"
202
- # Plain-GitHub path only: append a `Fixes <gh-issue-url>` trailer so the
203
- # eventual GH merge auto-closes the issue. The stamp-governed path skips
204
- # this trailer — those worktrees have no github remote, the merge gets
205
- # pushed to the stamp server, and GH never sees a commit that would
206
- # trigger auto-close. QA Phase 5 closes the GH issue explicitly via
207
- # `gh issue close`, so behaviour is preserved either way.
208
- if [ "<source.type>" = "github" ] && [ ! -d .stamp ]; then
230
+ # GitHub-bound paths (plain, local-stamp): append a `Fixes <gh-issue-url>`
231
+ # trailer so the eventual PR merge auto-closes the GH issue. The
232
+ # stamp-governed path skips this trailer — those worktrees have no github
233
+ # remote, the merge gets pushed to the stamp server, and GH never sees a
234
+ # commit that would trigger auto-close. QA Phase 5 closes the GH issue
235
+ # explicitly via `gh issue close`, so behaviour is preserved either way.
236
+ if [ "<source.type>" = "github" ] && [ "$MODE" != "stamp" ]; then
209
237
  COMMIT_BODY="$COMMIT_BODY"$'\n'"Fixes <linked-github URL>"
210
238
  fi
211
239
  git commit -m "<one-line summary>
@@ -216,11 +244,7 @@ $COMMIT_BODY
216
244
 
217
245
  Never use `--no-verify`. Fix hook failures at the root cause.
218
246
 
219
- **5. Detect repo type and route.**
220
-
221
- ```sh
222
- test -d .stamp && REPO_KIND=stamp || REPO_KIND=plain
223
- ```
247
+ **5. Route by `$MODE`** (set in Step 2): `stamp` → 5a, `plain` → 5b, `local-stamp` → 5c.
224
248
 
225
249
  #### 5a — Stamp-protected repo
226
250
 
@@ -257,6 +281,31 @@ gh pr create --fill
257
281
 
258
282
  Capture the PR URL into `linked-pr:`. Human merges through GitHub PR review.
259
283
 
284
+ #### 5c — Local-stamp repo (`.stamp/` present, GitHub origin)
285
+
286
+ Run review on `$WORK_BRANCH` against `$FEATURE_BRANCH` (the eventual PR base):
287
+
288
+ ```sh
289
+ stamp review --diff "$FEATURE_BRANCH..$WORK_BRANCH"
290
+ stamp status --diff "$FEATURE_BRANCH..$WORK_BRANCH"
291
+ ```
292
+
293
+ If the gate isn't open, iterate per the **5-round rule** (same shape as 5a — round 1 structure, round 2 consistency, round 3 polish; later rounds rare). Amend on `$WORK_BRANCH` between rounds. After 5 rounds still red → STOP with `🛑 BLOCKED — Local stamp review red after 5 rounds`.
294
+
295
+ When the gate opens, merge locally and push the signed merge as the PR head:
296
+
297
+ ```sh
298
+ git checkout "$FEATURE_BRANCH"
299
+ stamp merge "$WORK_BRANCH" --into "$FEATURE_BRANCH"
300
+ git push -u origin "$FEATURE_BRANCH"
301
+ gh pr create --base "$DEFAULT_BRANCH" --head "$FEATURE_BRANCH" --fill
302
+ git branch -D "$WORK_BRANCH"
303
+ ```
304
+
305
+ `stamp push` is intentionally absent — there is no stamp server. The signed merge commit is the PR head; reviewers can `stamp verify <pr-head-sha>` from any clone whose `.stamp/trusted-keys/` contains the signing key. Capture the PR URL into `linked-pr:`. Human merges through GitHub PR review. Never merge a GitHub PR yourself.
306
+
307
+ Local-stamp is single-tier only — the PR base is always `$DEFAULT_BRANCH`. Two-tier (stacked-base) flows require a stamp server to hold the intermediate base branch and aren't supported in this mode.
308
+
260
309
  ### Phase 4.5 — Release follow-up (single-tier stamp only)
261
310
 
262
311
  If the repo publishes artifacts (npm, crates.io, PyPI, GitHub Releases) gated on a version bump, the merge above adds the change but won't ship until the version bumps. Re-read `CLAUDE.md`/`AGENTS.md` for a "Releases" / "Publishing" section.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openthink/team",
3
- "version": "0.0.4",
3
+ "version": "0.0.5",
4
4
  "type": "module",
5
5
  "description": "Source-agnostic vault-driven role pipeline for spawning Claude agents against tickets",
6
6
  "bin": {