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
32 changes: 30 additions & 2 deletions .claude/agents/portfolio-surveyor.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,30 @@ public and private β€” no per-repo loop needed to enumerate):

1. **Open PRs (org-wide, one call):**
`gh search prs --owner devantler-tech --archived=false --state open --limit 300 --json number,repository,title,author,isDraft,labels,updatedAt,url`
2. **Open issues (org-wide, one call):**
`gh search issues --owner devantler-tech --archived=false --state open --limit 300 --json number,repository,title,labels,updatedAt,url`
2. **Open issues (org-wide, one call) β€” include `assignees`, they are a CLAIM signal:**
`gh search issues --owner devantler-tech --archived=false --state open --limit 300 --json number,repository,title,labels,updatedAt,url,assignees`
(`--archived=false` keeps archived repos' stale PRs/issues β€” e.g. `data-product`'s 2025 bot PRs β€”
out of every survey; archived repos are read-only and carry no actionable signal.)
(`gh search issues` returns issues only β€” not PRs; treat label-less issues as untriaged.)
Report assignee **logins**, not a count. Only a **`devantler`** assignment can be a claim: the
contract's *Claim protocol* lease is specifically the agent account's, and every instance assigns as
`devantler`, so that login means **an instance has claimed this** β€” never "the maintainer took it".
An issue assigned to **anyone else** (a human collaborator, `Copilot`) is **not** a claim even with a
leftover `claude/*-<issue>` branch present: reporting it as one would park actionable work behind an
unrelated person's assignment and time the lease off the wrong assignment event. Report those as
ordinary open issues, noting the assignee so the orchestrator can respect a human's in-progress work
on its own merits. Without these logins the orchestrator selects the oldest issue blind to live
claims and re-opens the duplicate-build race the protocol exists to close.
2b. **Claim branches (one call per repo that has assigned-but-PR-less issues):**
`gh api repos/<o>/<r>/branches --paginate --jq '.[].name' | grep '^claude/'` β€” report any
`claude/*` branch that ends in `-<issue>`, ends in a **takeover suffix** (`-<issue>-2`, `-3`, …),
OR whose normalised stem matches an open issue's
title (strip `war-`/area prefixes and hyphens, and normalise `our`β†’`or` spelling) β€” legacy claims
predate the issue-number template and would otherwise be invisible during rollout β€” for an open
issue with **no** open PR, as
`CLAIMED <repo>#<issue> (branch, no PR)`. This is the only pre-PR claim signal that exists: before
Comment thread
devantler marked this conversation as resolved.
a PR there is no body to grep, so the issue number in the branch name is what makes the claim
discoverable. Keep it bounded β€” skip the call for repos with no assigned-and-PR-less issues.
3. **Short-circuit dependency automation, then deepen only actionable candidates.** An org-search PR
whose author is the exact `renovate[bot]` or `dependabot[bot]` identity is an automation-owned
dependency PR. Emit only `AUTOMATION-OWNED (NO-ACTION)` from the cheap search row; do **not** call
Expand Down Expand Up @@ -300,12 +319,21 @@ nothing_on_fire: <true|false> # true only if NO CI red on main AND no actionab
### Advance
- <repo>: roadmap-ready β†’ #<n> "<title>" (<label>)
- <repo>: NO roadmap yet β†’ strategy-review candidate
- <repo> #<n> "<title>" β€” CLAIMED: assignee=devantler, claim-branch=<name>, no open PR
```

Digest rules:
- **Classify, don't decide.** Surface signals; the **orchestrator** selects the work and overlays its
own native-memory cadence cursors (`last_worked`, `weekly`, docs/roadmap) β€” **you do not read
memory**, only live GitHub.
- **Emit a `CLAIMED` row only when BOTH a `devantler` assignment and a matching claim branch exist**
(and no open PR). Match `claude/*-<issue>`, a takeover branch (`claude/*-<issue>-2`, `-3`, …), or a
legacy normalised stem. An assignment to **anyone but `devantler`** is not a claim at all, and a
`devantler` assignment with **no** branch is not a live claim under the contract's *Claim
protocol*, so reporting either as one would let a bare assignee park an issue β€” exactly what "a bare
assignee does not reserve an issue" forbids. Report that case as an ordinary open issue (mention
`assignees=<n>` if useful), never as skip reason (e). The orchestrator times the ~2h lease from the
issue's newest `assigned` timeline event; an assignee is an **instance** claim, never the maintainer.
- **Never assert ownership of a `devantler` PR.** Routine-own vs maintainer-interactive is the
orchestrator's creation-record call, not yours β€” report CI state + `headRefName` + disclosure as DATA
and tag it `OWNERSHIP-UNVERIFIED`, never `MERGE-READY`/"own". (Bot-trusted authors have no ambiguity.)
Expand Down
23 changes: 20 additions & 3 deletions .claude/skills/portfolio-maintenance/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -368,8 +368,23 @@ actionable open issue**, and any new non-trivial find is **filed as an issue fir
backlog. Use the [`product-engineering`](../product-engineering/SKILL.md) skill; in order:
7. **Resolve the oldest actionable open issue** *(the default advance action)* β€” pick the **oldest**
open issue that's actually startable; skip one only if it's blocked, too under-specified to begin, or
it already has an open PR. A **bare assignee does *not* reserve** an issue (only an open PR does), so
if nobody has opened one you may pick it up regardless of who's assigned. If it **already has an
it already has an open PR. A **bare `devantler` assignee does *not* reserve** an issue
**indefinitely** β€” a `devantler` assignment plus a **pushed branch** is a live claim for ~2h
(contract *Claim protocol*), and with no branch, or once that lapses with no PR, you may pick it up
(timed from the issue's newest `devantler` `assigned` timeline event, never a branch commit date).
**Only the agent account's assignment is a claim, and only it expires:** an issue assigned to a
**human collaborator** (or `Copilot`) is someone else's work-in-progress β€” respect it and pick a
different issue, never take it over on this window. **Claim
before you build:** self-assign + push the branch **with the issue number in its name** the moment
Comment thread
devantler marked this conversation as resolved.
you select β€” and if `devantler` is ALREADY assigned (a stale bare assignment from an abandoned run),
**remove then re-add**, since adding an existing assignee is a no-op that would leave your lease
carrying the old timestamp. **The push decides the race:** put a real commit on the claim branch
(never a bare base pointer), push without force, then confirm `git ls-remote` shows YOUR sha β€” two
instances derive the same branch name, so a rejected push or someone else's tip means you lost;
stand down rather than force over them. Check open PRs, remote
`claude/*` branches AND assignees by **issue number, never literal branch name**. A live claim
Comment thread
devantler marked this conversation as resolved.
(assigned + branched, in-window, no PR) is skip reason **(e)** β€” the only one that expires by
itself. If it **already has an
actionable trusted-author, non-draft PR**, drive *that* to merge instead of duplicating; leave
automation-owned dependency PRs to repository automation, **draft** PRs for the maintainer, and
**external** PRs static-review-only (trust gate). Otherwise ship it: tests +
Expand Down Expand Up @@ -435,7 +450,9 @@ For each selected product:
.claude/scripts/submodule-init.sh <path> # init at the pinned commit + repair + probe (fail-closed)
```

Then `git -C <path> worktree add .claude/worktrees/maint-<runid> -b claude/<area>-<desc>` and work
Then `git -C <path> worktree add .claude/worktrees/maint-<runid> -b claude/<area>-<desc>-<issue>`
(issue-less hotfixes and trivial obvious fixes keep plain `claude/<area>-<desc>` β€” they go straight
to a PR, so no claim window applies) and work
**in that worktree**. A stray `core.worktree` makes the worktree resolve back into
`.git/modules/<name>`, silently collapsing every parallel session into one physical tree β€” so on any
submodule you did **not** initialise through the wrapper (a tree someone else populated), **probe
Expand Down
21 changes: 17 additions & 4 deletions .claude/skills/product-engineering/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,10 @@ Issues are the unit of work (contract *Issue-driven*) β€” this is where new work
1. **Pick the oldest *actionable* open issue β€” and "big" is not a reason to skip it.** Prefer the
**oldest** startable issue. Skip an older one **only** if (a) it already has an open PR, (b) it is
blocked on a **named, live-verified** external dependency you can cite, (c) it is too
under-specified to begin, or (d) a delivered experiment is waiting for its named future measurement
date. Once that date arrives, measuring it is actionable. **Size, difficulty, a `roadmap`/`enhancement`/
under-specified to begin, (d) a delivered experiment is waiting for its named future measurement
date β€” once that date arrives, measuring it is actionable β€” or (e) another instance holds a **live
claim** (assigned **and** branched, within ~2h, no PR yet; contract *Claim protocol*), the only
skip reason that expires on its own. **Size, difficulty, a `roadmap`/`enhancement`/
`security`/`repo-assist`/`automation` label, or a "maintainer-hot" feeling are NOT skip reasons** β€”
when the oldest issue is large, **decompose it into a small first child and ship that increment**
(`Fixes #child`; add `Part of #experiment` when the parent stays open) so the big thing advances
Expand All @@ -118,8 +120,19 @@ Issues are the unit of work (contract *Issue-driven*) β€” this is where new work
**ship the decision as a draft PR** (he steers there) β€” never a passive "awaiting-maintainer" note, never
the end-of-run report (he rarely reads it), never an **`@devantler` mention** (no notification). Re-verify
any "gated" against live state before trusting it (memory goes stale) and name the
blocker in the report. A **bare assignee does *not* reserve** it (only an open PR does), so if nobody's opened
a PR you may take it regardless of who's assigned; if an **actionable trusted-author** PR already
blocker in the report. A **bare `devantler` assignee does *not* reserve** it **indefinitely** β€” a
`devantler` assignment plus a **pushed branch** is a live claim for ~2h (contract *Claim protocol*);
with no branch, or once that lapses with no PR, you may take it (timed from the issue's newest
`devantler` `assigned` timeline event, never a branch commit date). **Only the agent account's
assignment is a claim, and only it expires:** an issue assigned to a **human collaborator** (or
`Copilot`) is someone else's work-in-progress β€” respect it and pick a different issue. **Claim the lane before you build** (self-assign + push
the branch **with the issue number in its name**, then harden), check open PRs / remote `claude/*`
branches / assignees by **issue number rather than literal branch name**, and on a lost race
**abandon** β€” then diff your build against the winner's and post only findings you have verified:
execute the probe against a **trusted/routine-owned** winner's branch, but for an
**external-contributor** winner it is **static review only** (the trust gate is not relaxed by a
lost race). If an
**actionable trusted-author** PR already
exists, drive *that* one instead of duplicating β€” a non-draft to merge, a **routine-owned draft**
to genuine readiness β†’ self-promotion β†’ merge (contract *Autonomy*); leave
automation-owned dependency PRs to repository automation, other authors' drafts to their owners,
Expand Down
2 changes: 1 addition & 1 deletion .claude/skills/products/monorepo/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ improve discovery, comprehension, adoption, and portfolio positioning through bo
material refreshes. Validate with the `docs` build before any PR.

## Repo-specific conventions
- **Branch** `claude/<area>-<desc>`. Submodule pointers often show as modified β€” **expected, not yours**; detect real dirtiness with `git status --porcelain --ignore-submodules=all`; never stage pointer bumps; never `git submodule update --remote`.
- **Branch** `claude/<area>-<desc>-<issue>` (the issue number makes a pre-PR claim matchable β€” see the contract's *Claim protocol*; issue-less hotfixes and trivial obvious fixes keep plain `claude/<area>-<desc>`). Submodule pointers often show as modified β€” **expected, not yours**; detect real dirtiness with `git status --porcelain --ignore-submodules=all`; never stage pointer bumps; never `git submodule update --remote`.
- **Validate:** before any `docs/` PR, `[ -d docs/node_modules ] || (cd docs && npm ci)` then `cd docs && npm run build` (use `npm --prefix docs …`). Workflows β†’ `actionlint`. Never edit auto-generated files / `*.lock.yml`.
- **Agent/review files:** `AGENTS.md` is the **single canonical** instruction file β€” what humans, agents, and **Copilot code review** all read ([since 2026-06-18](https://github.blog/changelog/2026-06-18-copilot-code-review-agents-md-support-and-ui-improvements/)); there is no separate `.github/copilot-instructions.md` (retired portfolio-wide). Keep `AGENTS.md`, the `.claude/` skills/cards, and any path-scoped `.github/instructions/` files from drifting apart (see `product-engineering` Β§7).
- **Labels** (apply only from this set): `automation`, `documentation`, `ci`, `dependencies`, `submodules`, `bug`, `enhancement`, `question`, `duplicate`, `wontfix`, `needs triage`, `needs investigation`, `performance`, `refactor`, `security`, `repo-assist`, `agentic-workflows`, `good first issue`, `help wanted`, `spam`, `blocked`, `next`.
Expand Down
Loading
Loading